Go Web輕量級框架Gin學習系列:HTTP請求日誌

原創做者,公衆號【程序員讀書】,歡迎關注公衆號,轉載文章請註明出處哦。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框架的請求日誌默認在控制檯輸出,但更多的時候,尤爲上線運行時,咱們但願將用戶的請求日誌保存到日誌文件中,以便更好的分析與備份。

1. DefaultWriter

在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) //同時保存到文件和在控制檯中輸出
複製代碼

2. LoggerWithWriter

另外,咱們可使用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")
})
複製代碼

定製日誌格式

1. LogFormatterParams

上面的例子,咱們都是採用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" "
複製代碼

2. LoggerWithConfig

在前面的例子中,咱們使用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應用時必定要記錄用戶請求行爲,而且定時分析過濾。


你的關注,是我寫做路上最大的鼓勵!

相關文章
相關標籤/搜索