Go slog 怎样让字段按模块分组
Go slog 要让字段按模块分组,我会先区分两件事:一条日志中的局部对象用 slog.Group,一个模块持续输出的字段用 Logger.WithGroup 派生子 Logger。服务名、任务标识等公共字段先用 With 绑定,再进入模块组;这样既有模块命名空间,也能保留清楚的公共入口。
我更在意的是查询者能否判断字段归属,而不是日志看起来有没有层次。下面以一个虚构的商品目录导入任务为例讨论我的设计取舍,代码与数值都是教学示例,不是线上事故复盘或性能实测。log/slog 自 Go 1.21 进入标准库,背景见 https://go.dev/blog/slog。
官方文档:https://pkg.go.dev/log/slog
我会先决定日志要让谁读
同一个 rows,可能表示输入文件行数,也可能表示实际写入行数。如果全部塞进根部,再靠消息文字猜语义,字段名就容易失去稳定含义。我会把“跨模块关联”和“模块内部解释”分开:service、task_id 在根部,导入模块的 mode、rows 放进 import,来源描述放在它的 source 内。
这是本例的字段约定,不是 slog 强制的业务模型。若团队已经用固定的顶层 module 标签做筛选,也可以保留该标签,再决定是否需要嵌套对象。我的判断标准是字段消费者,而不是目录结构有几层。
单条记录的局部字段交给Group
slog.Group 创建一个分组属性,只包含传给它的字段。以下片段假定已有完整示例中的 task;decision 是 input 的同级属性,不会因为写在它后面就自动进入组。
// 接在已经定义的 task Logger 后:只把 rows 和 format 包进 input。
task.Info("file described",
slog.Group("input",
slog.Int("rows", 24),
slog.String("format", "csv"),
),
slog.String("decision", "preview"),
)
在内置 JSONHandler 中,input 对应嵌套对象;TextHandler 使用带点的限定键名。第三方 Handler 可以有自己的表现方式,不能把文本外观直接当成所有后端的存储协议。官方分组说明:https://pkg.go.dev/log/slog#hdr-Groups。
对我来说,这种局部分组适合文件摘要、请求描述等成套字段:只在需要时出现,不把整个模块的 Logger 改成“当前文件对象”。

模块Logger怎样保留公共根字段
package main
import (
"log/slog"
"os"
)
// runImport 接收模块专用 Logger,不修改全局默认 Logger。
func runImport(logger *slog.Logger) {
logger.Info("batch accepted",
slog.Int("rows", 24),
slog.Group("source",
slog.String("kind", "csv"),
slog.String("name", "sample.csv"),
),
)
}
func main() {
// 使用标准输出,进程不负责关闭 os.Stdout。
root := slog.New(slog.NewJSONHandler(os.Stdout, nil))
root = root.With(slog.String("service", "catalog-demo"))
// 公共任务字段先绑定,保留在根部;后加的字段进入 import 组。
task := root.With(slog.String("task_id", "demo-42"))
importLogger := task.WithGroup("import").
With(slog.String("mode", "preview"))
runImport(importLogger)
// task 未被 WithGroup 改写,因此这里的 status 仍在根部。
task.Info("task finished", slog.String("status", "previewed"))
}
按代码结构,第一条记录的公共路径是 service、task_id,模块路径是 import.mode、import.rows、import.source.kind 与 import.source.name。这里的点号是本文描述对象路径的记法,不是在 JSON 中手写带点的键。时间、级别、消息由内置 Handler 处理。
WithGroup 返回派生 Logger;原来的 task 仍可用于任务级事件,因此结尾的 status 不进入 import。官方实现中的派生关系可见 https://go.dev/src/log/slog/logger.go。

