golang,go,博客,开源,编程

gin日志中间件gin.Logger

Published on with 0 views and 0 comments

Gin 框架的日志系统是其核心组成部分之一。Gin 的日志系统用于记录 HTTP 请求、响应以及一些运行时信息,可以帮助开发者调试、监控和优化应用。Gin 本身并不直接依赖外部日志库,而是内建了简单的日志处理功能,同时也允许开发者灵活地使用其他日志库如 logruszapslog 等。

Gin 中的日志源码结构

在 Gin 中,日志的相关功能主要集中在以下几个地方:

  1. gin.Logger 中间件 - 负责自动记录 HTTP 请求日志。
  2. gin.DefaultWritergin.DefaultErrorWriter - 用于设置默认的日志输出流。
  3. Logger 相关配置 - 提供了自定义日志格式和输出选项。

1. Logger 中间件源码解析

gin.Logger 是一个中间件,用于记录请求的相关日志,默认会输出访问日志,如请求方法、请求路径、请求时间等。

Logger 中间件的实现位于 gin 包中的 logger.go 文件。我们来看一下它的实现。

package gin

import (
	"fmt"
	"github.com/gin-gonic/gin/render"
	"net/http"
	"time"
)

// Logger 中间件
func Logger() HandlerFunc {
	return func(c *Context) {
		// 获取请求的开始时间
		start := time.Now()

		// 获取请求方法和路径
		method := c.Request.Method
		path := c.Request.URL.Path

		// 记录请求的日志
		c.Next()

		// 请求处理完成后,计算耗时
		end := time.Now()
		latency := end.Sub(start)
		clientIP := c.ClientIP()
		statusCode := c.Writer.Status()

		// 输出日志
		if len(c.Errors) > 0 {
			// 输出错误日志
			for _, e := range c.Errors.Errors() {
				fmt.Fprintf(DefaultErrorWriter, "[GIN] %v | %s | %3d | %13v | %15s | %-7s %#v\n",
					end.Format(time.RFC3339), clientIP, statusCode, latency, method, path, e)
			}
		} else {
			// 输出请求日志
			fmt.Fprintf(DefaultWriter, "[GIN] %v | %s | %3d | %13v | %15s | %-7s %#v\n",
				end.Format(time.RFC3339), clientIP, statusCode, latency, method, path, nil)
		}
	}
}

解析 Logger 中间件

1. 中间件创建

func Logger() HandlerFunc {
    return func(c *Context) {
        start := time.Now() // 记录请求开始时间
        method := c.Request.Method // 请求方法(GET、POST 等)
        path := c.Request.URL.Path // 请求路径

        c.Next() // 让请求继续传递到下一个处理函数或中间件

        // 请求完成后,计算请求耗时
        end := time.Now()
        latency := end.Sub(start)
        clientIP := c.ClientIP() // 获取客户端 IP 地址
        statusCode := c.Writer.Status() // 获取响应的状态码

        // 输出日志
        if len(c.Errors) > 0 { // 如果请求过程中有错误,则输出错误日志
            for _, e := range c.Errors.Errors() {
                fmt.Fprintf(DefaultErrorWriter, "[GIN] %v | %s | %3d | %13v | %15s | %-7s %#v\n",
                    end.Format(time.RFC3339), clientIP, statusCode, latency, method, path, e)
            }
        } else { // 如果没有错误,则输出正常请求日志
            fmt.Fprintf(DefaultWriter, "[GIN] %v | %s | %3d | %13v | %15s | %-7s %#v\n",
                end.Format(time.RFC3339), clientIP, statusCode, latency, method, path, nil)
        }
    }
}

2. 日志输出格式

Logger 中间件输出的日志格式如下:

[GIN] 2022-10-11T10:00:00+08:00 | 192.168.1.1 | 200 | 12ms | 15s | GET /hello
  • 时间戳:表示请求完成的时间,使用 RFC3339 格式。
  • 客户端 IP:记录请求发起的客户端 IP 地址。
  • HTTP 状态码:记录 HTTP 响应的状态码(如 200、404 等)。
  • 耗时:请求的处理时间,单位是纳秒。
  • 请求方法:HTTP 请求的方法(如 GET、POST 等)。
  • 请求路径:HTTP 请求的 URL 路径。
  • 错误信息:如果请求处理过程中有错误,会记录错误信息。

3. 错误日志与常规日志

  • 如果在请求过程中出现了错误(通过 c.Errors 记录的错误),Gin 会输出到 DefaultErrorWriter
  • 否则,正常的请求日志会输出到 DefaultWriter

这两个默认的日志输出流 DefaultWriterDefaultErrorWriter 分别指向标准输出和标准错误流:

var (
    DefaultWriter    io.Writer = os.Stdout  // 默认的日志输出流
    DefaultErrorWriter io.Writer = os.Stderr // 默认的错误日志输出流
)

2. 自定义日志

Gin 允许开发者自定义日志输出流和格式。可以通过修改 DefaultWriterDefaultErrorWriter 来改变日志的输出位置。例如,可以将日志输出到文件中,或者使用第三方日志库。

设置自定义输出流

package main

import (
    "os"
    "github.com/gin-gonic/gin"
)

func main() {
    // 设置 Gin 的日志输出为文件
    file, err := os.Create("gin.log")
    if err != nil {
        panic(err)
    }

    // 设置日志输出流为文件
    gin.DefaultWriter = file
    gin.DefaultErrorWriter = file

    r := gin.Default()

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run(":8080")
}

使用第三方日志库

如果你希望使用更强大的日志库(如 logruszap),可以自定义 Gin 的日志中间件,替换默认的 fmt.Fprintf 日志输出方式。

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/sirupsen/logrus"
)

func Logger() gin.HandlerFunc {
    logger := logrus.New()
    return func(c *gin.Context) {
        // 获取请求的开始时间
        start := time.Now()

        // 获取请求方法和路径
        method := c.Request.Method
        path := c.Request.URL.Path

        // 请求处理
        c.Next()

        // 计算请求的耗时
        latency := time.Now().Sub(start)
        clientIP := c.ClientIP()
        statusCode := c.Writer.Status()

        // 记录日志
        logger.WithFields(logrus.Fields{
            "client_ip": clientIP,
            "status_code": statusCode,
            "method": method,
            "path": path,
            "latency": latency,
        }).Info("Request Details")
    }
}

func main() {
    r := gin.New()
    r.Use(Logger()) // 使用自定义的 Logger 中间件

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run(":8080")
}

在这个示例中,我们使用了 logrus 来记录日志。在中间件中,我们为每个请求生成一个包含客户端 IP、状态码、请求方法、路径和耗时等字段的日志。

3. 日志等级

Gin 的 Logger 中间件本身并不支持日志等级(如 debug、info、warn 等),但你可以通过与 logruszap 等日志库结合使用来实现日志等级的控制。

在使用 logruszap 时,你可以根据环境或配置文件来设置不同的日志等级,从而控制日志的详细程度。

4. 总结

Gin 的日志系统设计简单且高效,默认通过 Logger 中间件记录 HTTP 请求和响应的日志。通过 DefaultWriterDefaultErrorWriter,你可以将日志输出到控制台、文件或其他外部存储中。同时,Gin 提供了灵活的定制能力,允许你替换默认的日志输出,甚至使用更强大的第三方日志库如 logruszap。通过这些功能,Gin 的日志系统可以满足不同场景下的日志需求。


标题:gin日志中间件gin.Logger
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/01/07/1736218929178.html
联系:scotttu@163.com