当前位置: 首页 > news >正文

Gin:自定义日志、验证器与中间件全指南

前言

在使用 Gin 开发 Web 服务时,默认的功能已经能覆盖大部分场景,但在生产环境中我们往往需要更精细的控制——比如定制日志格式以便于 ELK 采集、增加业务专属的参数校验规则、或者编写通用的请求拦截中间件。Gin 本身提供了非常优雅的扩展机制,本文将结合四个实际示例,带你彻底掌握 Gin 中四个常用自定义能力的实现方法。

自定义HTTP访问日志格式

Gin默认的访问日志长这样:

[GIN] 2023/04/19 - 10:41:52 | 200 | 122.767µs | ::1 | GET "/ping"

虽然信息完整,但格式较为固定。如果我们需要与现有日志平台对接,或希望记录更多字段(例如 User-Agent、错误信息等),可以使用gin.LoggerWithFormatter自定义日志输出格式。

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, //用户ipv6地址 param.TimeStamp.Format(time.RFC1123), //访问时间 param.Method, //访问方法 param.Path, //访问路径 param.Request.Proto, //http版本 param.StatusCode, //状态码 param.Latency, //请求处理耗时 param.Request.UserAgent(), //客户端User-Agent头 param.ErrorMessage, //错误信息 ) })) router.Use(gin.Recovery()) router.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) router.Run(":8080") }

如果想输出到文件中,可以这样做:

f, _ := os.Create("gin.log") gin.DefaultWriter = f

自定义路由日志

在启动 Gin 服务时,控制台会打印所有注册的路由信息:

[GIN-debug] POST /foo --> main.main.func1 (3 handlers) [GIN-debug] GET /bar --> main.main.func2 (3 handlers) [GIN-debug] GET /status --> main.main.func3 (3 handlers)

这些信息对调试很有帮助,但格式无法变更。Gin 提供了gin.DebugPrintRouteFunc变量,允许我们接管路由日志的输出。

