前面两篇已经为大家介绍了golang中的日志如何使用,并在诸多日志框架库中选择了zap作为我们的日志框架,本篇将会讲解:
-
如何结合当下主流的Web框架gin进行请求日志的打印
-
对zap进行二次封装,注入trace信息,一遍我们可以在业务中查询一次请求的所有完整日志
这里是前两篇的链接:
1、gin 默认的中间件
首先我们来看一个最简单的 gin 项目:
func main() {
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
c.String("hello jianfan.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 响应的。
2、基于 zap 的中间件
gin框架支持用户自定义的middleware,我们可以模仿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掉项目可能出现的panic
func 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