iris.logger()需配合iris.timing()才能记录请求耗时,否则${duration}为空;手动获取需用ctx.getduration()/1e6转毫秒,且须在ctx.next()后调用;自定义计时应存start于ctx.values()以确保goroutine安全。

用 iris.Logger() 中间件快速开启耗时日志
默认不记录耗时,必须显式启用。Iris 的 Logger() 中间件本身不打点耗时,但它依赖 iris.Timing() 才能输出 duration 字段。漏掉这一步,日志里只会看到时间戳和状态码,看不到毫秒数。
正确做法是组合使用:
-
app.Use(iris.Timing())—— 注入计时器到上下文 -
app.Use(iris.Logger())—— 日志格式里自动包含${duration}
如果你只加了 Logger(),${duration} 会显示为空或 0ms,这是最常踩的坑。
ctx.GetDuration() 手动获取当前请求耗时(纳秒级)
当你需要把耗时写进监控指标、上报 Sentry 或做条件判断(比如超 500ms 记 warning),就得自己读取。Iris 在请求生命周期中把起始时间存进 context.Context,通过 ctx.GetDuration() 拿到的是纳秒值,不是毫秒。
常见误用:
- 直接打印
ctx.GetDuration()—— 输出像128492345这种数字,难读 - 除以 1000 当毫秒 —— 错,要除以
1e6(1000000)才是毫秒 - 在中间件里调用太早 —— 比如在
BeforeActivation阶段,此时计时还没开始,返回 0
推荐写法:
app.Use(func(ctx iris.Context) {
ctx.Next()
dur := ctx.GetDuration() / 1e6 // 转毫秒
if dur > 500 {
app.Logger().Warnf("slow request: %s %s -> %dms", ctx.Method(), ctx.Path(), dur)
}
})
自定义中间件 + time.Now() 更可控,但要注意 Goroutine 安全
如果项目已用 OpenTelemetry 或需对接 Prometheus,通常得绕过 iris.Timing(),自己埋点。这时别直接用 time.Now() 存局部变量 —— Iris 的 Context 是复用的,多个请求可能共享同一内存地址。
安全做法是把开始时间存在 ctx.Values().Set() 里:
app.Use(func(ctx iris.Context) {
start := time.Now()
ctx.Values().Set("start_time", start)
ctx.Next()
dur := time.Since(start).Milliseconds()
// 上报 metrics 或 log
})
注意:time.Since() 比 time.Now().Sub(start) 更简洁,且语义明确;不要用 ctx.Values().GetTime(),它不存在,Iris 没封装这个方法。
HTTP 标头透传耗时(如 X-Response-Time)要手动注入
Iris 不自动加响应头。想让前端或网关看到耗时,必须自己写:
- 不能在
ctx.Next()前设 header —— 此时 response body 还没生成,header 可能被覆盖 - 必须在
ctx.Next()后、且ctx.Response().WriteHeader()未触发前设置 - 推荐用
ctx.Header("X-Response-Time", fmt.Sprintf("%dms", dur))
如果用了 gzip 中间件,顺序很重要:耗时中间件必须在 iris.Gzip() 之后注册,否则 header 可能被压缩中间件拦截或重写。
真正容易被忽略的点是计时起点 —— Iris 的 Timing() 从路由匹配完成后才开始,不包含 DNS 查找、TLS 握手、请求体读取这些环节。真要端到端耗时,得在反向代理(如 Nginx)或 eBPF 层做。











