必须在 DAO 层或 ORM 调用前后手动埋点获取 SQL 耗时,Echo 中间件无法自动拦截;推荐用 context.WithValue 透传 request_id,并结合 GORM SlowThreshold 或 DAO 内 time.Since 记录慢查询。

中间件里怎么拿到 SQL 执行耗时
Echo 中间件本身不接触数据库操作,必须靠你在 DAO 层或 ORM 调用前后手动埋点。常见做法是用 time.Now() 和 time.Since() 包裹实际查询逻辑,把耗时传给中间件上下文或直接写日志。
别指望 Echo 的 c.Get()/c.Set() 自动跨中间件传递耗时——它只在一次 HTTP 请求生命周期内有效,而 SQL 可能发生在任意 handler 或 service 层。更稳妥的方式是:在执行 SQL 前把 time.Now() 存进 context(比如用 context.WithValue()),查完再取出来算差值。
- 不要在中间件里尝试 hook
database/sql的底层连接,那会破坏连接池行为 - 如果你用 GORM,优先考虑它的
logger接口 +Config.SlowThreshold,比自己写中间件更可靠 - 避免在中间件里直接调用
db.Query()等函数——这会让中间件和数据层耦合,违背分层原则
GORM v2 慢查询日志怎么对接 Echo 中间件
GORM 自带慢查询检测,但默认日志输出到 io.Writer,不自动关联 Echo 的 echo.Context。你需要自定义 gorm.Logger,把请求 ID、路径、SQL、耗时等信息一并写入。
关键点是:在 Echo 中间件中生成唯一 request_id 并塞进 context;然后让 GORM logger 从当前 goroutine 的 context(通过 context.WithValue 透传)里读这个 ID。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
func NewGormLogger() logger.Interface {
return logger.New(
log.New(os.Stdout, "\r\n", 0),
logger.Config{
SlowThreshold: time.Millisecond * 100,
LogLevel: logger.Warn,
Colorful: false,
},
)
}
<p>// 实际使用时,需包装成支持 context 的版本,例如在 handler 中:
ctx := c.Request().Context()
ctx = context.WithValue(ctx, "request_id", c.Request().Header.Get("X-Request-ID"))
db.WithContext(ctx).First(&user)
为什么不能在 Echo 中间件里直接拦截 SQL
因为 SQL 查询通常不在 HTTP handler 的直接调用栈里——它可能藏在 service 层、领域模型方法、甚至异步 goroutine 中。Echo 中间件只包裹 handler 执行,对 handler 内部的任意函数调用无感知。
- 中间件看到的是
c.Next()的耗时,不是db.Exec()的耗时 - 一个 handler 可能触发多次 SQL,中间件无法区分哪条慢、哪条快
- 如果 handler 调用了第三方库(如
ent、sqlc),它们的执行路径和日志机制各不相同,统一拦截几乎不可行
推荐的轻量级实现方案
不引入新依赖、不改 ORM 配置,只加几行代码就能记录慢 SQL 和关联请求信息:
- 在每个 DAO 方法入口加
start := time.Now() - 查完立刻算
elapsed := time.Since(start) - 如果
elapsed > 100 * time.Millisecond,用log.Printf或结构化日志(如zerolog.Ctx(c.Request().Context()))写入:SQL 片段、参数、耗时、c.Path()、c.Request().Header.Get("X-Request-ID") - SQL 片段建议截断前 200 字符,避免日志爆炸;参数用
fmt.Sprintf("%v", args)简单打印即可,不用拼完整语句
真正的难点不在记录,而在归因——你得确保慢 SQL 日志能和某次 HTTP 请求明确绑定。漏掉 request ID、没透传 context、或者在 defer 里记日志导致 panic 后丢失,都会让排查失效。

















