直接修改 e.logger.formatter 会 panic,因为 echo v4.10+ 的默认 logger 是只读实例,formatter 字段不可赋值;必须用新 logger 实例替换整个 e.logger。

为什么直接改 e.Logger.Formatter 会 panic
因为 Echo v4.10+ 的默认 e.Logger 是只读实例,底层由 zerolog 构建,启动后其 Formatter 字段不可赋值。你写 e.Logger.Formatter = myWriter 看似合理,实则触发 runtime panic:「assignment to field Formatter in immutable struct」。这不是 bug,是设计约束——必须用新 Logger 实例替换整个 e.Logger,而不是“修改”它。
如何注册可定制的全局日志中间件
核心动作只有两步:先构造带 Formatter 的新 echo.Logger 实例,再用 e.Use() 注册为中间件。不能用 e.Logger = ... 赋值,也不能漏掉 c.Next()。
- 调用
echo.NewLogger()创建新 logger,传入echo.LoggerConfig{Formatter: ...} -
Formatter必须是实现了zerolog.Formatter接口的值,比如zerolog.ConsoleWriter或zerolog.JSONWriter - 注册时用
e.Use(middleware.LoggerWithConfig(...)),不是e.Use(e.Logger)—— 后者只是把只读 logger 当中间件传进去,不生效 - 中间件函数体内必须调用
c.Next(),否则请求卡在日志层,后续 handler 永远不执行
ConsoleWriter 和 JSONWriter 的关键参数差异
两者都需显式设置字段才真正生效,zerolog 不会 fallback 默认值。开发环境用 ConsoleWriter,生产环境建议 JSONWriter。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
-
ConsoleWriter.TimeFormat必须设为"2006-01-02 15:04:05"才显示可读时间;不设就是 Unix 时间戳 -
ConsoleWriter.NoColor = true在容器或日志采集系统里必须开启,否则 ANSI 颜色字符污染日志流 -
JSONWriter.TimeFieldFormat = zerolog.TimeFormatUnixMicro可输出微秒级时间戳,比默认秒级更准 -
JSONWriter.SetIndent("", " ")让 JSON 日志可读性提升,但上线前建议关掉(减少序列化开销)
怎么把 trace_id、user_id 注入每条日志
zerolog 的 With() 是非线程安全的链式构建器,不能复用 logger 实例。必须在中间件中每次请求都生成新 logger,并存入 c 上下文。
- 在中间件开头生成唯一
reqID := atomic.AddUint64(&idCounter, 1) - 调用
c.Get("logger").(*zerolog.Logger).With().Uint64("req_id", reqID).Str("path", c.Request().URL.Path).Logger() - 再用
c.Set("logger", newLogger)存回去,后续 handler 中c.Get("logger")就能拿到带上下文字段的实例 - 注意:
c.Get("logger")返回的是 interface{},必须类型断言成*zerolog.Logger才能调With()
漏掉类型断言或复用未 With 的 logger 实例,会导致所有请求日志共享同一份字段,trace_id 混乱——这是线上最常被忽略的并发陷阱。










