概述
上篇文章分享了 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、可以利用 Linux logrotate
,統一由運維進行處理。
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
推薦閱讀
- Gin 框架 - 安裝和路由配置
- Go - 函式
- Go - 迴圈
- Go - Map 集合
- Go - Struct 結構體
- Go - Slice 切片
- Go - 陣列
- Go - 變數宣告
- Go - 環境安裝
本文歡迎轉發,轉發請註明作者和出處,謝謝!