文章目录
- [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框架内置的日志中间件不直接支持日志级别控制等功能,但可以通过集成第三方日志插件实现,如logrus、zap等。
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()
}