Go中实现请求日志分级需用自定义HTTP中间件拦截请求,在不同生命周期阶段写入debug/info/warn/error日志,结合slog等结构化日志库,按环境动态调整粒度,并集成trace ID与panic恢复机制。

在 Go 中实现请求日志分级,核心是用自定义 HTTP 中间件拦截请求,在不同阶段(如进入、处理完成、出错时)写入不同级别(debug/info/warn/error)的日志,同时结合结构化日志库(如 log/slog 或 zerolog)提升可读性和过滤能力。
按请求生命周期注入日志级别
中间件应在请求开始时记录 info 级别(如路径、方法、客户端 IP),在响应写出后记录 info 或 warn(如耗时 >500ms),发生 panic 或 handler 返回错误时记录 error;调试用的详细参数、Header、Body 则只在 debug 级别输出(需开关控制)。
- 用
context.WithValue传递 request ID 和日志实例,确保子 goroutine 可继承 - 避免在中间件中直接读取
r.Body(会消耗流),如需记录 body,先用io.TeeReader复制到 buffer - 对敏感字段(如 Authorization、password)做脱敏,统一替换为
[REDACTED]
使用 slog 实现轻量级分级输出
log/slog(Go 1.21+ 内置)天然支持层级与属性,适合构建请求日志中间件:
- 初始化 logger 时绑定公共属性:
slog.With("service", "api") - 在中间件中按需调用:
log.InfoContext(ctx, "request started", "method", r.Method, "path", r.URL.Path) - 错误场景用
log.ErrorContext,并附加err属性和堆栈(slog.String("stack", debug.Stack())) - 通过
slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})控制全局最低输出级别
区分环境动态调整日志粒度
开发环境默认启用 debug,记录完整 header 和 query;生产环境设为 info,仅记录关键指标:
立即学习“go语言免费学习笔记(深入)”;
- 用环境变量(如
ENV=prod)或配置文件控制slog.LevelVar,运行时热更新级别 - 对高频接口(如健康检查
/healthz)跳过debug日志,避免 IO 拖慢 - 用
middleware.Skipper函数白名单过滤不需要日志的路由(如静态资源)
集成 trace ID 与上下文透传
为排查链路问题,中间件应自动注入 trace ID(如从 X-Request-ID 或生成新 UUID),并让所有子日志自动携带:
- 解析或生成 trace ID 后存入 context:
ctx = context.WithValue(ctx, keyTraceID, traceID) - 用
slog.WithGroup("http")将请求相关日志归组,再用slog.String("trace_id", traceID)统一附加 - 若对接 OpenTelemetry,可用
otel.GetTextMapPropagator().Inject()向响应头写入 trace 上下文
基本上就这些。不复杂但容易忽略的是:日志级别必须和实际业务语义对齐,比如“用户登录失败”是 error,“缓存未命中”只是 info;中间件本身也要有 panic 恢复机制,否则日志还没写完服务就挂了。










