zap接收gin框架默认的日志并配置日志归档的方法
这篇"zap接收gin框架默认的日志并配置日志归档的方法"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"zap接收gin框架默认的日志并配置日志归档的方法"文章吧。
gin默认的中间件
首先我们来看一个最简单的gin项目:
func main() { r := gin.Default() r.GET("/hello", func(c *gin.Context) { c.String("hello liwenzhou.com!") }) r.Run(}
接下来我们看一下gin.Default()
的源码:
func Default() *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine}
也就是我们在使用gin.Default()
的同时是用到了gin框架内的两个默认中间件Logger()
和Recovery()
。
其中Logger()
是把gin框架本身的日志输出到标准输出(我们本地开发调试时在终端输出的那些日志就是它的功劳),而Recovery()
是在程序出现panic的时候恢复现场并写入500响应的。
基于zap的中间件
我们可以模仿Logger()
和Recovery()
的实现,使用我们的日志库来接收gin框架默认输出的日志。
这里以zap为例,我们实现两个中间件如下:
// GinLogger 接收gin框架默认的日志func GinLogger(logger *zap.Logger) 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) logger.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掉项目可能出现的panicfunc GinRecovery(logger *zap.Logger, 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 { logger.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 { logger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { logger.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() }}
如果不想自己实现,可以使用github上有别人封装好的https://github.com/gin-contrib/zap。
这样我们就可以在gin框架中使用我们上面定义好的两个中间件来代替gin框架默认的Logger()
和Recovery()
了。
r := gin.New()r.Use(GinLogger(), GinRecovery())
在gin项目中使用zap
最后我们再加入我们项目中常用的日志切割,完整版的logger.go
代码如下:
package loggerimport ( "gin_zap_demo/config" "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" "github.com/gin-gonic/gin" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore")var lg *zap.Logger// InitLogger 初始化Loggerfunc InitLogger(cfg *config.LogConfig) (err error) { writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge) encoder := getEncoder() var l = new(zapcore.Level) err = l.UnmarshalText([]byte(cfg.Level)) if err != nil { return } core := zapcore.NewCore(encoder, writeSyncer, l) lg = zap.New(core, zap.AddCaller()) zap.ReplaceGlobals(lg) // 替换zap包中全局的logger实例,后续在其他包中只需使用zap.L()调用即可 return}func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.TimeKey = "time" encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder return zapcore.NewJSONEncoder(encoderConfig)}func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: filename, MaxSize: maxSize, MaxBackups: maxBackup, MaxAge: maxAge, } return zapcore.AddSync(lumberJackLogger)}// 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) lg.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,并使用zap记录相关日志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 { lg.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 { lg.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { lg.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() }}
然后定义日志相关配置:
type LogConfig struct { Level string `json:"level"` Filename string `json:"filename"` MaxSize int `json:"maxsize"` MaxAge int `json:"max_age"` MaxBackups int `json:"max_backups"`}
在项目中先从配置文件加载配置信息,再调用logger.InitLogger(config.Conf.LogConfig)
即可完成logger实例的初识化。其中,通过r.Use(logger.GinLogger(), logger.GinRecovery(true))
注册我们的中间件来使用zap接收gin框架自身的日志,在项目中需要的地方通过使用zap.L().Xxx()
方法来记录自定义日志信息。
package mainimport ( "fmt" "gin_zap_demo/config" "gin_zap_demo/logger" "net/http" "os" "go.uber.org/zap" "github.com/gin-gonic/gin")func main() { // load config from config.json if len(os.Args) < 1 { return } if err := config.Init(os.Args[1]); err != nil { panic(err) } // init logger if err := logger.InitLogger(config.Conf.LogConfig); err != nil { fmt.Printf("init logger failed, err:%v\n", err) return } gin.SetMode(config.Conf.Mode) r := gin.Default() // 注册zap相关中间件 r.Use(logger.GinLogger(), logger.GinRecovery(true)) r.GET("/hello", func(c *gin.Context) { // 假设你有一些数据需要记录到日志中 var ( name = "q1mi" age = 18 ) // 记录日志并使用zap.Xxx(key, val)记录相关字段 zap.L().Debug("this is hello func", zap.String("user", name), zap.Int("age", age)) c.String(http.StatusOK, "hello liwenzhou.com!") }) addr := fmt.Sprintf(":%v", config.Conf.Port) r.Run(addr)}
以上就是关于"zap接收gin框架默认的日志并配置日志归档的方法"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。