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应用时必定要记录用户请求行为,而且定时分析过滤。


你的关注,是我写做路上最大的鼓励!

相关文章
相关标签/搜索