不算。标准库log只是基础输出工具,不拦截请求、不注入上下文、不支持链路追踪;真正日志中间件须嵌入http处理链,通过包装http.handler自动记录方法、路径、状态码、耗时等信息。

Go 用 log 包直接写日志算不算中间件?
不算。标准库 log 是基础输出工具,不拦截请求、不自动注入上下文、不支持链路追踪——它只是“打印”,不是“中间件”。真正意义上的日志中间件,要嵌入 HTTP 处理链(如 http.Handler),在每次请求进入和返回时自动记录时间、状态码、路径、耗时等信息。
如何用 middleware 方式包装 http.Handler 记录日志?
最轻量、无依赖的做法是手写一个闭包函数,接收原始 http.Handler 并返回带日志逻辑的新 http.Handler:
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 包装 ResponseWriter 以捕获状态码和字节数
lw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(lw, r)
log.Printf(
"%s %s %d %dms",
r.Method,
r.URL.Path,
lw.statusCode,
time.Since(start).Milliseconds(),
)
})
}
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
func (lw *loggingResponseWriter) WriteHeader(code int) {
lw.statusCode = code
lw.ResponseWriter.WriteHeader(code)
}
- 必须包装
ResponseWriter,否则拿不到真实状态码(WriteHeader可能被下游覆盖) - 不要在
defer里记日志——若 handler panic,defer执行时可能已丢失请求上下文 - 如果用了
gorilla/mux或chi,它们自带Logger中间件,但底层逻辑和上面一致
用 zap 替换 log.Printf 有什么关键差异?
zap 不是简单替换函数名,它要求显式传入结构化字段,并且默认不支持直接格式化字符串(避免隐式分配)。日志中间件里需改写为:
logger.Info("http request",
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.Int("status", lw.statusCode),
zap.Float64("duration_ms", time.Since(start).Seconds()*1000),
)
- 别用
logger.Sugar().Infof(...)—— 它会退化成 fmt.Sprintf,失去结构化优势,也更慢 - 如果要记录请求体或 header,得提前读取
r.Body,但注意:body 只能读一次,后续 handler 就收不到数据了;需要io.TeeReader或重放机制 -
zap的Logger实例不能全局共用(除非你确定所有 goroutine 都不会并发修改它),建议通过context.WithValue注入,或用依赖注入容器管理
为什么有些项目日志里看不到用户 IP 或 trace ID?
因为这些字段不在 Go 标准 http.Request 自动解析范围内,必须手动提取:
- 真实 IP 要从
X-Forwarded-For或X-Real-IPheader 读,但不可信——需结合RemoteAddr和反向代理配置校验 - trace ID 通常由上游网关注入(如
X-Request-ID),中间件里直接取:r.Header.Get("X-Request-ID");若不存在,可生成一个并写回响应头 - gin/echo 等框架的 “日志中间件” 默认不开启 trace ID 支持,得自己加字段,不然日志无法关联分布式调用链
日志中间件真正的复杂点不在写法,而在于字段可信度、上下文生命周期、以及和 tracing / metrics 系统的对齐——少一个 context.Context 透传,排查时就少一条线索。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











