gin 框架自定义日志输出,自定义 gin 中间件扩展 Logger

本贴最后更新于 721 天前,其中的信息可能已经东海扬尘

Gin 框架是款高性能的 GoWeb 框架,可以快速开发部署 API 服务。在使用过程中我们需要记录各种各样的日志,下面介绍下我们怎么自定义日志记录格式或扩展日志。

Gin 简单剖析

API 服务创建

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

以上是 GitHub 中官方介绍的一个简单的 demo,三步创建了一个 API 服务

  1. gin.Default 获取到一个 Engine 实例

  2. engine.GET() 添加一个 Get 请求的路由逻辑

  3. engine.Run() 启动服务

gin.Default 剖析

func New() *Engine {
	debugPrintWARNINGNew()
	engine := &Engine{
		RouterGroup: RouterGroup{
			Handlers: nil,
			basePath: "/",
			root:     true,
		},
		FuncMap:                template.FuncMap{},
		RedirectTrailingSlash:  true,
		RedirectFixedPath:      false,
		HandleMethodNotAllowed: false,
		ForwardedByClientIP:    true,
		AppEngine:              defaultAppEngine,
		UseRawPath:             false,
		UnescapePathValues:     true,
		trees:                  make(methodTrees, 0, 9),
		delims:                 render.Delims{"{{", "}}"},
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() interface{} {
		return engine.allocateContext()
	}
	return engine
}

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

gin.Default 通过 New 创建了 Engine 实例, 并 Use 了 Logger Recovery 两个 HandlerFunc 中间件。注释也介绍了
默认返回一个引擎实例,其中包含日志记录器和崩溃恢复中间件。

那么我们是不是可以通过自己的 Logger 中间件来记录日志? 写到这里发现官方文档其实是有介绍的,少走弯路还是先看文档额~~不过直接看源码也没坏处,哈哈

1png

日志中间件

func (c *Context) Next() {
	c.index++
	s := int8(len(c.handlers))
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

Gin 通过循环当前的中间件处理链 Handler,逐个调用中间件。

自定义日志中间件

  1. 日志记录(logrus)
    logrus 是第三方包,GitHub 上比较活跃,已经实现了很多常用功能,日常开发中用到的较多。

  2. 日志分割(rotatelogs)
    logrus 没有提供日志切分,go-file-rotatelogs 可以实现 Linux logratate 的大多数功能。

使用 logrus 的 hook 来加载 github.com/lestrrat/go-file-rotatelogs 模块。每次当我们写入日志的时候,logrus 都会调用 go-file-rotatelogs 来判断日志是否要进行切分…

package api  
  
import (  
   "github.com/gin-gonic/gin"  
   "os" 
   "fmt" 
   "github.com/sirupsen/logrus" 
   "github.com/lestrrat/go-file-rotatelogs" 
   "time" 
   "github.com/rifflock/lfshook"
)  
  
func Logger() gin.HandlerFunc {  
   logClient := logrus.New()  
  
   //禁止logrus的输出  
   src, err := os.OpenFile(os.DevNull, os.O_APPEND|os.O_WRONLY, os.ModeAppend)  
   if err!= nil{  
      fmt.Println("err", err)  
   }  
   logClient.Out = src  
   logClient.SetLevel(logrus.DebugLevel)  
   apiLogPath := "api.log"  
   logWriter, err := rotatelogs.New(  
      apiLogPath+".%Y-%m-%d-%H-%M.log",  
      rotatelogs.WithLinkName(apiLogPath), // 生成软链,指向最新日志文件  
      rotatelogs.WithMaxAge(7*24*time.Hour), // 文件最大保存时间  
      rotatelogs.WithRotationTime(24*time.Hour), // 日志切割时间间隔  
   )  
   writeMap := lfshook.WriterMap{  
      logrus.InfoLevel:  logWriter,  
      logrus.FatalLevel: logWriter,  
   }  
   lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{})  
   logClient.AddHook(lfHook)  
  
  
   return func (c *gin.Context) {  
      // 开始时间  
      start := time.Now()  
      // 处理请求  
      c.Next()  
      // 结束时间  
      end := time.Now()  
      //执行时间  
      latency := end.Sub(start)  
  
      path := c.Request.URL.Path 
  
      clientIP := c.ClientIP()  
      method := c.Request.Method  
      statusCode := c.Writer.Status()  
      logClient.Infof("| %3d | %13v | %15s | %s  %s |",  
         statusCode,  
         latency,  
         clientIP,  
         method, path,  
      )  
   }  
}

注:

3png

// Next should be used only inside middleware.
// It executes the pending handlers in the chain inside the calling handler.
// See example in GitHub.
func (c *Context) Next() {
	c.index++
	s := int8(len(c.handlers))
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

这里面为了计算程序的执行时长, 我们又调用了一次 c.Next(),让后面的中间件执行,这样日志中间件拿到了程序其它所有中间件执行的总时长,认为是程序的处理时间。

c.Next 循环的时候都是同一个 Context 指针上下文,index 下标为共享的数据,我们可以通过调用一次 c.Next 让 log 中间件插一脚。

  • Gin
    11 引用 • 33 回帖
  • 日志
    36 引用 • 97 回帖
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    410 引用 • 1317 回帖 • 683 关注

赞助商 我要投放

回帖
请输入回帖内容 ...