千家信息网

zap接收gin框架默认的日志并配置日志归档的方法

发表于:2024-11-15 作者:千家信息网编辑
千家信息网最后更新 2024年11月15日,这篇"zap接收gin框架默认的日志并配置日志归档的方法"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我
千家信息网最后更新 2024年11月15日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框架默认的日志并配置日志归档的方法"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。

0