上篇文章分享了 Gin 框架的路由配置,這篇文章分享日誌記錄。git
查了不少資料,Go 的日誌記錄用的最多的仍是 github.com/sirupsen/logrus
。github
Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger.golang
Gin 框架的日誌默認只會在控制檯輸出,我們利用 Logrus
封裝一箇中間件,將日誌記錄到文件中。數組
這篇文章就是學習和使用 Logrus
。框架
好比,咱們約定日誌格式爲 Text,包含字段以下:運維
請求時間
、日誌級別
、狀態碼
、執行時間
、請求IP
、請求方式
、請求路由
。函數
接下來,我們利用 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 支持日誌分割嗎?
不支持,但有辦法實現它。
一、能夠利用 Linux logrotate
,統一由運維進行處理。
二、能夠利用 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
Gin 框架
基礎篇
本文歡迎轉發,轉發請註明做者和出處,謝謝!