1.gin默认日志使用
package init
import (
"fmt"
"gin/global"
"github.com/gin-gonic/gin"
"io"
"net/http"
"os"
"path"
"runtime/debug"
"time"
)
func LoggerToFile() gin.LoggerConfig {
if _, err := os.Stat(global.Settings.LogsAddress); os.IsNotExist(err) {
err = os.MkdirAll(global.Settings.LogsAddress, 0777)
if err != nil {
panic(fmt.Errorf("create log dir '%s' error: %s", global.Settings.LogsAddress, err))
}
}
timeStr := time.Now().Format("2006-01-02")
fileName := path.Join(global.Settings.LogsAddress, "success_"+timeStr+".log")
os.Stderr, _ = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
var conf = gin.LoggerConfig{
Formatter: func(param gin.LogFormatterParams) string {
return fmt.Sprintf("%s - %s %s %s %s %s %d %s %s\n",
param.TimeStamp.Format("2006-01-02 15:04:05"),
param.ClientIP,
param.Method,
param.Path,
param.Request.PostForm.Encode(),
param.Request.Proto,
param.StatusCode,
param.Latency,
param.ErrorMessage,
)
},
Output: io.MultiWriter(os.Stdout, os.Stderr),
}
return conf
}
func Recover(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
if _, errDir := os.Stat(global.Settings.LogsAddress); os.IsNotExist(errDir) {
errDir = os.MkdirAll(global.Settings.LogsAddress, 0777)
if errDir != nil {
panic(fmt.Errorf("create log dir '%s' error: %s", global.Settings.LogsAddress, errDir))
}
}
timeStr := time.Now().Format("2006-01-02")
fileName := path.Join(global.Settings.LogsAddress, "error_"+timeStr+".log")
f, errFile := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if errFile != nil {
fmt.Println(errFile)
}
timeFileStr := time.Now().Format("2006-01-02 15:04:05")
_, _ = f.WriteString("panic error time:" + timeFileStr + "\n")
_, _ = f.WriteString(fmt.Sprintf("%v", err) + "\n")
_, _ = f.WriteString("stacktrace from panic:" + string(debug.Stack()) + "\n")
_ = f.Close()
c.JSON(http.StatusOK, gin.H{
"code": http.StatusInternalServerError,
"msg": fmt.Sprintf("%v", err),
})
//终止后续接口调用,不加的话recover到异常后,还会继续执行接口里后续代码
c.Abort()
}
}()
c.Next()
}
使用
package router
func Router() *gin.Engine {
r := gin.Default()
r.Use(gin.LoggerWithConfig(init.LoggerToFile()))
//错误日志执行
r.Use(init.Recover)
return r
}
成功日志、失败日志为多个文件
2.使用logrus
package log
import (
"fmt"
"gin/global"
"gin/utils"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"net"
"net/http"
"os"
"path"
"strings"
"time"
)
func Log() gin.HandlerFunc {
return func(c *gin.Context) {
if _, err := os.Stat(global.Settings.LogsAddress); os.IsNotExist(err) {
err = os.MkdirAll(global.Settings.LogsAddress, 0777)
if err != nil {
panic(fmt.Errorf("create log dir '%s' error: %s", global.Settings.LogsAddress, err))
}
}
timeStr := time.Now().Format(time.DateOnly)
fileName := path.Join(global.Settings.LogsAddress, timeStr+".log")
os.Stderr, _ = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
//实例化
logger := logrus.New()
//设置输出
logger.Out = os.Stderr
//设置日志级别
logger.SetLevel(logrus.InfoLevel)
//打印方法
logger.SetReportCaller(true)
//设置日志格式
logger.SetFormatter(&logrus.TextFormatter{TimestampFormat: time.DateTime})
// 开始时间
startTime := time.Now()
// 处理请求
c.Next()
// 结束时间
endTime := time.Now()
// 执行时间
latencyTime := endTime.Sub(startTime)
method := c.Request.Method
url := c.Request.RequestURI
form := c.Request.PostForm.Encode()
statusCode := c.Writer.Status()
clientIP := c.Request.Header.Get("X-Real-IP")
if clientIP == "" {
clientIP = c.Request.Header.Get("X-Forwarded-For")
}
if clientIP == "" {
clientIP, _, _ = net.SplitHostPort(c.Request.RemoteAddr)
}
if strings.Contains(clientIP, ":") {
clientIP = strings.Split(clientIP, "%")[0]
}
errormessage := c.Errors.ByType(gin.ErrorTypePrivate).String()
// 日志格式
if len(c.Errors) > 0 {
logger.Errorf(errormessage)
} else {
msg := fmt.Sprintf(" %s %s %s %s %d %s %s", clientIP, method, url, form, statusCode, latencyTime, errormessage)
if statusCode == http.StatusOK {
logger.Info(msg)
} else {
logger.Error(msg)
}
}
}
}
func Write(msg string) {
setOutPutFile(logrus.InfoLevel, "info")
logrus.Info(msg)
}
func Debug(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.DebugLevel, "debug")
logrus.WithFields(fields).Debug(args)
}
func Info(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.InfoLevel, "info")
logrus.WithFields(fields).Info(args)
}
func Warn(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.WarnLevel, "warn")
logrus.WithFields(fields).Warn(args)
}
// 执行完程序结束
func Fatal(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.FatalLevel, "fatal")
logrus.WithFields(fields).Fatal(args)
}
func Error(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.ErrorLevel, "error")
logrus.WithFields(fields).Error(args)
}
func Panic(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.PanicLevel, "panic")
logrus.WithFields(fields).Panic(args)
}
func Trace(fields logrus.Fields, args ...interface{}) {
setOutPutFile(logrus.TraceLevel, "trace")
logrus.WithFields(fields).Trace(args)
}
func setOutPutFile(level logrus.Level, logName string) {
if _, err := os.Stat(global.Settings.LogsAddress); os.IsNotExist(err) {
err = os.MkdirAll(global.Settings.LogsAddress, 0777)
if err != nil {
panic(fmt.Errorf("create log dir '%s' error: %s", global.Settings.LogsAddress, err))
}
}
timeStr := time.Now().Format(time.DateOnly)
fileName := path.Join(global.Settings.LogsAddress, logName+"_"+timeStr+".log")
var err error
os.Stderr, err = os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fmt.Println("open log file err", err)
}
logrus.SetOutput(os.Stderr)
logrus.SetLevel(level)
logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: time.DateTime})
return
}
func Recover(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
if _, errDir := os.Stat(global.Settings.LogsAddress); os.IsNotExist(errDir) {
errDir = os.MkdirAll(global.Settings.LogsAddress, 0777)
if errDir != nil {
panic(fmt.Errorf("create log dir '%s' error: %s", global.Settings.LogsAddress, errDir))
}
}
timeStr := time.Now().Format(time.DateOnly)
fileName := path.Join(global.Settings.LogsAddress, timeStr+".log")
f, errFile := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if errFile != nil {
fmt.Println(errFile)
}
timeFileStr := time.Now().Format(time.DateTime)
_, _ = f.WriteString("panic error time:" + timeFileStr + "\n")
_, _ = f.WriteString(fmt.Sprintf("%v", err) + "\n")
_ = f.Close()
utils.ReturnError(c, http.StatusInternalServerError, fmt.Sprintf("%v", err))
//终止后续接口调用,不加的话recover到异常后,还会继续执行接口里后续代码
c.Abort()
}
}()
c.Next()
}
使用
package router
func Router() *gin.Engine {
r := gin.Default()
r.Use(log.Log())
//错误日志执行
r.Use(init.Recover)
return r
}
成功日志、失败日志为一个文件 调用方法会写入多个文件 生成多个文件
3.mysql日志
package init
import (
"fmt"
"gin/global"
"gopkg.in/natefinch/lumberjack.v2"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"io"
"log"
"os"
"time"
)
func InitMysqlDB() {
mysqlInfo := global.Settings.Mysqlinfo
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True",
mysqlInfo.Name, mysqlInfo.Password, mysqlInfo.Host,
mysqlInfo.Port, mysqlInfo.DBName)
global.DB, _ = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: getGormLogger(),
})
}
func getGormLogger() logger.Interface {
var logMode logger.LogLevel
switch global.Settings.Mysqlinfo.LogMode {
case "silent":
logMode = logger.Silent
case "error":
logMode = logger.Error
case "warn":
logMode = logger.Warn
case "info":
logMode = logger.Info
default:
logMode = logger.Info
}
return logger.New(getGormLogWriter(), logger.Config{
SlowThreshold: 200 * time.Millisecond, // 慢 SQL 阈值
LogLevel: logMode, // 日志级别
IgnoreRecordNotFoundError: false, // 忽略ErrRecordNotFound(记录未找到)错误
})
}
// 自定义 gorm Writer
func getGormLogWriter() logger.Writer {
var writer io.Writer
// 是否启用日志文件
if global.Settings.Mysqlinfo.EnableFileLogWriter {
// 自定义 Writer
writer = &lumberjack.Logger{
Filename: global.Settings.LogsAddress + "/" + global.Settings.Mysqlinfo.Logfile,
MaxSize: global.Settings.LogConfig.MaxSize,
MaxBackups: global.Settings.LogConfig.MaxBackups,
MaxAge: global.Settings.LogConfig.MaxAge,
Compress: global.Settings.LogConfig.Compress,
}
} else {
// 默认 Writer
writer = os.Stdout
}
writer = io.MultiWriter(writer, os.Stdout)
return log.New(writer, "\r\n", log.LstdFlags)
}
使用该配置文件可生成sql.log在指定文件夹下
配置文件为:
logsAddress: './logs/'
mysql:
name: 'root'
host: '192.168.1.9'
port: 3306
password: 'root'
dbName: 'test'
logfile: 'sql.log'
logmode: 'info'
#开启日志写入
enable_file_log_writer: true
log:
maxsize: 500
maxbackups: 3
maxage: 28
compress: true
读取配置文件生成global公共变量
package init
import (
"gin/config"
"gin/global"
"github.com/spf13/viper"
)
func InitConfig() {
// 实例化viper
v := viper.New()
//文件的路径如何设置
v.SetConfigFile("./config.yaml")
if err := v.ReadInConfig(); err != nil {
panic(err)
}
serverConfig := config.ServerConfig{}
//给serverConfig初始值
if err := v.Unmarshal(&serverConfig); err != nil {
panic(err)
}
// 传递给全局变量
global.Settings = serverConfig
}
配置映射读取
package config
type ServerConfig struct {
Mysqlinfo MysqlConfig `mapstructure:"mysql"`
LogsAddress string `mapstructure:"logsAddress"`
LogConfig LogConfig `mapstructure:"log"`
}
type MysqlConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Name string `mapstructure:"name"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbName"`
Logfile string `mapstructure:"logfile"`
LogMode string `mapstructure:"logmode"`
EnableFileLogWriter bool `mapstructure:"enable_file_log_writer"`
}
type LogConfig struct {
MaxSize int `mapstructure:"maxsize"`
MaxBackups int `mapstructure:"maxbackups"`
MaxAge int `mapstructure:"maxage"`
Compress bool `mapstructure:"compress"`
}
全局变量生成
package global
var ( Settings config.ServerConfig
DB *gorm.DB)
具体使用方法为global.DB.Raw()即可