package logger import ( "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" "github.com/gin-gonic/gin" "github.com/natefinch/lumberjack" "github.com/spf13/viper" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func Init() (err error) { writerSyncer := getLogWriter( viper.GetString("logger.name"), viper.GetInt("logger.max_sizes"), viper.GetInt("logger.max_age"), viper.GetInt("logger.max_backups"), ) encoder := getEncoder() var l = new(zapcore.Level) err = l.UnmarshalText([]byte(viper.GetString("logger.level"))) if err != nil { return } core := zapcore.NewCore(encoder, writerSyncer, zapcore.DebugLevel) lg := zap.New(core, zap.AddCaller()) //用 lg 来代替全局 logger zap.ReplaceGlobals(lg) return } func getEncoder() zapcore.Encoder { // return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()) encoder := zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", FunctionKey: zapcore.OmitKey, MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.CapitalLevelEncoder, EncodeTime: zapcore.ISO8601TimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } // encoder.EncodeTime = zapcore.ISO8601TimeEncoder // encoder.EncodeLevel = zapcore.CapitalLevelEncoder return zapcore.NewConsoleEncoder(encoder) } func getLogWriter(filename string, maxSize int, maxBackups int, maxAge int) zapcore.WriteSyncer { lumberjackLogger := &lumberjack.Logger{ Filename: filename, MaxSize: maxSize, // 最大大小,单位是M MaxBackups: maxBackups, //最大备份数量,在上个文件达到最大大小时,开始备份文件 MaxAge: maxAge, //最大保持备份天数 Compress: false, //是否压缩 } return zapcore.AddSync(lumberjackLogger) } func GinLogger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery ip := c.ClientIP() user_agent := c.Request.UserAgent() err := c.Errors.ByType(gin.ErrorTypePrivate).String() c.Next() cost := time.Since(start) zap.L().Info( path, zap.Int("status", c.Writer.Status()), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", ip), zap.String("user-agent", user_agent), zap.String("errors", err), zap.Duration("cost", cost), ) } } func GinRecovery(stack bool) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { // Check for a broken connection, as it is not really a // condition that warrants a panic stack trace. var brokenPipe bool if ne, ok := err.(*net.OpError); ok { if se, ok := ne.Err.(*os.SyscallError); ok { if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { brokenPipe = true } } } httpRequest, _ := httputil.DumpRequest(c.Request, false) if brokenPipe { zap.L().Error(c.Request.URL.Path, zap.Any("error", err), zap.String("request", string(httpRequest)), ) // If the connection is dead, we can't write a status to it. c.Error(err.(error)) // nolint: errcheck c.Abort() return } if stack { zap.L().Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { zap.L().Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } }