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

直接配 SlowThreshold + LogLevel: logger.Warn 就能记录慢查询,不需要自己写钩子或包装驱动——GORM v2 原生支持,且比钩子更准、更全。
为什么只设 LogMode(logger.Warn) 不生效
很多人试过调用 db.Logger.LogMode(logger.Warn) 却没看到慢 SQL 日志,根本原因是:GORM 的慢查询判断逻辑依赖显式设置的 SlowThreshold。不设它,哪怕日志级别是 Warn,也只会打错误和警告(比如连接失败、约束冲突),不会按耗时过滤 SQL。
-
SlowThreshold默认值是 200ms,但它不是“默认启用”,而是“默认为零值”;必须显式赋值,例如200 * time.Millisecond -
LogLevel必须 ≤Warn(Info也行,但会混入大量正常 SQL) - 不要用
db.LogMode(...)动态切换,它不触发慢查询判断链路,只控制基础日志开关
Trace 是唯一能精准计时并获取完整上下文的位置
GORM 的 logger.Trace 在 SQL 执行完成、结果集已读取、连接尚未释放时被调用,此时才能拿到真实耗时、影响行数、原始 SQL 字符串和错误对象。这是唯一同时满足「精准计时 + 完整上下文」的入口。
- 务必通过
fc()闭包获取 SQL 和rowsAffected:直接从ctx或反射取不到参数值 -
fc()是延迟执行的,避免无谓字符串拼接开销 - 别在
Trace里做 I/O(如写文件、发 HTTP 请求),否则会拖慢所有数据库操作 - 如果要用 traceID,确保
ctx是从请求链路传入的,而不是塞进全局*gorm.DB实例(该实例生命周期远长于单次请求)
如何用 logrus 写入文件并保留慢 SQL 上下文
想把慢 SQL 日志落地到文件(而非仅终端),不能只替换 io.Writer,还要确保自定义 logger 正确实现了 Trace 方法——否则 logrus 只会接管基础日志输出,漏掉慢查询判断逻辑。
- 先创建
logrus.Logger并配置输出到文件:logger.Out = file - 再用它构造 GORM
logger.New,并传入自定义Trace实现(非默认实现) - 示例中常犯错:直接
DB.SetLogger(tools.Logger()),但tools.Logger()返回的是纯*logrus.Logger,没重写Trace,导致慢查询日志丢失 - 正确做法是继承
gorm.Logger,例如定义type SlowQueryLogger struct { gorm.Logger; slowThreshold time.Duration },然后重写Trace
调试阶段想看带参 SQL?别用 Info 级别
很多人误以为把日志设成 Info 就能看到实际执行的 SQL(含参数值),但 GORM v2 中 ParameterizedQueries: true 仅在 Debug 级别下生效。Info 模式下它被完全忽略,SQL 永远显示问号占位符。
- 要看到带参 SQL,必须设
LogLevel: logger.Debug+ParameterizedQueries: true -
Debug级别会额外打印事务、钩子、反射等信息,日志量激增,且含原始参数(可能泄露密码、token) - 生产环境禁用
Debug;用构建标签或配置项控制 logger 创建,例如:if os.Getenv("APP_ENV") == "debug" { ... } - 高频调用附近临时加
.Debug()(如db.Session(&session).Debug().First(...))会覆盖全局配置,引入竞态风险,应避免
真正难的不是写几行 logger 配置,而是理解 GORM 日志链路里 SlowThreshold、LogLevel、Trace 三者的协作边界——它们不在同一层,缺一不可,且任意一个配错都会静默失效。



















