Gin框架 - 使用 Logrus 進行日志記錄


概述

上篇文章分享了 Gin 框架的路由配置,這篇文章分享日志記錄。

查了很多資料,Go 的日志記錄用的最多的還是 github.com/sirupsen/logrus。

    Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger.

 



Gin 框架的日志默認只會在控制台輸出,咱們利用 Logrus 封裝一個中間件,將日志記錄到文件中。

這篇文章就是學習和使用 Logrus 。

日志格式

比如,我們約定日志格式為 Text,包含字段如下:

請求時間、 日志級別、 狀態碼、 執行時間、 請求IP、 請求方式、 請求路由。

接下來,咱們利用 Logrus 實現它。

Logrus 使用

用 dep 方式進行安裝。

在 Gopkg.toml 文件新增:

    [[constraint]]    
      name = "github.com/sirupsen/logrus"    
      version = "1.4.2"

 



在項目中導入:

import "github.com/sirupsen/logrus"

 



在項目命令行執行:

dep ensure

 



這時,在 vendor/github.com/ 目錄中就會看到 sirupsen 目錄。

准備上手用了,上手之前咱們先規划一下,將這個功能設置成一個中間件,比如:logger.go。

日志可以記錄到 File 中,定義一個 LoggerToFile 方法。

日志可以記錄到 MongoDB 中,定義一個 LoggerToMongo 方法。

日志可以記錄到 ES 中,定義一個 LoggerToES 方法。

日志可以記錄到 MQ 中,定義一個 LoggerToMQ 方法。

...

這次咱們先實現記錄到文件, 實現 LoggerToFile 方法,其他的可以根據自己的需求進行實現。

這個 logger 中間件,創建好了,可以任意在其他項目中進行遷移使用。

廢話不多說,直接看代碼。

 

  package middleware    
    import (    
        "fmt"    
        "ginDemo/config"    
        "github.com/gin-gonic/gin"    
        "github.com/sirupsen/logrus"    
        "os"    
        "path"    
        "time"    
    )    
    // 日志記錄到文件    
    func LoggerToFile() gin.HandlerFunc {    
        logFilePath := config.Log_FILE_PATH    
        logFileName := config.LOG_FILE_NAME    
        //日志文件    
        fileName := path.Join(logFilePath, logFileName)    
        //寫入文件    
        src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)    
        if err != nil {    
            fmt.Println("err", err)    
        }    
        //實例化    
        logger := logrus.New()    
        //設置輸出    
        logger.Out = src    
        //設置日志級別    
        logger.SetLevel(logrus.DebugLevel)    
        //設置日志格式    
        logger.SetFormatter(&logrus.TextFormatter{})    
        return func(c *gin.Context) {    
            // 開始時間    
            startTime := time.Now()    
            // 處理請求    
            c.Next()    
            // 結束時間    
            endTime := time.Now()    
            // 執行時間    
            latencyTime := endTime.Sub(startTime)    
            // 請求方式    
            reqMethod := c.Request.Method    
            // 請求路由    
            reqUri := c.Request.RequestURI    
            // 狀態碼    
            statusCode := c.Writer.Status()    
            // 請求IP    
            clientIP := c.ClientIP()    
            // 日志格式    
            logger.Infof("| %3d | %13v | %15s | %s | %s |",    
                statusCode,    
                latencyTime,    
                clientIP,    
                reqMethod,    
                reqUri,    
            )    
        }    
    }    
    // 日志記錄到 MongoDB    
    func LoggerToMongo() gin.HandlerFunc {    
        return func(c *gin.Context) {    
        }    
    }    
    // 日志記錄到 ES    
    func LoggerToES() gin.HandlerFunc {    
        return func(c *gin.Context) {    
        }    
    }    
    // 日志記錄到 MQ    
    func LoggerToMQ() gin.HandlerFunc {    
        return func(c *gin.Context) {    
        }    
    }

 



日志中間件寫好了,怎么調用呢?

只需在 main.go 中新增:

 

  engine := gin.Default() //在這行后新增    
    engine.Use(middleware.LoggerToFile())

 


執行一下,看看日志:

 

  time="2019-07-17T22:10:45+08:00" level=info msg="| 200 |      27.698µs |             ::1 | GET | /v1/product/add?name=a&price=10 |"    
    time="2019-07-17T22:10:46+08:00" level=info msg="| 200 |      27.239µs |             ::1 | GET | /v1/product/add?name=a&price=10 |"

 



這個 time="2019-07-17T22:10:45+08:00" ,這個時間格式不是咱們想要的,怎么辦?

時間需要格式化一下,修改 logger.SetFormatter

 

  //設置日志格式    
    logger.SetFormatter(&logrus.TextFormatter{    
        TimestampFormat:"2006-01-02 15:04:05",    
    })

 



執行一下,再看日志:

   

time="2019-07-17 22:15:57" level=info msg="| 200 |     185.027µs |             ::1 | GET | /v1/product/add?name=a&price=10 |"    
    time="2019-07-17 22:15:58" level=info msg="| 200 |      56.989µs |             ::1 | GET | /v1/product/add?name=a&price=10 |"

 


時間變得正常了。

我不喜歡文本格式,喜歡 JSON 格式,怎么辦?

    //設置日志格式    
    logger.SetFormatter(&logrus.JSONFormatter{    
        TimestampFormat:"2006-01-02 15:04:05",    
    })

 


