https://www.syncd.cn/post/6 这边文章中整理关于日志包zap的使用,那么在Gin web框架中如何接入zap的日志中间件呢?

Gin 的 zap中间件

gin的中间件中其实有zap的中间件:https://github.com/gin-contrib/zap 并且也给了简单的例子

不过官网给的例子确实太简单了,不过还在我在之前的博客中已经整理了关于zap包的使用,那么就可以把它的这个例子写的比较完善了,代码如下:

package main

import (
    ginzap "github.com/gin-contrib/zap"
    "github.com/gin-gonic/gin"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
    "net/http"
    "os"
    "time"
)

var Logger *zap.Logger

func init() {
    hook := lumberjack.Logger{
        Filename:"./logs/test.log",  //日志文件路径
        MaxSize: 20,// 每个日志的大小,单位是M
        MaxAge: 7, // 文件被保存的天数
        Compress:true, // 是否压缩
        MaxBackups:10, // 保存多少个文件备份
    }
    encoderConfig := zapcore.EncoderConfig{
        TimeKey:"Time",
        LevelKey:"Level",
        NameKey: "Logger",
        CallerKey: "Caller",
        MessageKey:"Msg",
        LineEnding: zapcore.DefaultLineEnding,
        EncodeLevel: zapcore.LowercaseLevelEncoder,
        EncodeTime: zapcore.ISO8601TimeEncoder,
        EncodeDuration: zapcore.SecondsDurationEncoder,
        EncodeCaller: zapcore.ShortCallerEncoder,
    }
    atomicLevel := zap.NewAtomicLevel()
    atomicLevel.SetLevel(zap.InfoLevel)
    core := zapcore.NewCore(
        zapcore.NewJSONEncoder(encoderConfig),
        zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout),zapcore.AddSync(&hook)),
        atomicLevel,
    )
    caller := zap.AddCaller()
    development := zap.Development()
    filed := zap.Fields(zap.String("service","blog"))
    Logger = zap.New(core,caller, development,filed)
}

func Index(c *gin.Context) {
    Logger.Info("hello world")
    c.String(http.StatusOK,"hello")
}

func Panic(c *gin.Context) {
    // 这里手动触发panic 
    panic("an unexpected error happen!")
}

func main() {
    router := gin.New()
    
    // 添加ginzap 中间件
    router.Use(ginzap.Ginzap(Logger,time.RFC3339,true))
    // 将所有的panic 输出到error 日志中
    router.Use(ginzap.RecoveryWithZap(Logger,true))
    
    router.GET("/",Index)
    router.GET("/panic", Panic)
    router.Run()
}

测试我们访问两个路由我即可在终端以及日志文件中看到对应的日志内容:

