如何在Golang中实现请求日志分级_使用自定义中间件记录信息

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

在 Go 中实现请求日志分级,核心是用自定义 HTTP 中间件拦截请求,在不同阶段(如进入、处理完成、出错时)写入不同级别(debug/info/warn/error)的日志,同时结合结构化日志库(如 log/slogzerolog)提升可读性和过滤能力。

按请求生命周期注入日志级别

中间件应在请求开始时记录 info 级别(如路径、方法、客户端 IP),在响应写出后记录 infowarn(如耗时 >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,仅记录关键指标:

  • 用环境变量(如 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 恢复机制,否则日志还没写完服务就挂了。