Go log/slog 如何把请求字段加入结构化日志
如果每条 Go 请求日志都要补上 trace_id、request_id 和租户编号,最稳妥的做法不是在每次 Info 调用时手写一遍,而是在请求入口创建一个带公共属性的 slog.Logger,随后沿着 context.Context 传递。这样既能保持 JSON 字段统一,也不会把业务函数改成“先拼日志字符串再输出”。
Logger.With适合固化一条请求链路都会出现的字段。- 有上下文时优先使用
InfoContext、ErrorContext或LogAttrs。 - trace 字段可以检索,但密码、Cookie 和完整令牌不能直接写入日志。
先把日志分成请求字段和事件字段
log/slog 的一条记录由消息、级别和键值属性组成。请求字段描述“这条链路是谁”,例如 trace_id、request_id、HTTP 方法和路径;事件字段描述“此刻发生了什么”,例如订单号、耗时或错误。前者应该在请求入口绑定,后者在具体业务点追加。
下面的初始化只负责输出 JSON。示例中的代码和日志都是文章操作示意,字段值请替换成你的网关或链路追踪系统提供的真实值。
package main
import (
"context"
"log/slog"
"net/http"
"os"
)
type loggerKey struct{}
func main() {
// JSONHandler 让采集系统按字段解析日志,而不是依赖字符串切割。
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
})
slog.SetDefault(slog.New(handler))
http.HandleFunc("/orders", requestLogger(orderHandler))
_ = http.ListenAndServe(":8080", nil) // 示例服务;生产环境要处理返回错误。
}
func requestLogger(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 实际项目应优先读取可信网关注入的 trace_id,缺失时再生成 request_id。
traceID := r.Header.Get("X-Trace-ID")
if traceID == "" {
traceID = "trace-demo"
}
requestID := r.Header.Get("X-Request-ID")
if requestID == "" {
requestID = "request-demo"
}
reqLog := slog.Default().With(
"trace_id", traceID,
"request_id", requestID,
"http_method", r.Method,
"http_path", r.URL.Path,
)
ctx := context.WithValue(r.Context(), loggerKey{}, reqLog)
next(w, r.WithContext(ctx))
}
}
func orderHandler(w http.ResponseWriter, r *http.Request) {
logger := r.Context().Value(loggerKey{}).(*slog.Logger)
// 事件字段只在真正处理订单的地方追加,避免污染所有请求日志。
logger.InfoContext(r.Context(), "order accepted", "order_id", "ord-1001")
w.WriteHeader(http.StatusAccepted)
}

为什么要用 With,而不是每行重复传参
With 会返回一个使用同一 Handler、但已经附带公共属性的新 Logger。后续日志调用都会带上这些属性,代码只需要在事件位置添加订单号、结果或耗时。它还比每行重复传参更容易统一字段名,避免一处写 requestId、另一处写 request_id。
如果一个子系统还会使用通用键名,可以再调用 WithGroup,例如把支付模块的 id 放到 payment.id 或 JSON 的 payment 对象下,减少跨模块键名冲突。
有 context 时优先用 Context 方法
InfoContext、ErrorContext 和 LogAttrs 接受调用点的上下文,便于自定义 Handler 或链路系统读取当前 span。若你已经把 Logger 放进 context,取出后仍要把同一个请求上下文传给日志方法;不要为了“带字段”而创建一个永远不会取消的新 context。
func loadOrder(ctx context.Context, orderID string) error {
logger, ok := ctx.Value(loggerKey{}).(*slog.Logger)
if !ok {
logger = slog.Default() // 没有请求包装时仍保持可用的兜底行为。
}
// Attr 适合固定类型,也能避免把参数顺序写错。
logger.LogAttrs(ctx, slog.LevelInfo, "loading order",
slog.String("order_id", orderID),
slog.String("component", "order-service"),
)
return nil
}

字段命名和敏感数据要先定规则
建议在团队内固定一份字段表:trace_id 用于跨服务串联,request_id 用于单次入口定位,http_method 与 http_path 用于筛选,order_id 等业务字段只在相关事件中出现。不要把完整 Authorization、Cookie、密码或身份证号放进公共 Logger;需要记录时先脱敏或只保留不可逆摘要。
| 字段类型 | 绑定位置 | 示例 |
|---|---|---|
| 请求公共字段 | 中间件 | trace_id、request_id |
| 路由字段 | 请求入口 | http_method、http_path |
| 业务事件字段 | 具体函数 | order_id、result、latency_ms |
常见问题
直接调用 slog.Info 会丢掉请求字段吗?
如果没有把公共 Logger 设为默认,直接调用顶层函数不会自动获得请求级字段。业务代码应显式使用请求 Logger,或在请求入口调用 slog.SetDefault 之外采用清晰的依赖传递方案。
为什么日志里有重复的 trace_id?
通常是同一个键既通过 With 绑定,又在某次事件调用中再次传入。统一约定公共字段只在入口绑定,事件字段只在事件点追加。
什么时候用 LogAttrs?
字段类型明确、调用频繁或需要减少交替键值参数时可以用它。普通低频日志用 InfoContext 也足够清楚。
赤陶色沙丘月影手机壁纸怎么做出极简构图
- 上一篇
- 赤陶色沙丘月影手机壁纸怎么做出极简构图
- 下一篇
- MySQL 联合索引中间列范围条件为什么影响后列
-
- Golang · Go教程 | 26分钟前 |
- Go log.Logger 输出到多路目标时如何保持格式一致
- 154浏览 收藏
-
- Golang · Go教程 | 38分钟前 |
- Go slog.HandlerOptions 如何给不同 logger 设置独立级别
- 187浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go database/sql NullString 如何区分空字符串和 NULL
- 260浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · database/sql · PreparedStmt · Go 预处理语句 database/sql sql.Stmt
- Go database/sql 如何复用预处理语句并及时关闭
- 106浏览 收藏
-
- Golang · Go教程 | 1小时前 | go · 安全开发 · 令牌 · crypto/rand 随机令牌 随机字节
- Go crypto/rand 如何生成不可预测的随机字节
- 371浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go crypto/sha256 如何对大文件分块计算摘要
- 168浏览 收藏
-
- Golang · Go教程 | 2小时前 | 文件处理 · go · 归档 · archive/tar tar.Reader Go解压
- Go tar.Reader 如何跳过目录条目后解压文件
- 189浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go archive/zip 如何只读取压缩包中央目录信息
- 231浏览 收藏
-
- Golang · Go教程 | 2小时前 | HTTP · gzip · Go教程 · HTTP响应 compress/gzip 流式压缩
- Go compress/gzip 如何流式压缩 HTTP 响应
- 373浏览 收藏
-
- Golang · Go教程 | 2小时前 | Go教程 · html/template · text/template · HTML转义 · 模板安全 · Go html/template xss text/template 模板转义
- Go html/template 如何区分安全模板与纯文本模板
- 427浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 107次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 22次使用
-
- OpenCompass
- OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
- 34次使用
-
- AGI-Eval
- AGI-Eval是由上海交大等高校联合发布的大模型评测社区,提供公正透明的LLM能力榜单、多领域评测集及Data Studio数据服务,助力AI模型性能评估与NLP科研开发。
- 23次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 260次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- golang xorm 自定义日志记录器之使用zap实现日志输出、切割日志(最新)
- 2023-02-24 432浏览
-
- Go语言常用的打log方式详解
- 2023-02-24 485浏览
-
- Go常用技能日志log包创建使用示例
- 2023-01-28 105浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览

