发布于2026-07-09 阅读(0)
扫一扫,手机访问
应显式配置 SlowThreshold(如200ms)并设LogLevel为Warn/Error,避免仅用LogMode;日志需通过自定义Writer接入统一日志系统,并在查询前用WithContext透传上下文。

先说说最常见的误区。不少开发者只记得调用 .LogMode(logger.Warn),以为这样就开启了慢查询监控,结果日志里全是连接失败、事务冲突之类的警告,期待的慢SQL却一条也没出现。问题出在哪里?logger.Warn 这个级别本身并不会自动帮你去判断SQL耗时——它只是一个输出门槛。你必须显式地告诉GORM:“到底跑多少毫秒算慢?”
这才是关键:用 WithConfig 指定 SlowThreshold,而不是依赖默认值。来看一段清爽的配置:
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: 200 * time.Millisecond,
LogLevel: logger.Warn,
},
)
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger})
这里有两个细节值得注意:SlowThreshold 的单位是 time.Duration,不是毫秒整数,别写错了;LogLevel 务必设成 Warn 或 Error,避免Info级别下全量SQL刷屏。另外,不要指望 logger.Default.LogMode(logger.Warn) 能继承你想要的阈值——它不会,它只会用默认的100ms。
Writer,但线上慎用默认日志里打印的SQL都是带 ? 占位符的,比如 SELECT * FROM users WHERE id = ?。真要调试时,看到实际绑定的 id = 52 才方便定位问题。那怎么看到真实的参数值?需要自定义 logger.Writer,在 Printf 方法里解析 fc() 返回的SQL和参数。
不过得提个醒:反射取参数值会拉低性能,压测场景下延迟可能翻倍。生产环境还是关掉它,开发或预发阶段用用就好。另外,如果项目里开了 PrepareStmt: true(推荐这样做),日志里天然就是预编译形式,参数脱敏更安全,也就没必要自己解析了。
Trace 方法才是耗时判断的唯一可靠入口GORM 的 Trace 回调在SQL执行完毕、结果集已读取、rowsAffected 可获取之后才触发——这是唯一能拿到真实耗时、行数和错误信息的时机。别想着用 BeforeQuery 或 AfterQuery 钩子替代,它们不保证执行完成,rowsAffected 经常是 -1。
自定义 Trace 时有几个关键点:必须调用 fc() 来获取SQL和行数,不能跳过;elapsed 是从 begin 到 Trace 被调用的时间,代表端到端耗时(包括网络、反序列化);日志内容至少包含 sql、elapsed.Milliseconds()、rows、err。还有一个容易忽略的:不要在 Trace 里做I/O操作(比如写文件),改用缓冲队列或异步 logger 更稳。
Writer 没接对默认情况下,GORM日志会输出到 os.Stdout,和项目主日志(比如用zap、zerolog写到文件或Loki)完全隔离。查问题时得翻两个地方,根本串不起来。解决办法是把项目日志实例包装成 logger.Writer:
type LogWriter struct{ logger *zap.Logger }
func (w LogWriter) Printf(format string, v ...interface{}) {
w.logger.Debug("gorm-sql", zap.String("msg", fmt.Sprintf(format, v...)))
}
再传给 logger.New。这样所有GORM日志都带上了 traceID、请求路径等上下文字段,和业务日志同源可检索。最容易被忽略的是:GORM在预加载(Preload)、事务嵌套等场景会新建 session,原 context 可能丢失。务必在每次查询前用 db.WithContext(ctx) 显式透传,否则你可能会发现日志里少了很多关键信息。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8