gin.Logger() 不记录请求体和响应体,因其不消费 c.Request.Body 也不拦截 ResponseWriter;需手动读取 Body 并包装 ResponseWriter 才能记录,但要注意内存与安全问题。

gin.Logger() 为什么日志里看不到请求体和响应体
因为 gin.Logger() 是 Gin 默认提供的轻量日志中间件,它只读取并打印请求行(method、path)、状态码、耗时、客户端 IP 等元信息,**不消费也不缓存 c.Request.Body,也不拦截 ResponseWriter**。所以你自然看不到 POST 的 JSON 内容或返回的响应数据。
- 想记录请求体?必须手动读取
c.Request.Body,再用ioutil.NopCloser把字节重装回去——但注意:大文件会吃内存,生产环境慎用 - 想记录响应体?必须包装
ResponseWriter(比如自定义responseWriter结构体),在c.Next()后从缓冲区取内容 - 调试阶段可用
gin.Mode() == gin.DebugMode控制是否开启体内容记录,避免线上日志爆炸 - 别在日志中间件里调用
c.ShouldBindJSON()——它会提前读空 Body,后续 handler 绑定失败,报错变成invalid character
自定义日志中间件怎么安全地记录操作行为(如增删改)
管理后台的「操作日志」不能简单套用 gin.Logger(),它需要结构化字段(操作人、IP、资源 ID、结果)、异步落库、且要过滤掉 GET 查询类请求。
- 用
c.Request.Method过滤:只处理POST、PUT、DELETE,跳过GET - 用户身份必须由前序鉴权中间件通过
c.Set("user", user)显式写入;下游日志中间件要用user, ok := c.Get("user")判断,ok == false就不该记日志 - 数据库写入必须放
c.Next()之后,且建议起 goroutine 异步执行——但注意:c.Copy()才能安全传入新协程,直接传原始*gin.Context会 panic - 客户端 IP 别直接信
c.ClientIP(),要结合反向代理头(如X-Real-IP)和可信 IP 段配置,否则内网穿透场景下全是 127.0.0.1
多个中间件顺序错了会出什么问题
中间件执行是链式调用,注册顺序 = 请求进入顺序,返回顺序则倒过来。顺序错一个,整个逻辑就可能崩。
- 鉴权中间件(如 JWT 解析)必须放在日志中间件之前:否则未登录请求也会被记日志,还可能因
c.Get("user")返回 nil 导致 panic - 日志中间件必须放在
Recovery之后:否则 panic 发生时日志根本没刷出来,终端只剩一行500,查无可查 - CORS 中间件建议放最前:不然浏览器预检
OPTIONS请求会被鉴权拦住,报Method Not Allowed - 全局
r.Use(a, b, c)和分组group.Use(x, y)是独立链条,后者不会继承前者——想让 API 分组带鉴权但管理后台不带,就得用分组而非全局
要不要自己写 Recovery 中间件?还是直接用 gin.Recovery()
gin.Recovery() 足够应对绝大多数 panic 场景,但默认只打屏、不落盘、不告警。生产环境几乎一定得换。
- 默认
gin.Recovery()会把 panic 错误堆栈打到终端,但服务重启后就没了;换成lumberjack.Logger可按大小/天数轮转写文件 - 错误响应格式要统一:用自定义
RecoveryFunc(如response.ServerError)返回标准 JSON,而不是裸 HTML 或空白页 - 敏感信息要脱敏:非 debug 模式下,
err.Error()不能直接返回给前端,尤其不能暴露路径、变量名、数据库语句 - 别忘了在 Recovery 里加监控上报:比如触发 Prometheus counter + 钉钉告警,否则错误静默发生,你根本不知道
日志不是“有就行”,关键在「谁在什么时候干了什么,结果如何,有没有异常」——中间件顺序、上下文传递、异步安全、体内容读取,每个环节都容易漏掉一环,最后日志看着全,查问题时却什么线索都没有。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











