
WebGin简单框架-logger
Web目录结构
C:. ├─.idea │ └─dictionaries ├─controllers ├─dao │ ├─mysql │ └─redis ├─dist │ └─static │ ├─css │ ├─fonts │ └─js ├─logger ├─logic ├─models ├─pkg │ ├─myJWT │ └─snowflake ├─routers ├─setting └─main.go
1.gin_middleware.go
gin_middleware.go
package logger import ( "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // GinLogger 接收gin框架默认的日志 func GinLogger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery 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", c.ClientIP()), zap.String("user-agent", c.Request.UserAgent()), zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), zap.Duration("cost", cost), ) } } // GinRecovery recover掉项目可能出现的panic 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() } }
2.logger.go
logger.go
package logger import ( "os" "github.com/gin-gonic/gin" "github.com/natefinch/lumberjack" "github.com/spf13/viper" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( logger *zap.Logger ) // Init 初始化zap日志 func Init() { writeSyncer := getLogWriter() // 日志输出位置相关 errWriteSyncer := getErrLogWriter() encoder := getEncoder() // 日志的格式相关 // 解析字符串格式的级别 level := zap.AtomicLevel{} if err := level.UnmarshalText([]byte(viper.GetString("log.level"))); err != nil { level = zap.NewAtomicLevel() // 默认用info } //core := zapcore.NewCore(encoder, writeSyncer, level) // 根据app的模式把日志输出到不同的位置 var core zapcore.Core if viper.GetString("app.mode") == gin.DebugMode { // consoleEncoder 一个往终端输出日志的配置对象 consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()) // NewTee 可以指定多个日志配置 core = zapcore.NewTee( zapcore.NewCore(encoder, writeSyncer, level), // 创建一个将debug级别以上的日志输出到终端的配置信息 zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel), // 将error级别以上的日志输出到err文件 zapcore.NewCore(encoder, errWriteSyncer, zapcore.ErrorLevel), ) } else { core = zapcore.NewCore(encoder, writeSyncer, level) } logger := zap.New(core, zap.AddCaller(), zap.Fields(zap.String("app", "xiaodebu"))) // 根据上面的配置创建logger //logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) // 如果是在原装方法上包一层的话,此处要加 zap.ReplaceGlobals(logger) // 替换zap库里全局的logger //sugarLogger = logger.Sugar() } func getEncoder() zapcore.Encoder { //encoderConfig := zap.NewProductionEncoderConfig() encoderConfig := zapcore.EncoderConfig{ TimeKey: "ts", LevelKey: "level", NameKey: "logger", CallerKey: "caller", MessageKey: "msg", StacktraceKey: "stacktrace", LineEnding: zapcore.DefaultLineEnding, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeTime: zapcore.EpochTimeEncoder, EncodeDuration: zapcore.SecondsDurationEncoder, EncodeCaller: zapcore.ShortCallerEncoder, } encoderConfig.TimeKey = "timestamp" encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 人类可读的时间格式 //encoderConfig.EncodeTime = zapcore.EpochTimeEncoder encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder //return zapcore.NewConsoleEncoder(encoderConfig) // 可读日志 return zapcore.NewJSONEncoder(encoderConfig) // json格式日志 } func getLogWriter() zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: viper.GetString("log.filename"), MaxSize: viper.GetInt("log.max_size"), // 日志文件大小 单位:MB MaxBackups: viper.GetInt("log.max_backups"), // 备份数量 MaxAge: viper.GetInt("log.max_age"), // 备份时间 单位:天 Compress: viper.GetBool("log.compress"), // 是否压缩 } return zapcore.AddSync(lumberJackLogger) } func getErrLogWriter() zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: viper.GetString("log.filename") + ".err", MaxSize: viper.GetInt("log.max_size"), // 日志文件大小 单位:MB MaxBackups: viper.GetInt("log.max_backups"), // 备份数量 MaxAge: viper.GetInt("log.max_age"), // 备份时间 单位:天 Compress: viper.GetBool("log.compress"), // 是否压缩 } return zapcore.AddSync(lumberJackLogger) }