Iris 日志中间件需手动配对起止时间才能统计耗时;因中间件线性执行且无自动结束钩子,仅记录“start”无法得出真实耗时,正确做法是用 defer + time.Since() 或 ctx.OnClose() 在请求真正结束时记录。

必须手动配对记录起止时间,Iris 不提供自动生命周期钩子;只打“start”不打“end”,就永远算不出耗时。
为什么日志中间件里看不到请求完成时间
因为 Iris 的中间件是线性执行的,ctx.Next() 之后才是 controller 和后续中间件,但框架不会自动在请求结束时触发回调。如果你只在中间件开头 log.Println("start"),没在 ctx.Next() 后补上结束标记或耗时计算,那日志里就只有开始、没有结束——自然无法统计真实耗时。
常见错误写法:
func logger(ctx iris.Context) {
log.Printf("[START] %s %s", ctx.Method(), ctx.Path())
ctx.Next() // ← 这里执行完 controller,但你没做任何事后操作
// ← 缺少结束记录!
}
正确做法:用 ctx.OnClose() 或 defer + time.Since()
ctx.OnClose() 是 Iris 提供的、在响应写入完成且连接即将关闭时触发的回调,适合记录最终耗时;但要注意它可能在异步 goroutine 中执行,不能直接修改 response 或调用 ctx.StatusCode() 等会 panic 的方法。
更稳妥通用的做法是用 defer + time.Now():
- 在中间件开头记下
start := time.Now() - 用
defer在函数返回前计算并打印耗时:log.Printf("cost: %v", time.Since(start)) - 这个
defer会在ctx.Next()执行完、controller 返回、所有中间件出栈后才触发,时机准确
示例:
func logger(ctx iris.Context) {
start := time.Now()
ctx.Next()
log.Printf("[LOG] %s %s %d %v",
ctx.Method(), ctx.Path(),
ctx.GetStatusCode(), time.Since(start))
}
注意 ctx.GetStatusCode() 可能不准
ctx.GetStatusCode() 返回的是当前已设置的状态码,但如果 controller 或后续中间件里调用了 ctx.StatusCode(500) 或发生了 panic(被 Iris 默认 recover),这个值可能还没更新。实际生产中建议结合 ctx.ResponseWriter().Status() 获取更可靠的值(需确保 response 已 flush)。
更健壮的写法是把耗时日志放在 ctx.OnClose() 里,并只记录基础字段:
func logger(ctx iris.Context) {
start := time.Now()
ctx.OnClose(func() {
log.Printf("[CLOSE] %s %s %v", ctx.Method(), ctx.Path(), time.Since(start))
})
ctx.Next()
}
注意:ctx.OnClose() 注册多次会全部执行,适合做清理或审计,但不适合修改 response。
真正容易被忽略的是:耗时统计必须和日志写入强绑定,不能依赖 controller 里另起 goroutine 打日志——那样时间戳错乱、状态码不可信、甚至日志丢失。盯住 defer 和 OnClose 这两个点,就踩不进坑。