func main() { r := gin.Default() gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { // 方法,资源路径,函数名,函数个数 log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) } r.POST("/foo", func(c *gin.Context) { c.JSON(http.StatusOK, "foo") }) r.GET("/bar", func(c *gin.Context) { c.JSON(http.StatusOK, "bar") }) r.GET("/status", func(c *gin.Context) { c.JSON(http.StatusOK, "status") }) r.Run() }

示例输出:

[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] [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 2026/04/19 10:41:52 endpoint POST /foo main.main.func2 3 2026/04/19 10:41:52 endpoint GET /bar main.main.func3 3 2026/04/19 10:41:52 endpoint GET /status main.main.func4 3

可以看到,最后三行就是我们自己设置的路由日志了

自定义参数验证器

Gin 内置的binding标签提供了常用验证规则(如requiredemailgt等),但在实际业务中我们常常需要定制校验逻辑,比如判断日期是否晚于今天、字段值是否在指定范围内等。

下面我们为Booking结构体的日期字段实现一个bookabledate验证规则,要求日期不能早于今天。

func main() { r := gin.Default() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("bookabledate", bookleDate) } r.GET("/bookdate", getBookable) r.Run(":8080") } // 验证输入输出, // required:字段不能缺失;bookabledate:自定义规则,由后续函数实现; // gtfield=CheckIn:CheckOut必须大于CheckIn,time_format:指定日期格式2006-01-02 type booking struct { CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"` } // 验证实现函数,日期要晚于今日 var bookleDate validator.Func = func(f validator.FieldLevel) bool { date, ok := f.Field().Interface().(time.Time) if ok { today := time.Now() // 验证日期是否在date之后 if today.After(date) { return false } } return true } func getBookable(c *gin.Context) { var b booking if err := c.ShouldBindWith(&b, binding.Query); err == nil { c.JSON(http.StatusOK, gin.H{"message": "时间验证通过"}) } else { c.JSON(http.StatusBadRequest, gin.H{"error": "时间要在今天之后"}) } }

自定义中间件

中间件是 Gin 最强大的功能之一。通过编写自定义中间件,我们可以实现请求计时、鉴权、日志记录、请求体修改等通用逻辑。

中间件执行流程:

当一个请求到达 Gin 时,它会按顺序执行所有已注册的中间件。如果在中间件中调用了c.Next(),则会先执行后续的中间件和最终的路由处理函数,待它们全部返回后再继续执行c.Next()后面的代码。

func main() { // 创建一个没有中间件的gin引擎 r := gin.New() // 添加自定义的中间件 r.Use(Logger()) r.Use(SetDate()) // 设置响应路由 r.GET("/:name", func(c *gin.Context) { // 从上下文获取值 say := c.MustGet("say").(string) // 解析uri中的参数 name := c.Param("name") // 由于处理太快,输出日志总是0s,所以sleep 1纳秒 time.Sleep(1) c.String(http.StatusOK, "Hello %s,%s", name, say) }) r.Run(":8080") } // 自定义中间件:自定义一个计算其他所有中间件总耗时的中间件 func Logger() gin.HandlerFunc { return func(c *gin.Context) { // 记录请求的时间 t := time.Now() // 暂停当前函数,调用后续所有中间件和路由处理函数 c.Next() // 计算总耗时 latency := time.Since(t) // 输出所用时间 log.Print(latency) } } // 自定义中间件,用于在上下文写入数据供后续使用 func SetDate() gin.HandlerFunc { return func(c *gin.Context) { c.Set("say", "欢迎来到本站!") } }

输出结果:

2026/04/19 11:26:45 508.6µs

可以看到,通过自定义各种中间件,可以得到自己想要的数据或完成想要的功能,比如看请求到响应完成的总耗时。

总结

Gin 的扩展性设计非常优秀,通过上述四个方向的自定义能力,开发者可以轻松实现:

自定义方向核心 API / 方法主要用途
访问日志格式gin.LoggerWithFormatter定制 HTTP 请求日志样式和输出目标
路由注册日志gin.DebugPrintRouteFunc修改启动时路由信息的打印方式
参数验证规则binding.Validator.Engine().RegisterValidation添加业务专属的参数校验逻辑
中间件gin.HandlerFunc+c.Next()实现请求拦截、预处理、后处理

掌握这些自定义技巧,能够让你的 Gin 应用更加健壮、可观测、易维护,更好地适应生产环境的复杂需求。

http://www.cnnetsun.cn/news/2001053.html

相关文章:

  • 别再为模糊的3D重建头疼了!手把手教你用3DSlicer处理DICOM数据,搞定医学影像三维可视化
  • zephyr邪修笔记(9)最简化自定义板子-定制化第一步
  • 告别VS2022!在VSCode里搞定.NET Framework 4.6项目的编译与调试(保姆级教程)
  • 别再只盯着压差了!手把手教你从PSRR、噪声到环路补偿,全面评估一颗LDO芯片
  • 深入理解传输中二层PW和三层BFD之间的关系
  • 从Cortex-M4/M7寄存器入手,聊聊中断嵌套与栈切换那些容易踩的坑
  • 3步破解百度网盘SVIP:macOS逆向工程实战指南
  • 强化学习1——初步理解PPO
  • 编码规范基础:业界通用代码风格总结
  • 别再手动改模型了!用timm库的create_model函数,5分钟搞定PyTorch迁移学习
  • 山东大学软件学院项目实训-创新实训-计科智伴(二)——个人博客(MinIO配置)
  • ST7735S Datasheet里的‘黑话’:SPI时序、0x36寄存器与Gamma曲线,一次搞懂驱动屏的那些关键配置
  • Win11Debloat终极指南:3步轻松清理Windows系统,提升50%性能
  • 终极指南:3分钟快速检测微信单向好友,轻松清理“僵尸“好友
  • K210人脸识别项目实战:如何用SD卡实现人脸特征值的断电保存与加载?
  • C# 14 AOT编译Dify客户端:为什么你的AOT二进制仍加载慢?6个被忽略的IL trimming陷阱曝光
  • 重装系统后,Android Studio老项目报CMake版本错误?三步搞定NDK路径配置(附local.properties详解)
  • 5个步骤快速掌握KMS_VL_ALL_AIO:Windows与Office批量激活终极指南
  • 【硬件知识】光模块学习
  • LeetCode 1855.下标对中的最大距离:双指针
  • Python的__getattr__在属性访问链中的递归调用问题
  • 【2026年最新600套毕设项目分享】微信小程序的电影院订票选座系统(30118)
  • 从流体力学到临床:一文搞懂FFR(血流储备分数)的计算原理与核心价值
  • 避坑指南:在Cesium中构建清晰三维管道网络,如何解决实体重叠与视觉混乱问题?
  • 汽修厂最怕你发现的秘密武器!只输个车型,汽车毛病怎么修全都有
  • MCU逐波保护( CBC)技术
  • 3分钟快速安装TrollStore:TrollInstallerX终极指南
  • 保姆级教程:在Ubuntu 20.04上搞定GAMIT/GLOBK 10.7安装(含gfortran版本避坑指南)
  • 开源移动端性能测试测试工具推荐-perfara
  • “方向盘没松开就答错”?Dify注意力掩码机制深度解析:如何用3行配置实现驾驶专注度感知式应答降频(实测降低误唤醒率76%)