Gin框架使用Zap接收日志的實(shí)現(xiàn)
前言
日志對于項(xiàng)目重要性不言而喻,如果用過Gin框架大家都知道,Gin框架中自帶日志logger;可以文件和控制臺輸出,但性能和功能遠(yuǎn)不如Zap。
一、Gin使用默認(rèn)日志中間件
下面簡單寫個例子
func main() { r := gin.New() // 創(chuàng)建一個不包含中間件的路由器 r.Use(gin.Logger(), gin.Recovery()) // 使用 Logger 中間件、Recovery 中間件 r.GET("/hello", func(c *gin.Context) { // 路由添加 c.String(200, "hello!") }) r.Run(":9090") }
瀏覽器訪問http://127.0.0.1:9090/hello,控制臺會輸出一下日志內(nèi)容:
二、Zap日志中間件
1.封裝Zap日志包
我在Gin框架下創(chuàng)建了一個pkg目錄,創(chuàng)建了一個glog包,包含三個文件zap.go、sugar.go、logger.go。里面涉及的global的配置大家可以下載項(xiàng)目看看(https://gitee.com/tfc2016/gino)
zap.go
package glog import ( "gino/global" "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "time" ) var ( logger *zap.Logger sugar *zap.SugaredLogger Logger *zap.Logger ) func NewZapLogger() { writeSyncer := getLogWriter() encoder := getEncoder() core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) // zap打印時將整個調(diào)用的stack鏈路會存放到內(nèi)存中,默認(rèn)打印調(diào)用處的caller信息。所以需要再初始化zap時額外增加AddCallerSkip跳過指定層級的caller logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) defer logger.Sync() Logger = logger sugar = Logger.Sugar() } // getEncoder zapcore.Encoder func getEncoder() zapcore.Encoder { encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = CustomTimeEncoder if global.Config.Log.StacktraceKey != "" { encoderConfig.StacktraceKey = global.Config.Log.StacktraceKey } if global.Config.Log.Format == "json" { return zapcore.NewJSONEncoder(encoderConfig) } return zapcore.NewConsoleEncoder(encoderConfig) } func getLogWriter() zapcore.WriteSyncer { lumberJackLogger := &lumberjack.Logger{ Filename: global.Config.Log.Path + "/" + global.Config.Log.Filename, // 日志文件位置 MaxSize: global.Config.Log.MaxSize, // 進(jìn)行切割之前,日志文件的最大大小(MB為單位) MaxBackups: global.Config.Log.MaxBackups, // 保留舊文件的最大個數(shù) MaxAge: global.Config.Log.MaxAge, // 保留舊文件的最大天數(shù) Compress: global.Config.Log.Compress, // 是否壓縮/歸檔舊文件 } return zapcore.AddSync(lumberJackLogger) } // CustomTimeEncoder 自定義日志輸出時間格式 func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) }
sugar.go
package glog // Debug uses fmt.Sprint to construct and log a message. func Debug(args ...interface{}) { sugar.Debug(args) } // Info uses fmt.Sprint to construct and log a message. func Info(args ...interface{}) { sugar.Info(args) } // Warn uses fmt.Sprint to construct and log a message. func Warn(args ...interface{}) { sugar.Warn(args) } // Error uses fmt.Sprint to construct and log a message. func Error(args ...interface{}) { sugar.Error(args) } // DPanic uses fmt.Sprint to construct and log a message. In development, the // logger then panics. (See DPanicLevel for details.) func DPanic(args ...interface{}) { sugar.DPanic(args) } // Panic uses fmt.Sprint to construct and log a message, then panics. func Panic(args ...interface{}) { sugar.Panic(args) } // Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit. func Fatal(args ...interface{}) { sugar.Fatal(args) } // Debugf uses fmt.Sprintf to log a templated message. func Debugf(template string, args ...interface{}) { sugar.Debugf(template, args) } // Infof uses fmt.Sprintf to log a templated message. func Infof(template string, args ...interface{}) { sugar.Infof(template, args, nil) } // Warnf uses fmt.Sprintf to log a templated message. func Warnf(template string, args ...interface{}) { sugar.Warnf(template, args, nil) } // Errorf uses fmt.Sprintf to log a templated message. func Errorf(template string, args ...interface{}) { sugar.Errorf(template, args, nil) } // DPanicf uses fmt.Sprintf to log a templated message. In development, the // logger then panics. (See DPanicLevel for details.) func DPanicf(template string, args ...interface{}) { sugar.DPanicf(template, args, nil) } // Panicf uses fmt.Sprintf to log a templated message, then panics. func Panicf(template string, args ...interface{}) { sugar.Panicf(template, args, nil) } // Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit. func Fatalf(template string, args ...interface{}) { sugar.Fatalf(template, args, nil) }
logger.go(此文件主要是Gorm輸出日志采用Zap)
package glog import ( "go.uber.org/zap" ) func ZapDebug(msg string, fields ...zap.Field) { logger.Debug(msg, fields...) } func ZapInfo(msg string, fields ...zap.Field) { logger.Info(msg, fields...) } func ZapWarn(msg string, fields ...zap.Field) { logger.Warn(msg, fields...) } func ZapError(msg string, fields ...zap.Field) { logger.Error(msg, fields...) } func ZapDPanic(msg string, fields ...zap.Field) { logger.DPanic(msg, fields...) } func ZapPanic(msg string, fields ...zap.Field) { logger.Panic(msg, fields...) } func ZapFatal(msg string, fields ...zap.Field) { logger.Fatal(msg, fields...) }
2.封裝logger和recover的Zap日志
package middleware import ( "bytes" "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" "go.uber.org/zap" "io/ioutil" "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" ) // ZapLogger 接收gin框架默認(rèn)的日志 func ZapLogger(lg *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery post := "" if c.Request.Method == "POST" { // 把request的內(nèi)容讀取出來 bodyBytes, _ := ioutil.ReadAll(c.Request.Body) c.Request.Body.Close() // 把剛剛讀出來的再寫進(jìn)去 c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) switch c.ContentType() { case "application/json": var result map[string]interface{} d := jsoniter.NewDecoder(bytes.NewReader(bodyBytes)) d.UseNumber() if err := d.Decode(&result); err == nil { bt, _ := jsoniter.Marshal(result) post = string(bt) } default: post = string(bodyBytes) } } 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("post", post), 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), ) } } // ZapRecovery recover項(xiàng)目可能出現(xiàn)的panic,并使用zap記錄相關(guān)日志 func ZapRecovery(lg *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 { 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: err check 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() } }
使用 Use(middleware.ZapLogger(glog.Logger), middleware.ZapRecovery(glog.Logger, true)) 替換默認(rèn)的Logger()、Recovery()的中間件,運(yùn)行項(xiàng)目中注冊接口,會看到,日志文件中輸出記錄
18:21:24.044","caller":"gin@v1.7.7/context.go:168","msg":"/api/v1/register","status":400,"method":"POST","path":"/api/v1/register","query":"","post":"{\"username\":\"ceshi\",\"password\":\"123456\"}","ip":"127.0.0.1","user-agent":"PostmanRuntime/7.29.0","errors":"","cost":0.0129476}
到此這篇關(guān)于Gin框架使用Zap接收日志的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Gin Zap接收日志內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go?數(shù)據(jù)結(jié)構(gòu)之二叉樹詳情
這篇文章主要介紹了?Go?數(shù)據(jù)結(jié)構(gòu)之二叉樹詳情,二叉樹是一種數(shù)據(jù)結(jié)構(gòu),在每個節(jié)點(diǎn)下面最多存在兩個其他節(jié)點(diǎn)。即一個節(jié)點(diǎn)要么連接至一個、兩個節(jié)點(diǎn)或不連接其他節(jié)點(diǎn),下文基于GO語言展開二叉樹結(jié)構(gòu)詳情,需要的朋友可以參考一下2022-05-05Go語言面向?qū)ο笾械亩鄳B(tài)你學(xué)會了嗎
面向?qū)ο笾械亩鄳B(tài)(Polymorphism)是指一個對象可以具有多種不同的形態(tài)或表現(xiàn)方式,本文將通過一些簡單的示例為大家講解一下多態(tài)的實(shí)現(xiàn),需要的可以參考下2023-07-07Golang?gRPC?HTTP協(xié)議轉(zhuǎn)換示例
這篇文章主要為大家介紹了Golang?gRPC?HTTP協(xié)議轉(zhuǎn)換示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06