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

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

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
    12 引用 • 33 回帖
  • 日志
    45 引用 • 105 回帖
  • golang

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

    499 引用 • 1395 回帖 • 246 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...