原創做者,公衆號【程序員讀書】,歡迎關注公衆號,轉載文章請註明出處哦。git
咱們知道,用戶向服務器發起的每一次Web請求,都會經過HTTP協議頭部或Body攜帶許多的請求元信息給服務器,如請求的URL地址,請求方法,請求頭部和請求IP地址等等諸多原始信息,而在Gin框架中,咱們可使用日誌的方式記錄和輸出這些信息,記錄用戶的每一次請求行爲。程序員
下面是一條Gin框架在控制檯中輸出的日誌:github
[GIN] 2019/05/04 - 22:08:56 | 200 | 5.9997ms | ::1 | GET /test
複製代碼
好了,下面看看要如何輸出上面的日誌吧!數據庫
在Gin框架中,要輸出用戶的http請求日誌,最直接簡單的方式就是藉助日誌中間件,下面Gin框架的中間件定義:bash
func Logger() HandlerFunc
複製代碼
因此,當咱們使用下面的代碼建立一個gin.Engine
時,會在控制檯中用戶的請求日誌:服務器
router := gin.Default()
複製代碼
而使用下面的代碼建立gin.Engine
時,則不會在控制檯輸出用戶的請求日誌:框架
router := gin.New()
複製代碼
這是爲何呢?這是因爲使用Default()
函數建立的gin.Engine
實例默認使用了日誌中件間gin.Logger()
,因此,當咱們使用第二種方式建立gin.Engine
時,能夠調用gin.Engine
中的Use()
方法調用gin.Logger()
,以下:函數
router := gin.New()
router.Use(gin.Logger())
複製代碼
Gin框架請求日誌默認是在咱們運行程序的控制檯中輸出,並且輸出的日誌中有些字體有標顏色,以下圖所示:字體
固然,咱們可使用DisableConsoleColor()
函數禁用控制檯日誌的顏色輸出,代碼以下所示ui
gin.DisableConsoleColor()//禁用請求日誌控制檯字體顏色
router := gin.Default()
router.GET("test",func(c *gin.Context){
c.JSON(200,"test")
})
複製代碼
運行後發出Web請求,在控制檯輸出日誌字體則沒有顏色:
雖然Gin框架默認是開始日誌字體顏色的,但可使用DisableConsoleColor()
函數來禁用,但當被禁用後,在程序中運行須要從新打開控制檯日誌的字體顏色輸出時,可使用ForceConsoleColor()
函數從新開啓,使用以下:
gin.ForceConsoleColor()
複製代碼
Gin框架的請求日誌默認在控制檯輸出,但更多的時候,尤爲上線運行時,咱們但願將用戶的請求日誌保存到日誌文件中,以便更好的分析與備份。
在Gin框架中,經過gin.DefaultWriter
變量可能控制日誌的保存方式,gin.DefaultWriter
在Gin框架中的定義以下:
var DefaultWriter io.Writer = os.Stdout
複製代碼
從上面的定義咱們能夠看出,gin.DefaultWriter
的類型爲io.Writer
,默認值爲os.Stdout
,即控制檯輸出,所以咱們能夠經過修改gin.DefaultWriter
值來將請求日誌保存到日誌文件或其餘地方(好比數據庫)。
package main
import (
"github.com/gin-gonic/gin"
"io"
"os"
)
func main() {
gin.DisableConsoleColor()//保存到文件不須要顏色
file, _ := os.Create("access.log")
gin.DefaultWriter = file
//gin.DefaultWriter = io.MultiWriter(file) 效果是同樣的
router := gin.Default()
router.GET("/test", func(c *gin.Context) {
c.String(200, "test")
})
_ = router.Run(":8080")
}
複製代碼
運行後上面的程序,會在程序所在目錄建立access.log
文件,當咱們發起Web請求後,請求的日誌會保存到access.log
文件,而不會在控制檯輸出。
經過下面的代碼,也可能讓請求日誌同行保存到文件和在控制檯輸出:
file, _ := os.Create("access.log")
gin.DefaultWriter = io.MultiWriter(file,os.Stdout) //同時保存到文件和在控制檯中輸出
複製代碼
另外,咱們可使用gin.LoggerWithWriter
中間件,其定義以下:
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc
複製代碼
示例代碼:
package main
import (
"github.com/gin-gonic/gin"
"os"
)
func main() {
gin.DisableConsoleColor()
router := gin.New()
file, _ := os.Create("access.log")
router.Use(gin.LoggerWithWriter(file,""))
router.GET("test", func(c *gin.Context) {
c.JSON(200,"test")
})
_ = router.Run()
}
複製代碼
gin.LoggerWithWriter
中間件的第二個參數,能夠指定哪一個請求路徑不輸出請求日誌,例以下面代碼,/test
請求不會輸出請求日誌,而/ping
請求日誌則會輸出請求日誌。
router.Use(gin.LoggerWithWriter(file,"/test"))//指定/test請求不輸出日誌
router.GET("test", func(c *gin.Context) {
c.JSON(200,"test")
})
router.GET("ping", func(c *gin.Context) {
c.JSON(200,"pong")
})
複製代碼
上面的例子,咱們都是採用Gin框架默認的日誌格式,但默認格式可能並不能知足咱們的需求,因此,咱們可使用Gin框架提供的gin.LoggterWithFormatter()
中間件,定製日誌格式,gin.LoggterWithFormatter()
中間件的定義以下:
func LoggerWithFormatter(f LogFormatter) HandlerFunc
複製代碼
從gin.LoggterWithFormatter()
中間件的定義能夠看到該中間件的接受一個數據類型爲LogFormatter
的參數,LogFormatter
定義以下:
type LogFormatter func(params LogFormatterParams) string
複製代碼
從LogFormatter
的定義看到該類型爲func(params LogFormatterParams) string
的函數,其參數是爲LogFormatterParams
,其定義以下:
type LogFormatterParams struct {
Request *http.Request
TimeStamp time.Time
StatusCode int
Latency time.Duration
ClientIP string
Method string
Path string
ErrorMessage string
BodySize int
Keys map[string]interface{}
}
複製代碼
定製日誌格式示例代碼:
func main() {
router := gin.New()
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
//定製日誌格式
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
_ = router.Run(":8080")
}
複製代碼
運行上面的程序後,發起Web請求,控制檯會輸出如下格式的請求日誌:
::1 - [Wed, 08 May 2019 21:53:17 CST] "GET /ping HTTP/1.1 200 1.0169ms "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" "
複製代碼
在前面的例子中,咱們使用gin.Logger()
開啓請求日誌、使用gin.LoggerWithWriter
將日誌寫到文件中,使用gin.LoggerWithFormatter
定製日誌格式,而實際上,這三個中間件,其底層都是調用gin.LoggerWithConfig
中間件,也就說,咱們使用gin.LoggerWithConfig
中間件,即可以完成上述中間件全部的功能,gin.LoggerWithConfig
的定義以下:
func LoggerWithConfig(conf LoggerConfig) HandlerFunc
複製代碼
gin.LoggerWithConfig
中間件的參數爲LoggerConfig
結構,該結構體定義以下:
type LoggerConfig struct {
// 設置日誌格式
// 可選 默認值爲:gin.defaultLogFormatter
Formatter LogFormatter
// Output用於設置日誌將寫到哪裏去
// 可選. 默認值爲:gin.DefaultWriter.
Output io.Writer
// 可選,SkipPaths切片用於定製哪些請求url不在請求日誌中輸出.
SkipPaths []string
}
複製代碼
如下例子演示如何使用gin.LoggerConfig
達到日誌格式、輸出日誌文件以及忽略某些路徑的用法:
func main() {
router := gin.New()
file, _ := os.Create("access.log")
c := gin.LoggerConfig{
Output:file,
SkipPaths:[]string{"/test"},
Formatter: func(params gin.LogFormatterParams) string {
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
params.ClientIP,
params.TimeStamp.Format(time.RFC1123),
params.Method,
params.Path,
params.Request.Proto,
params.StatusCode,
params.Latency,
params.Request.UserAgent(),
params.ErrorMessage,
)
},
}
router.Use(gin.LoggerWithConfig(c))
router.Use(gin.Recovery())
router.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
router.GET("/test", func(c *gin.Context) {
c.String(200, "test")
})
_ = router.Run(":8080")
}
複製代碼
運行上面的程序後,發起Web請求,控制檯會輸出如下格式的請求日誌:
::1 - [Wed, 08 May 2019 22:39:43 CST] "GET /ping HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" "
::1 - [Wed, 08 May 2019 22:39:46 CST] "GET /ping HTTP/1.1 200 0s "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" "
複製代碼
每條HTTP請求日誌,都對應一次用戶的請求行爲,記錄每一條用戶請求日誌,對於咱們追蹤用戶行爲,過濾用戶非法請求,排查程序運行產生的各類問題相當重要,所以,開發Web應用時必定要記錄用戶請求行爲,而且定時分析過濾。
你的關注,是我寫做路上最大的鼓勵!