最直接方式是查询前链式调用DB.Debug(),仅对当次操作生效;全局开启需在gorm.Open后配置Logger.LogMode(logger.Info),二者缺一不可。

使用 DB.Debug() 临时开启 SQL 日志
最直接的方式是在具体查询前链式调用 Debug() 方法,它只对当次操作生效,不会影响全局行为。适合调试某条复杂查询或排查某个接口的 SQL 问题。
常见错误是以为调用一次 Debug() 就全局开启——其实它不持久,也不会透传到关联查询或事务中。
-
db.Debug().First(&user, 1)会打印SELECT * FROM users WHERE id = 1及其参数 - 若后续再执行
db.First(&order, 1)(没加Debug()),则不会输出 SQL - 在事务里:必须对事务对象调用,如
tx := db.Begin(); tx.Debug().Create(&u)
初始化时配置全局 Logger 开启 Debug
生产环境之外,想让所有操作都输出 SQL,就得替换 GORM 的 logger。GORM v2 默认使用 log.New() 包装的默认 logger,不输出 SQL 参数;需用 logger.Default.LogMode(logger.Info) 显式启用。
注意:logger.Info 是最低阈值,logger.Warn 或 logger.Error 不会打印 SQL;且该设置必须在 gorm.Open() 之后、首次 DB 操作之前完成。
立即学习“go语言免费学习笔记(深入)”;
- 示例配置:
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) - 如果用了
gorm.Open(..., &gorm.Config{Logger: logger.Default})却没调LogMode(),SQL 依然静默 - 自定义 logger(如写入文件)时,必须确保实现
LogMode接口并返回对应 level 的实例
避免日志被缓冲或丢失的关键点
GORM 的 SQL 日志本质是标准库 log 输出,默认写到 os.Stdout。如果你在容器、测试或某些 IDE 中看不到输出,大概率是日志流被截断或未刷新。
- 确认没有重定向
os.Stdout到空设备(比如测试中用了os.Stdout = io.Discard) - 在短生命周期程序(如 CLI 工具)中,DB 操作后立即退出,可能导致日志来不及刷出——加
time.Sleep(10 * time.Millisecond)或显式log.SetOutput(os.Stdout)重置 - 使用
logger.Default.LogMode(logger.Info)时,若同时设置了Config.NowFunc,要确保它返回的时间可格式化,否则日志可能 panic
调试时别忽略参数绑定和驱动差异
看到的 SQL 是 GORM 构造的“逻辑 SQL”,不是最终发给数据库的字节流。PostgreSQL 驱动用 $1 占位符,MySQL 用 ?,而 SQLite 可能直接拼接——这些占位符是否被正确替换,得看日志里 Args 字段。
- 日志中出现
Args: []interface {}{"name"}表示参数已传入,但若看到Args: []interface {}{nil},说明字段为空或零值未被忽略 -
Debug()不会显示预编译语句的底层 wire 协议内容,抓包或数据库侧日志(如 MySQL 的general_log)才能确认真实发送内容 - 使用
Scan()或原生Raw().Rows()时,Debug()仍有效;但Session(&gorm.Session{DryRun: true})下不会真正执行,SQL 仅模拟生成
GORM 的 SQL 调试依赖两层控制:一个是运行时的 Debug() 开关,另一个是初始化时的 Logger.LogMode() 级别。很多人卡在只设了后者却忘了调 LogMode,或者以为 Debug() 能跨 goroutine 生效——它不能。



















