会,因为Iris中间件不自动携带Zap日志实例,直接调用未初始化的*zap.Logger会导致nil pointer dereference panic。

中间件里直接用 Zap 日志实例会 panic 吗?
会,而且很常见。Iris 的中间件函数签名是 func(ctx iris.Context),它不自动携带任何日志实例;如果你在中间件里直接调用全局 zap.L().Info() 或未初始化的 *zap.Logger,运行时可能报 nil pointer dereference。根本原因是:Zap 不是线程安全的全局单例(哪怕用了 zap.L(),也得确保它已被 zap.Must(zap.NewProduction()) 等正确初始化),更不能在中间件中随意 new 一个 logger 而不管理生命周期。
怎么把 *zap.Logger 安全注入到 iris.Context
推荐用 Iris 的上下文值传递机制,而不是全局变量或闭包捕获。在启动时把 logger 注入到 app,再在中间件中从 ctx.Values() 取出——这是最轻量、最可控的方式:
app := iris.New()
logger, _ := zap.NewProduction() // 或 Development()
app.Logger().SetOutput(logger.Core()) // 这步可选,仅影响 Iris 自身日志输出
app.Use(func(ctx iris.Context) {
ctx.Values().Set("logger", logger)
ctx.Next()
})
后续中间件里就能安全取用:
app.Use(func(ctx iris.Context) {
logger, _ := ctx.Values().Get("logger").(*zap.Logger)
logger.Info("request started",
zap.String("method", ctx.Method()),
zap.String("path", ctx.Path()),
zap.String("ip", ctx.RemoteAddr()))
ctx.Next()
})
Zap 字段命名和请求上下文怎么对齐?
别硬写 zap.String("url", ctx.Request().URL.String()) 这类易错字段。Iris 提供了更稳定的访问方式,且要注意字段名风格统一(比如全小写 + 下划线),避免和 Zap 内置字段冲突(如 error):
-
zap.String("method", ctx.Method())—— 比ctx.Request().Method更可靠 -
zap.String("path", ctx.Path())—— 已解析路由路径,不含 query -
zap.String("query", ctx.Request().URL.RawQuery)—— 如需完整 query string -
zap.String("user_agent", ctx.GetHeader("User-Agent"))—— Header 访问更安全 -
zap.Int("status", ctx.ResponseWriter().StatusCode())—— 注意:必须在ctx.Next()之后读取
如果要记录耗时,别用 time.Since() 手动算,改用 ctx.GetDuration()(需启用 app.Use(iris.Recovery()) 和 app.Use(iris.Logger()) 前置中间件,否则为 0)。
为什么日志里看不到请求 body 或 form 数据?
因为 ctx.Request().Body 是单次读取流,Iris 默认不缓存它;你一旦调用 ctx.ReadJSON() 或 ctx.FormValue(),原始 body 就被消费掉了,Zap 中再读就是空。真要记录 body,必须提前用 ctx.ReadBody() 缓存,并控制大小(防 OOM):
app.Use(func(ctx iris.Context) {
logger := ctx.Values().Get("logger").(*zap.Logger)
if ctx.ContentLength() > 0 && ctx.ContentLength() < 1024*10 { // 限制 10KB
body, _ := ctx.ReadAll()
logger.Info("request body", zap.ByteString("body", body))
ctx.ResetBody(body) // 恢复 body 供后续处理器使用
}
ctx.Next()
})
注意:ctx.ResetBody() 是 Iris v13+ 才支持的方法;旧版本需用 ctx.Request().Body = ioutil.NopCloser(bytes.NewReader(body)) 手动重置,但更易出错。
实际部署时,body 日志建议只在 debug 环境开启,生产环境默认关闭——字段膨胀、敏感信息泄露、性能损耗三重风险都得手动兜住。


















