日志中间件必须显式调用 ctx.Next() 才能继续执行后续 handler,否则请求提前终止;需避免读取 body、禁用 LogRequestBody/LogResponseBody 默认值,改用条件日志或截断;全局注册用 app.Use(),替换默认 logger 为支持轮转的 zerolog。

日志中间件必须显式调用 ctx.Next(),否则请求会提前终止
很多新手写日志中间件时只记录日志就返回,忘了继续执行后续 handler。Iris 不会自动跳转到下一个中间件或路由处理函数——ctx.Next() 是必须手动触发的“放行”动作。
常见错误写法:ctx.StatusCode(200) 后直接 return,结果是响应发出去了,但真正业务逻辑(比如数据库查询、gRPC 调用)根本没跑。
- 正确顺序:记录日志 → 调用
ctx.Next()→ (可选)记录响应后日志 - 如果中间件要拦截请求(如鉴权失败),必须配合
ctx.StopExecution(),否则ctx.Next()之后仍会继续执行 - 别在日志中间件里调用
ctx.ReadBody()或ctx.PostValues(),它们会消费 request body,导致后续 handler 读不到数据
避免日志中间件打印完整请求体导致性能下降
生产环境开启 LogRequestBody: true 看似方便调试,实则危险:每次 POST/PUT 请求都会把整个 body 缓存进内存,大 JSON 或文件上传场景下 GC 压力陡增,QPS 明显下滑。
- 默认应设为
LogRequestBody: false和LogResponseBody: false - 如需调试,改用条件日志:仅对
ctx.StatusCode() >= 400或特定路径(如/debug/*)启用完整日志 - 若必须记录 body,建议只取前 512 字节:
body := ctx.Request().Body+io.LimitReader(body, 512),再用io.ReadAll
用 app.Use() 注册全局日志中间件,而非每个路由单独加
app.Use() 是注册全局前置中间件的标准方式,所有匹配路由都会经过它;而 app.Get("/path", middleware, handler) 只对单个路由生效,容易漏掉新接口。
- 推荐结构:
app.Use(requestLogger)放在所有app.Get/app.Post之前 - 若需区分静态资源和 API 日志,可用
app.Party("/api").Use(apiLogger)分组注册 - 注意:不要在中间件里修改
ctx.Response().Writer的状态码后再调用ctx.Next()——Iris 不会重置状态码,后续 handler 写响应可能触发http: multiple response.WriteHeader calls错误
替换默认 logger 实现文件轮转,原生 iris.Logger() 不支持
Iris 自带的 logger 只能写入 os.File,但不提供按大小/时间切分、压缩、保留天数等功能。长期运行的服务若只用 os.OpenFile("app.log", os.O_APPEND),单文件会无限膨胀。
- 生产环境务必换第三方日志器,例如
github.com/rs/zerolog - 注入方式:
app.Logger().SetChild(zerolog.New(rollingWriter).With().Timestamp().Logger()) - 轮转 writer 需自行实现(如用
gopkg.in/natefinch/lumberjack.v2包封装io.WriteCloser) - 替换后
app.Logger().Infof()仍可用,但底层已走 zerolog,记得调用app.Logger().SetLevel(iris.LogInfo)控制输出级别,防止 debug 日志刷屏
fmt.Sprintf 拼接、同步写磁盘、无节制的 body 缓存,都可能悄悄拖垮服务。


















