GORM v2 中 logger.Info 默认不显示参数值,因SQL与参数分离以保障安全和性能;要显示带参SQL,必须设 LogLevel 为 Debug 并启用 ParameterizedQueries。

为什么 logger.Info 默认不显示参数值
GORM v2 默认使用 logger.Info 级别输出 SQL,但只打印带问号占位符的语句(如 SELECT * FROM users WHERE id = ?),参数被单独传入驱动,日志层看不到实际值。这不是 bug,而是为避免敏感数据泄露和性能损耗做的默认隔离。
真正要看到「带参数的完整 SQL」,必须启用 GORM 的 logger.Config 中的 ParameterizedQueries,并确保日志级别 ≥ Info —— 但注意:这个开关仅对 Debug 级别生效,Info 不会拼接参数。
-
ParameterizedQueries: true只在Debug模式下起作用,Info模式下它被忽略 - 直接用
log.Println或第三方 logger 接管时,需手动格式化参数,GORM 不自动注入 - SQLite 驱动对参数日志支持较弱,MySQL/PostgreSQL 更稳定
配置 Config.Logger 启用带参 Debug 日志
最可靠的方式是把 GORM 日志级别设为 Debug,并开启 ParameterizedQueries。GORM 会在该模式下把参数原样代入 SQL 字符串(非执行,仅日志)。
import (
"gorm.io/gorm/logger"
"log"
"os"
)
<p>newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Debug, // 必须是 Debug
Colorful: true,
ParameterizedQueries: true, // 关键:启用参数替换
},
)</p><p>db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
-
LogLevel: logger.Debug是硬性前提,Info或Warn下ParameterizedQueries无效 -
Colorful: true能让 SQL 高亮,方便快速识别(尤其在终端查看时) - 不要用
logger.Default.LogMode(logger.Info)动态切换,它不触发参数拼接逻辑
避免在生产环境误开 Debug 日志
Debug 级别不仅打 SQL,还会记录事务开始/提交、钩子调用、结构体反射过程等,日志量激增,且含原始参数(可能含密码、token、手机号等)。不能靠「加个 if 判断环境」临时开启。
立即学习“go语言免费学习笔记(深入)”;
- 用构建标签或配置项控制 logger 实例创建,例如:
if os.Getenv("GIN_MODE") == "debug" { ... } - 禁止在
db.First(&u, 1)这类高频调用附近动态调用db.Session(&session).Debug().First(...),这会覆盖全局 logger 配置并引发竞态 - 若只需某次查询看参数,改用
db.ToSQL()(v2.2.5+):sql, vars := db.Where("id = ?", 123).ToSQL(db.Statement),再自己 printf
自定义 logger 实现更灵活的参数日志(进阶)
如果既要参数又要可控级别(比如只对特定表开启),得实现 logger.Interface。核心是重写 Trace 方法,在 begin 和 end 之间拿到 sql 和 rows,再用 fmt.Sprintf 替换 ? —— 但注意:GORM 不暴露参数类型,需按 []interface{} 顺序逐个格式化。
func (l myLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
sql, rows := fc()
if len(l.vars) > 0 && strings.Contains(sql, "?") {
sql = l.replaceParams(sql, l.vars) // 自己实现:遍历 l.vars,用 %v 替换每个 ?
}
log.Printf("[%.3fs] [%d] %s", time.Since(begin).Seconds(), rows, sql)
}
- GORM 把参数存于
stmt.Settings["gorm:query:vars"],但需在自定义 logger 的Before钩子里提前取出来 - 直接用
fmt.Sprint(l.vars)拼接不安全:[]byte会转成切片地址,time.Time缺少格式控制 - 这种方案绕过 GORM 内置逻辑,升级 GORM 版本时容易断裂,建议仅用于调试或审计场景
GORM 的参数化日志不是开关一开就完事,它和日志级别、驱动实现、甚至构建环境都咬合得很紧。最容易被忽略的是:你看到的「带参数 SQL」根本没发给数据库,只是 logger 拼出来的字符串——它不保证和真实执行的 SQL 完全一致,尤其涉及 database/sql 预处理重写或驱动自动转义时。



















