slowthreshold 必须显式设置才能触发 warn 级别慢查询判断,gorm v2 默认 200ms 但不自动生效;logmode(logger.warn) 是最小必要配置,配合正确 slowthreshold 和统一日志 writer 才有效。

SlowThreshold 必须显式设置,否则 Warn 级别不触发慢查询判断
GORM v2 默认的 SlowThreshold 是 200ms,但这个值**不会自动生效**——你必须在 logger.Config 中显式赋值,否则哪怕开了 LogMode(logger.Warn),也不会把耗时超阈值的 SQL 标记为“慢”。
-
SlowThreshold是独立开关,和日志级别无关;没设它,Warn 模式只报错、警告(比如连接失败),不按耗时过滤 SQL - 建议设为
200 * time.Millisecond(业务库)或500 * time.Millisecond(报表/分析类查询),避免日志刷屏 - 阈值单位是
time.Duration,别漏掉* time.Millisecond,写成200是纳秒级,永远不触发
LogMode(logger.Warn) 是慢查询日志的最小必要配置
只设 SlowThreshold 不开 LogMode,照样没日志;只开 LogMode(logger.Info) 又会把所有 SQL 都打出来,根本分不清哪条真慢。
-
LogMode(logger.Warn)是最轻量且有效的组合:它默认只输出慢 SQL + 耗时 + 影响行数 + 错误,不干扰正常流程 - 别用
LogMode(logger.Info)上线,尤其在高并发场景下,字符串拼接和反射开销会放大延迟 - 如果需要临时看全量 SQL(比如调试),用
db.Logger.LogMode(logger.Info)动态开启,完事立刻关掉
自定义 logger 时,Trace 方法才是唯一能精准计时的位置
很多人在 Info() 或 Warn() 里打日志,这时 SQL 早已执行完,你拿到的只是结果,不是真实耗时源头。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
Trace()在 SQL 执行完成、结果集已读取、连接尚未归还时调用,能同时拿到begin time、fc()(用于延迟获取 SQL 和影响行数)、err - 务必用
context.WithValue(ctx, key, time.Now())在 Begin 阶段存时间,End 阶段再取——全局time.Now()在并发下会串上下文 - 别在
Trace()里做 I/O(如写文件、发 HTTP),否则拖慢所有数据库操作;日志应交给主应用统一处理
日志输出目标必须和主应用日志一致,否则查问题只能靠猜
GORM 默认往 os.Stdout 写日志,而你的服务日志大概率进了 rotating file 或 lumberjack,导致慢查询记录和请求日志完全脱节。
- 初始化
logger.New()时,第一个参数必须传你项目实际用的日志 writer(比如zapLogger.Desugar().Writer()或log.New(file, "", log.LstdFlags)) - 想串联 traceID,得确保
ctx是从请求链路传入的,而不是塞进全局*gorm.DB实例——Session 创建时要带该ctx - 生产环境保持
ParameterizedQueries: true(默认),避免参数明文拼入日志;如需脱敏后参数值,得自己实现Writer.Printf对args做泛型遍历替换
GORM 的慢查询日志机制本身很稳,但真正容易出问题的地方,从来不是配置项写错,而是日志落点不一致、计时逻辑被并发污染、或者脱敏和性能之间没做取舍。