我不把所有层级都塞进组名
我最想提醒的是绑定位置。With 在进入组之前还是之后调用,会影响新属性的归属;只看最终变量名,很难看出差别。
// 片段中的 root 来自完整示例;两种绑定位置并不一样。
a := root.With("task_id", "demo-42").WithGroup("import")
b := root.WithGroup("import").With("task_id", "demo-42")
a.Info("position A", "rows", 24)
b.Info("position B", "rows", 24)
// 从 task 派生两个并列模块,不从 importLogger 派生兄弟模块。
parseLogger := task.WithGroup("parse")
storeLogger := task.WithGroup("store")
parseLogger.Info("input checked", "valid", true)
storeLogger.Info("write skipped", "reason", "preview")
在示例A中,task_id 是进入模块组之前绑定的公共字段;示例B中它在 import 内。后面的 parseLogger 与 storeLogger 从共同的 task 派生,属于并列模块。如果从 importLogger 继续 WithGroup("store"),得到的是更深的嵌套,而不是切回根部再建立兄弟组。
这也是我不按每个 Go 包名机械叠组的原因:检索者需要记住的是业务位置,不是源码目录。模块层、局部对象层通常已经能够表达本例;更多层级是否值得引入,应由字段含义决定。
让查询、阅读和开销一起可控
我会在接入日志平台之前,先写一张小型字段约定。它不依赖颜色或截图解释,复制成文本也能知道每个字段的含义;同一种计数固定名称、固定类型,避免今天是数字、明天是带单位字符串。
| 本例字段 | 归属 | 设计目的 |
|---|---|---|
| service、task_id | 根部 | 跨模块关联 |
| import.mode、import.rows | 模块 | 解释导入任务 |
| import.source.kind、name | 局部来源对象 | 描述输入来源 |
| status | 任务结束记录的根部 | 任务级结果 |
我会在组件构建时准备长期复用的模块 Logger,在任务开始时绑定该任务的标识,不把每条日志都重新构造一遍。官方博客介绍了 With 和 Handler 为公共属性提供优化空间;但能省多少开销取决于 Handler 与负载,这里没有跑基准,不能据此给出加速比例。
另一个容易忽略的地方是参数求值:组并不会使昂贵的字段计算自动变懒。需要时可考虑级别判断或合适的延迟表示,但不要为了让结构漂亮而在每条低级别日志里构造大对象。官方性能讨论可参考 https://pkg.go.dev/log/slog#hdr-Performance_considerations。
哪些边界值得单独留意
空组名是不是表示退出当前组?
不是。WithGroup("") 不建立新组,也不是“返回父级”。需要根级或兄弟模块日志时,保留相应的父 Logger 变量。
分组以后就不会泄露敏感值吗?
不会。分组只是结构设计,不是脱敏或访问控制。我的示例只使用合成标识和示例文件名;业务中仍应单独设计允许记录的字段,避免输出凭据、完整个人数据或不必要的输入内容。
能不能只把键写成import.rows?
这不等同于创建组,尤其当输出是JSON时。为了表达结构,我会用实际分组属性或模块子Logger,而不是依赖某个平台替我解释键中的点号。
什么时候不值得分组?
如果一个小工具只输出少量固定字段,消费者又偏好平面记录,强行叠组未必有收益。我的取舍是:先稳定公共字段和查询习惯,再引入有明确边界的模块组;不是日志越深就越专业。
这组写法适合字段可能重名、模块需要独立扩展的服务。单条对象用Group,长期模块用WithGroup,公共字段先绑定,并保留父Logger用于任务级事件——这是我在这个示例中愿意保留的最小结构。
Go slog 怎样补充请求上下文字段
- 上一篇
- Go slog 怎样补充请求上下文字段
- 下一篇
- Go context 怎样传递可读取消原因
-
- Golang · Go教程 | 36分钟前 |
- Go reflect.Value.SetZero 怎样清空可变值
- 388浏览 收藏
-
- Golang · Go教程 | 39分钟前 |
- Go http.CrossOriginProtection 怎样给写接口加跨源请求保护
- 464浏览 收藏
-
- Golang · Go教程 | 42分钟前 |
- Go x509.CreateCertificate 怎样生成带 SAN 的本地测试证书
- 233浏览 收藏
-
- Golang · Go教程 | 50分钟前 |
- Go regexp.Regexp NumSubexp 怎样计算捕获组数量
- 269浏览 收藏
-
- Golang · Go教程 | 53分钟前 | 标准库 · go · Go HTTP客户端 连接复用 httptrace GotConnInfo
- Go httptrace.GotConnInfo 怎样判断连接是否复用
- 313浏览 收藏
-
- Golang · Go教程 | 54分钟前 |
- Go context 怎样复制值但不继承取消信号
- 275浏览 收藏
-
- Golang · Go教程 | 1小时前 |
- Go fmt.Formatter 怎样实现自定义格式化
- 388浏览 收藏
-
- Golang · Go教程 | 1小时前 | cookie Go Authorization httputil.DumpRequestOut HTTP调试 请求头脱敏
- Go httputil.DumpRequestOut 怎样调试请求头而不泄露密钥
- 233浏览 收藏
-
- Golang · Go教程 | 1小时前 | rss Go GC debug.FreeOSMemory HeapReleased
- Go debug.FreeOSMemory 怎样判断释放是否真的有帮助
- 173浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 582次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 620次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 652次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 616次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 433次使用
-
- Java 性能优化上线清单:从定位、改造到灰度发布
- 2026-06-11 860浏览
-
- Spring Boot 压测验证:Gatling、JMeter 与性能回归门禁
- 2026-06-11 843浏览
-
- Java NMT 非堆内存排查:Direct Buffer、线程栈与 Metaspace 分析
- 2026-06-11 826浏览
-
- Spring Boot 容器内存优化:JVM 堆、非堆与 MaxRAMPercentage
- 2026-06-11 809浏览
-
- Tomcat 连接与线程参数调优:maxThreads、acceptCount 与 KeepAlive
- 2026-06-11 792浏览