{"Level":"info","Time":"2019-09-26T10:52:42.364+0800","Caller":"gin_log/main.go:50","Msg":"hello world","service":"blog"}
{"Level":"info","Time":"2019-09-26T10:52:42.386+0800","Caller":"zap@v0.0.0-20190911144541-f473495929db/zap.go:46","Msg":"/","service":"blog","status":200,"method":"GET","path":"/","query":"","ip":"127.0.0.1","user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36","time":"2019-09-26T02:52:42Z","latency":0.022}
{"Level":"error","Time":"2019-09-26T10:52:50.693+0800","Caller":"zap@v0.0.0-20190911144541-f473495929db/zap.go:93","Msg":"[Recovery from panic]","service":"blog","time":"2019-09-26T10:52:50.693+0800","error":"an unexpected error happen!","request":"GET /panic HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh-HK;q=0.9,zh;q=0.8\r\nConnection: keep-alive\r\nCookie: Pycharm-b245d0ed=9bfd6207-2687-4f98-9c6a-b0d93ad88578\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36\r\n\r\n","stack":"goroutine 34 [running]:\nruntime/debug.Stack(0x0, 0xc000248000, 0x1d2)\n\tD:/Go/src/runtime/debug/stack.go:24 +0xa4\ngithub.com/gin-contrib/zap.RecoveryWithZap.func1.1(0xc00023e000, 0xc0001982a0, 0x4bba01)\n\tD:/go_project/pkg/mod/github.com/gin-contrib/zap@v0.0.0-20190911144541-f473495929db/zap.go:97 +0x37e\npanic(0xa1e0e0, 0xc03b60)\n\tD:/Go/src/runtime/panic.go:522 +0x1c3\nmain.Panic(0xc00023e000)\n\tD:/go_project/src/awesomeCode/gin_log/main.go:56 +0x40\ngithub.com/gin-gonic/gin.(*Context).Next(0xc00023e000)\n\tD:/go_project/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:124 +0x41\ngithub.com/gin-contrib/zap.RecoveryWithZap.func1(0xc00023e000)\n\tD:/go_project/pkg/mod/github.com/gin-contrib/zap@v0.0.0-20190911144541-f473495929db/zap.go:109 +0x69\ngithub.com/gin-gonic/gin.(*Context).Next(0xc00023e000)\n\tD:/go_project/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:124 +0x41\ngithub.com/gin-contrib/zap.Ginzap.func1(0xc00023e000)\n\tD:/go_project/pkg/mod/github.com/gin-contrib/zap@v0.0.0-20190911144541-f473495929db/zap.go:32 +0xd3\ngithub.com/gin-gonic/gin.(*Context).Next(0xc00023e000)\n\tD:/go_project/pkg/mod/github.com/gin-gonic/gin@v1.4.0/context.go:124 +0x41\ngithub.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0001e0000, 0xc00023e000)\n\tD:/go_project/pkg/mod/github.com/gin-gonic/gin@v1.4.0/gin.go:389 +0x5b9\ngithub.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0001e0000, 0xc1df40, 0xc00023a000, 0xc0000ec900)\n\tD:/go_project/pkg/mod/github.com/gin-gonic/gin@v1.4.0/gin.go:351 +0x13b\nnet/http.serverHandler.ServeHTTP(0xc00019e410, 0xc1df40, 0xc00023a000, 0xc0000ec900)\n\tD:/Go/src/net/http/server.go:2774 +0xaf\nnet/http.(*conn).serve(0xc0001b2320, 0xc1f400, 0xc00018a3c0)\n\tD:/Go/src/net/http/server.go:1878 +0x858\ncreated by net/http.(*Server).Serve\n\tD:/Go/src/net/http/server.go:2884 +0x2fb\n"}
{"Level":"info","Time":"2019-09-26T10:52:50.693+0800","Caller":"zap@v0.0.0-20190911144541-f473495929db/zap.go:46","Msg":"/panic","service":"blog","status":500,"method":"GET","path":"/panic","query":"","ip":"127.0.0.1","user-agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36","time":"2019-09-26T02:52:50Z","latency":0}

我们上面的代码是通过中间的方式,在Gin web项目中添加了zap输出日志的功能,这样的好处其实我们通过上面的结果也看到了,及时当我们的项目出现了panic的时候程序也不至于崩溃,还能将panic的信息输入到error日志中,同时关于gin web框架内容的日志也会输入到我们的日志文件。

关于Gin web中接入自定义的日志,自己也搜了很多资料,看到也有的是不是通过中间件的方式接入,而是在程序初始化的时候初始化一个全局logger对象,在其他包中导入该logger,当然这种方法也是可以的,但是个人对比还是觉得接入中间件的方式比较好

关于非中间件的方式

这种方式也是我们在其他项目中会经常使用方式,代码例子如下:

package main

import (
    "github.com/gin-gonic/gin"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
    "net/http"
    "os"
)

var Logger *zap.Logger

func init() {
    hook := lumberjack.Logger{
        Filename:"./logs/test.log",  //日志文件路径
        MaxSize: 20,// 每个日志的大小,单位是M
        MaxAge: 7, // 文件被保存的天数
        Compress:true, // 是否压缩
        MaxBackups:10, // 保存多少个文件备份
    }
    encoderConfig := zapcore.EncoderConfig{
        TimeKey:"Time",
        LevelKey:"Level",
        NameKey: "Logger",
        CallerKey: "Caller",
        MessageKey:"Msg",
        LineEnding: zapcore.DefaultLineEnding,
        EncodeLevel: zapcore.LowercaseLevelEncoder,
        EncodeTime: zapcore.ISO8601TimeEncoder,
        EncodeDuration: zapcore.SecondsDurationEncoder,
        EncodeCaller: zapcore.ShortCallerEncoder,
    }
    atomicLevel := zap.NewAtomicLevel()
    atomicLevel.SetLevel(zap.InfoLevel)
    core := zapcore.NewCore(
        zapcore.NewJSONEncoder(encoderConfig),
        zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout),zapcore.AddSync(&hook)),
        atomicLevel,
    )
    caller := zap.AddCaller()
    development := zap.Development()
    filed := zap.Fields(zap.String("service","blog"))
    Logger = zap.New(core,caller, development,filed)
}

func Index(c *gin.Context) {
    Logger.Info("hello world")
    c.String(http.StatusOK,"hello")
}

func main() {
    router := gin.Default()
    router.GET("/",Index)
    router.Run(":9090")
}