Gin框架使用Zap接收日志的实现
目录
- 前言
- 一、Gin使用默认日志中间件
- 二、Zap日志中间件
- 1.封装Zap日志包
- 2.封装logger和recover的Zap日志
前言
日志对于项目重要性不言而喻,如果用过Gin框架大家都知道,Gin框架中自带日志logger;可以文件和控制台输出,但性能和功能远不如Zap。
一、Gin使用默认日志中间件
下面简单写个例子
func main() { r := gin.New() // 创建一个不包含中间件的路由器 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,控制台会输出一下日志内容:
二、Zap日志中间件
1.封装Zap日志包
我在Gin框架下创建了一个pkg目录,创建了一个glog包,包含三个文件zap.go、sugar.go、logger.go。里面涉及的global的配置大家可以下载项目看看(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打印时将整个调用的stack链路会存放到内存中,默认打印调用处的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, // 进行切割之前,日志文件的最大大小(MB为单位) MaxBackups: global.Config.Log.MaxBackups, // 保留旧文件的最大个数 MaxAge: global.Config.Log.MaxAge, // 保留旧文件的最大天数 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,http://www.devze.com 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 Wahttp://www.devze.comrnf(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框架默认的日志 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的内容读取出来 bodyBytes, _ := ioutil.ReadAll(c.Request.Body) c.Request.Body.Close() // 把刚刚读出来的再写进去 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项目可能出现的panic,并使用zap记录相关日志 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)) 替换默认的Logger()、Recovery()的中间件编程客栈,运行项目中注册接口,会看到,日志文件中输出记录
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.0javascript129476}
到此这篇关于Gin框架使用Zap接收日志的实现的文章就介绍到这了,更多相关Gin Zap接收日志内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论