Go HTTP日志中间件需用slog.NewJSONHandler输出结构化JSON,传*os.File、复用logger、动态注入字段(如request_method、status_code),敏感信息脱敏,必带trace_id以保障可检索与链路对齐。

Go HTTP 日志中间件的输出格式不能靠“美化字符串”来凑,关键在于:用对日志器类型、选对序列化方式、在正确位置注入字段。slog 是当前最轻量且可控的选择,标准 log 包仅适合极简场景。
用 slog.NewJSONHandler 输出结构化 JSON 格式
结构化日志不是“把字符串拼成 JSON”,而是从源头用键值对组织数据。slog 的 NewJSONHandler 会自动把 slog.Info("msg", "key", value) 中的 key/value 转为 JSON 字段。
- 必须传
*os.File或其他io.Writer,不能直接传os.Stdout(它不是接口实现体,Go 1.22+ 会报错) -
slog.HandlerOptions{AddSource: true}可加文件行号,但会轻微拖慢性能,开发环境开、生产关 - 避免在 handler 内部用
slog.With(...).Info()频繁新建 logger 实例——它不 cheap,应复用带 context 的 logger - 示例初始化:
logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo}))
自定义文本格式需绕过 slog 直接实现 io.Writer
如果非得输出类似 [INFO] 2024/03/15 10:22:33 GET /api/user 200 12ms 这种人类可读格式,slog 不支持自由排版,得自己写 io.Writer。
- 实现
Write([]byte)方法,在里面解析原始日志行(如 Gin 默认格式)、重排字段、补上 trace_id - 别在
Write里做阻塞操作(如写网络、调远程 API),否则整个 HTTP 请求会被卡住 - 不要试图用正则从
slog.TextHandler输出中提取字段再重写——它已格式化完毕,无法还原 level 或 context 值 - 若坚持用标准
log包,只能靠log.SetFlags控制前缀(时间/文件),消息体仍需手动拼接,且无级别过滤能力
HTTP 中间件里记录请求日志时,字段必须动态注入
静态格式模板(如固定字符串)无法满足真实需求。路径、状态码、耗时、IP 等都得在请求生命周期内实时捕获。
- 状态码必须从包装过的
http.ResponseWriter读,原生接口不暴露该字段 - 耗时要用
time.Since(start)计算,别信time.Now().Sub()在高并发下可能因调度偏差失准 - 敏感字段(如
Authorizationheader、token=query)必须脱敏,不能只靠“不打印”——debug 日志一旦开启,它们就进日志流了 - 推荐字段命名统一用小写下划线:
request_method、status_code、response_time_ms,方便后续被 Loki/ELK 自动索引
最容易被忽略的是:格式只是表象,真正决定日志可用性的,是字段是否可检索、是否带上下文、是否与 tracing ID 对齐。一个没 trace_id 的 JSON 日志,和一段乱码没区别。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