執行一下,再看日志:

    {"level":"info","msg":"| 200 |       24.78µs |             ::1 | GET | /v1/product/add?name=a\u0026price=10 |","time":"2019-07-17 22:23:55"}    
    {"level":"info","msg":"| 200 |      26.946µs |             ::1 | GET | /v1/product/add?name=a\u0026price=10 |","time":"2019-07-17 22:23:56"}

 


msg 信息太多,不方便看,怎么辦?

 

  // 日志格式    
    logger.WithFields(logrus.Fields{    
        "status_code"  : statusCode,    
        "latency_time" : latencyTime,    
        "client_ip"    : clientIP,    
        "req_method"   : reqMethod,    
        "req_uri"      : reqUri,    
    }).Info()

 



執行一下,再看日志:

 

  {"client_ip":"::1","latency_time":26681,"level":"info","msg":"","req_method":"GET","req_uri":"/v1/product/add?name=a\u0026price=10","status_code":200,"time":"2019-07-17 22:37:54"}    
    {"client_ip":"::1","latency_time":24315,"level":"info","msg":"","req_method":"GET","req_uri":"/v1/product/add?name=a\u0026price=10","status_code":200,"time":"2019-07-17 22:37:55"}

 



說明一下:time、 msg、 level 這些參數是 logrus 自動加上的。

logrus 支持輸出文件名和行號嗎?

不支持,作者的回復是太耗性能。

不過網上也有人通過 Hook 的方式實現了,選擇在生產環境使用的時候,記得做性能測試。

logrus 支持日志分割嗎?

不支持,但有辦法實現它。

1、可以利用 Linuxlogrotate,統一由運維進行處理。

2、可以利用 file-rotatelogs 實現。

需要導入包:

github.com/lestrrat-go/file-rotatelogs

github.com/rifflock/lfshook

 



奉上完整代碼:

    package middleware    
    import (    
        "fmt"    
        "ginDemo/config"    
        "github.com/gin-gonic/gin"    
        rotatelogs "github.com/lestrrat-go/file-rotatelogs"    
        "github.com/rifflock/lfshook"    
        "github.com/sirupsen/logrus"    
        "os"    
        "path"    
        "time"    
    )    
    // 日志記錄到文件    
    func LoggerToFile() gin.HandlerFunc {    
        logFilePath := config.Log_FILE_PATH    
        logFileName := config.LOG_FILE_NAME    
        // 日志文件    
        fileName := path.Join(logFilePath, logFileName)    
        // 寫入文件    
        src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)    
        if err != nil {    
            fmt.Println("err", err)    
        }    
        // 實例化    
        logger := logrus.New()    
        // 設置輸出    
        logger.Out = src    
        // 設置日志級別    
        logger.SetLevel(logrus.DebugLevel)    
        // 設置 rotatelogs    
        logWriter, err := rotatelogs.New(    
            // 分割后的文件名稱    
            fileName + ".%Y%m%d.log",    
            // 生成軟鏈,指向最新日志文件    
            rotatelogs.WithLinkName(fileName),    
            // 設置最大保存時間(7天)    
            rotatelogs.WithMaxAge(7*24*time.Hour),    
            // 設置日志切割時間間隔(1天)    
            rotatelogs.WithRotationTime(24*time.Hour),    
        )    
        writeMap := lfshook.WriterMap{    
            logrus.InfoLevel:  logWriter,    
            logrus.FatalLevel: logWriter,    
            logrus.DebugLevel: logWriter,    
            logrus.WarnLevel:  logWriter,    
            logrus.ErrorLevel: logWriter,    
            logrus.PanicLevel: logWriter,    
        }    
        lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{    
            TimestampFormat:"2006-01-02 15:04:05",    
        })    
        // 新增 Hook    
        logger.AddHook(lfHook)    
        return func(c *gin.Context) {    
            // 開始時間    
            startTime := time.Now()    
            // 處理請求    
            c.Next()    
            // 結束時間    
            endTime := time.Now()    
            // 執行時間    
            latencyTime := endTime.Sub(startTime)    
            // 請求方式    
            reqMethod := c.Request.Method    
            // 請求路由    
            reqUri := c.Request.RequestURI    
            // 狀態碼    
            statusCode := c.Writer.Status()    
            // 請求IP    
            clientIP := c.ClientIP()    
            // 日志格式    
            logger.WithFields(logrus.Fields{    
                "status_code"  : statusCode,    
                "latency_time" : latencyTime,    
                "client_ip"    : clientIP,    
                "req_method"   : reqMethod,    
                "req_uri"      : reqUri,    
            }).Info()    
        }    
    }    
    // 日志記錄到 MongoDB    
    func LoggerToMongo() gin.HandlerFunc {    
        return func(c *gin.Context) {    
        }    
    }    
    // 日志記錄到 ES    
    func LoggerToES() gin.HandlerFunc {    
        return func(c *gin.Context) {    
        }    
    }    
    // 日志記錄到 MQ    
    func LoggerToMQ() gin.HandlerFunc {    
        return func(c *gin.Context) {    
        }    
    }

 



這時會新生成一個文件 system.log.20190717.log,日志內容與上面的格式一致。

最后, logrus 可擴展的 Hook 很多,大家可以去網上查找。

有些讀者建議,手機上看代碼不方便,建議更新到 GitHub 上。

現已更新,地址如下:

https://github.com/xinliangnote/Go



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM