要看到带参数的完整SQL,必须用logger.Debug级别并设置ParameterizedQueries: true;Info级别只显示问号占位符,因GORM v2默认分离SQL与参数传输,避免敏感数据落盘和减少拼接开销。

要看到带参数的完整 SQL,必须用 logger.Debug 级别 + ParameterizedQueries: true;仅设 logger.Info 只会打印问号占位符,参数永远不出现。
为什么 logger.Info 不显示真实参数值
GORM v2 默认把 SQL 和参数分离传输——SQL 字符串走一条路径,参数数组走另一条,日志层在 Info 级别只拿到前者。这不是遗漏,是刻意设计:避免敏感数据(如密码、手机号)意外落盘,也省去字符串拼接开销。你看到的 SELECT * FROM users WHERE id = ? 就是它本意呈现的样子。
-
LogLevel: logger.Info时,ParameterizedQueries开关完全被忽略 - 想看参数代入后的结果,唯一可靠路径是升到
Debug级别 - SQLite 驱动对参数日志支持弱,MySQL/PostgreSQL 更稳定,开发环境优先用后者验证
配置带参 Debug 日志的最小可行代码
不是加个 .Debug() 链式调用就行,必须重建 logger 实例并注入 gorm.Config:
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Debug, // 必须是 Debug
Colorful: true,
ParameterizedQueries: true, // 关键:启用参数替换
},
)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
-
Colorful: true让 SQL 在终端高亮,一眼识别出语句块 - 别用
logger.Default.LogMode(logger.Debug)动态切换——它不触发ParameterizedQueries逻辑 -
SlowThreshold在Debug下仍有效,但慢查询会混在大量日志里,建议开发期单独用Warn级配SlowThreshold做专项监控
生产环境千万别开 Debug 日志
Debug 不只是打 SQL:事务开始/提交、钩子调用、结构体反射过程、预加载子查询的每一步都会输出。日志量可能暴涨 10 倍,且原始参数明文可见——密码、token、身份证号全在里头。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用构建标签或环境变量控制 logger 创建,例如:
if os.Getenv("APP_ENV") == "dev" { /* 启用 Debug logger */ } - 禁止在高频调用附近临时写
db.Debug().First(&u, 1),这会覆盖全局 logger,且无法保证所有子查询(如Preload)都被捕获 - 上线前务必确认
ParameterizedQueries是false(默认值),否则参数值会被拼进日志——不是脱敏,是直接暴露
慢查询日志该用 Warn 而不是 Debug
真正需要盯的不是“所有 SQL”,而是“哪些 SQL 拖垮了 P95”。GORM 原生支持慢查询过滤,但必须两个条件同时满足:
-
LogLevel设为logger.Warn或更低(Info也可,但日志太多) -
SlowThreshold显式设置,比如200 * time.Millisecond;不设它,Warn级也不会触发慢判断 - 所有查询(含
Preload、Raw、事务内操作)都走同一套判定逻辑,比自定义钩子更全更准
最易被忽略的是:日志输出目标必须和主应用一致。GORM 默认往 os.Stdout 写,而你的服务日志大概率进了 rotating file 或 syslog。不统一输出通道,排查时根本找不到那条慢 SQL。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










