Gin框架基础篇009_日志中间件详解

文章目录

  • [1. Gin内置日志中间件](#1. Gin内置日志中间件)
    • [1.1. 默认日志中间件](#1.1. 默认日志中间件)
    • [1.2. 禁用debug日志](#1.2. 禁用debug日志)
    • [1.3. 控制日志颜色](#1.3. 控制日志颜色)
    • [1.4. 自定义日志中间件](#1.4. 自定义日志中间件)
      • [1.4.1. 日志配置项](#1.4.1. 日志配置项)
      • [1.4.2. 自定义方法](#1.4.2. 自定义方法)
      • [1.4.3. 自定义日志格式示例](#1.4.3. 自定义日志格式示例)
      • [1.4.4. 自定义日志输出位置示例](#1.4.4. 自定义日志输出位置示例)
  • [2. 集成第三方日志插件](#2. 集成第三方日志插件)
    • [2.1. 集成 logrus](#2.1. 集成 logrus)
    • [2.2 集成 zap](#2.2 集成 zap)

在开发Web应用时,日志记录是不可或缺的一部分。它不仅帮助开发者调试和排查问题,还能用于监控应用运行状态、分析性能和用户行为。Gin框架提供了灵活的日志记录机制,既包含内置的日志中间件,也支持自定义日志记录方式。

1. Gin内置日志中间件

Gin框架内置了日志中间件,它们记录HTTP请求的基本信息。

1.1. 默认日志中间件

gin.Default()默认使用了gin.Logger()中间件。

go 复制代码
func Default(opts ...OptionFunc) *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery()) // 默认使用了gin.Logger()中间件
	return engine.With(opts...)
}

输出格式类似:

复制代码
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /test/ping                --> gin-quickstart/handler.TestHello (5 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
[GIN] 2025/12/29 - 15:40:59 | 200 |     256.625µs |       127.0.0.1 | GET      "/test/ping"

1.2. 禁用debug日志

gin框架默认使用Debug模式,所以上面的示例中会有很多GIN-debug日志。如果不想打印这些日志,可以将gin模式改为生产模式:

go 复制代码
gin.SetMode(gin.ReleaseMode) // 指定为生产模式
router := gin.Default()

日志示例:

go 复制代码
[GIN] 2025/12/29 - 15:44:41 | 200 |     320.666µs |       127.0.0.1 | GET      "/test/ping"

可以看到,GIN-debug日志都消失了!

1.3. 控制日志颜色

根据检测到的 TTY,控制台的日志输出默认是有颜色的。

我们可以使用以下两个方法来控制日志的颜色显示:

  • gin.DisableConsoleColor():禁用颜色打印。
  • gin.ForceConsoleColor():强制颜色打印。
go 复制代码
// 禁用颜色打印
gin.DisableConsoleColor()
router := gin.Default()

// 强制颜色打印
gin.ForceConsoleColor()
router := gin.Default()

1.4. 自定义日志中间件

1.4.1. 日志配置项

gin框架底层,使用gin.LoggerConfig来配置日志中间件。

go 复制代码
// LoggerConfig 定义日志中间件的配置
type LoggerConfig struct {
	// 格式化方法,默认为gin.defaultLogFormatter
	Formatter LogFormatter

	// 日志输出位置,默认为gin.DefaultWriter
	Output io.Writer

	// 不输出日志的URL路径
	SkipPaths []string

	// 自定义方法,用于判断哪些日志不需要输出
	Skip Skipper
}

默认的gin.Logger()中间件源码如下:

go 复制代码
func Logger() HandlerFunc {
	return LoggerWithConfig(LoggerConfig{})
}

从源码看到,该日志中间件使用的是默认配置:

  • 格式化方法为gin.defaultLogFormatter
  • 日志输出位置为gin.DefaultWriter
  • 没有不需要输出日志的路由路径。

1.4.2. 自定义方法

gin框架提供了如下几个方法供我们自定义日志中间件:

  • gin.LoggerWithConfig(conf LoggerConfig):最灵活的自定义方法,支持直接使用gin.LoggerConfig配置项来灵活自定义日志中间件。
  • gin.LoggerWithFormatter(f LogFormatter):自定义日志格式化方法,控制日志输出格式。
  • gin.LoggerWithWriter(out io.Writer, notlogged ...string):自定义日志输出位置,以及不需要输出日志的路径。

源码:

go 复制代码
// LoggerWithFormatter instance a Logger middleware with the specified log format function.
func LoggerWithFormatter(f LogFormatter) HandlerFunc {
	return LoggerWithConfig(LoggerConfig{
		Formatter: f,
	})
}

// LoggerWithWriter instance a Logger middleware with the specified writer buffer.
// Example: os.Stdout, a file opened in write mode, a socket...
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc {
	return LoggerWithConfig(LoggerConfig{
		Output:    out,
		SkipPaths: notlogged,
	})
}

可以看到,另外两个方法底层,调用的依旧是gin.LoggerWithConfig(conf LoggerConfig)方法!

1.4.3. 自定义日志格式示例

如果需要自定义日志格式,可以使用gin.LoggerWithFormatter()

go 复制代码
r.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,
    )
}))

1.4.4. 自定义日志输出位置示例

通过修改gin.DefaultWriter来设置输出位置:

go 复制代码
// 输出到文件
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f)
r := gin.Default()



// 同时输出到控制台和文件
f, _ := os.Create("gin.log")
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
r := gin.Default()

通过gin.LoggerWithWriter(out io.Writer, notlogged ...string)来设置输出位置:

go 复制代码
// 同时输出到控制台和文件
logFile, _ := os.OpenFile("./logs/gin.log", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
router := gin.New()
router.Use(gin.LoggerWithWriter(io.MultiWriter(logFile, os.Stdout)))

2. 集成第三方日志插件

Gin框架内置的日志中间件不直接支持日志级别控制等功能,但可以通过集成第三方日志插件实现,如logruszap等。

2.1. 集成 logrus

go 复制代码
package main

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

func main() {
    // 设置logrus格式
    log := logrus.New()
    log.SetFormatter(&logrus.JSONFormatter{})
    log.SetOutput(os.Stdout)
    
    // 设置日志级别
    log.SetLevel(logrus.InfoLevel)
    
    r := gin.New()
    
    // 自定义日志中间件
    r.Use(func(c *gin.Context) {
        start := time.Now()
        
        c.Next()
        
        latency := time.Since(start)
        
        clientIP := c.ClientIP()
        method := c.Request.Method
        statusCode := c.Writer.Status()
        path := c.Request.URL.Path
        
        if len(c.Errors) > 0 {
            // 错误日志
            log.WithFields(logrus.Fields{
                "latency": latency,
                "client_ip": clientIP,
                "method": method,
                "status_code": statusCode,
                "path": path,
                "errors": c.Errors.ByType(gin.ErrorTypePrivate).Strings(),
            }).Error("Request Error")
        } else {
            // 普通请求日志
            log.WithFields(logrus.Fields{
                "latency": latency,
                "client_ip": clientIP,
                "method": method,
                "status_code": statusCode,
                "path": path,
            }).Info("Request Info")
        }
    })
    
    r.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello World"})
    })
    
    r.GET("/error", func(c *gin.Context) {
        c.AbortWithError(500, errors.New("internal error"))
    })
    
    r.Run()
}

2.2 集成 zap

Zap是Uber开源的高性能日志库,适合对性能要求较高的场景:

go 复制代码
package main

import (
    "github.com/gin-gonic/gin"
    "go.uber.org/zap"
)

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync() // 确保缓冲区日志被刷新
    
    r := gin.New()
    
    // 自定义zap日志中间件
    r.Use(func(c *gin.Context) {
        start := time.Now()
        path := c.Request.URL.Path
        raw := c.Request.URL.RawQuery
        
        c.Next()
        
        latency := time.Since(start)
        clientIP := c.ClientIP()
        method := c.Request.Method
        statusCode := c.Writer.Status()
        
        if raw != "" {
            path = path + "?" + raw
        }
        
        fields := []zap.Field{
            zap.Int("status", statusCode),
            zap.String("method", method),
            zap.String("path", path),
            zap.String("ip", clientIP),
            zap.Duration("latency", latency),
            zap.String("user-agent", c.Request.UserAgent()),
        }
        
        if len(c.Errors) > 0 {
            fields = append(fields, zap.String("error", c.Errors.ByType(gin.ErrorTypePrivate).String()))
            logger.Error("Request Error", fields...)
        } else {
            logger.Info("Request Info", fields...)
        }
    })
    
    r.GET("/hello", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello World"})
    })
    
    r.Run()
}
相关推荐
光影少年1 天前
前端如何调用gpu渲染,提升gpu渲染
前端·aigc·web·ai编程
源代码•宸1 天前
Golang原理剖析(channel面试与分析)
开发语言·经验分享·后端·面试·golang·select·channel
moxiaoran57531 天前
Go语言中的泛型
golang
加油20191 天前
GO语言内存逃逸和GC机制
golang·内存管理·gc·内存逃逸
源代码•宸1 天前
Golang原理剖析(channel源码分析)
开发语言·后端·golang·select·channel·hchan·sudog
liuyunshengsir1 天前
golang Gin 框架下的大数据量 CSV 流式下载
开发语言·golang·gin
CHHC18801 天前
golang 项目依赖备份
开发语言·后端·golang
老蒋每日coding1 天前
AI智能体设计模式系列(八)—— 记忆管理模式
人工智能·设计模式·golang
Sail-With-Dawn2 天前
免费网站进阶!——InfinityFree创建数据库教程
web
且去填词2 天前
深入理解 GMP 模型:Go 高并发的基石
开发语言·后端·学习·算法·面试·golang·go