GORM v2 原生支持慢查询日志,需同时配置 SlowThreshold 和 LogMode(logger.Warn);Trace 方法是唯一能精准计时并获取完整上下文的位置,务必重写 Trace 并避免 I/O 操作。

直接配 SlowThreshold + LogMode(logger.Warn) 就能记录慢查询,不用自己写钩子或包装驱动——GORM v2 原生支持,且比钩子更准、更全。
为什么不能只设 LogMode(logger.Info)
这样所有 SQL 都会打印,日志爆炸,根本分不清哪条真慢。默认 SlowThreshold 是 200ms,但如果你没显式设置它,哪怕开了 Warn 级别,也不会触发慢查询判断逻辑。必须两者都配:
-
LogLevel设为logger.Warn或更低(Info也行,但慎用) -
SlowThreshold显式指定,比如200 * time.Millisecond - 不设
SlowThreshold时,Warn模式只报错和警告,不按耗时过滤 SQL
Trace 方法才是慢查询日志的真正入口
GORM 的 logger.Trace 在 SQL 执行完、结果集已读取、连接未释放时调用,能拿到真实耗时、影响行数、原始 SQL 和错误。这是唯一能同时满足“精准计时 + 完整上下文”的位置。
- 自定义 logger 时,重写
Trace方法,在elapsed > SlowThreshold时才记录 - 务必调用
fc()获取 SQL 和rowsAffected—— 直接读ctx或反射取不到参数值 - 避免在
Trace里做 I/O(如写文件、发 HTTP),否则会拖慢所有数据库操作 - 如果要用 traceID,确保
ctx是从请求链路传入的,而不是靠中间件统一塞进全局 DB 实例
上线环境必须关掉参数明文输出
默认 ParameterizedQueries: true 会让日志里显示带 ? 的 SQL,但如果你开了 logger.Config{... ParameterizedQueries: false},就会把真实参数值拼进去——这有安全风险,且压测时字符串拼接开销明显。
- 生产环境保持
ParameterizedQueries: true(默认值) - 如需脱敏后参数值,得自己实现
Writer.Printf,对args做泛型遍历和替换,不是简单开关能解决的 - 别信“加个
Debug()就能看参数”——那只是临时升日志级别,参数仍不脱敏,且开发环境外不该用
别被 AfterFind 钩子误导
它在结构体反序列化完成后才触发,此时 SQL 早已执行完毕,连接可能已归还池中,你拿不到真实耗时、SQL 原文、参数绑定值,对 Count、Raw、Session 查询完全无效。
-
BeforeQuery/AfterQuery钩子虽可用,但 v2 中它们不覆盖所有路径(比如预加载的子查询可能绕过) - 真正稳定捕获全部查询生命周期的,只有
Logger.Trace - 想监控慢查询,优先走 logger 接口,钩子只适合做业务侧审计(如记录谁改了哪条数据)
最易被忽略的一点:日志输出目标必须和主应用日志一致。GORM 默认往 os.Stdout 写,而你的服务日志很可能进了 rotatelogs 或 lumberjack 文件,不重定向的话,慢查询日志就散落在不同地方,线上排查等于盲人摸象。











