当前位置:首页 > 文章列表 > Golang > Go教程 > Go slog 怎样把相关字段归到同一个 group

Go slog 怎样把相关字段归到同一个 group

来源:17golang原创 2026-11-15 20:29:31 0浏览 收藏

Go slog 把相关字段归入同一个 group,有两种常用写法:slog.Group("request", ...) 构造一组属性,适合只包住当前这几个字段;logger.WithGroup("request") 返回带组作用域的派生日志器,适合连续多条日志共用同一组。后一种写法会影响之后新增的属性,不能把它当作只对下一对键值生效的标记。

以商品目录服务为例,服务标识希望留在日志根层,请求编号和路由属于 request,数据库名称属于 db。先决定这些归属,再选API,能避免把本应平级的字段意外嵌到另一个组里。以下为原创示例与结构推导,没有把示例描述为本机运行结果。

官方文档:https://pkg.go.dev/log/slog

先给请求日志划分字段归属

假设服务里到处都写 id、name 和 status,仅靠这些短字段名,查询时很难分清请求编号与其他业务编号。group 的作用是给相关属性一个稳定的归属:根层放 service,request 组放 id 与 route,db 组放 name。status 放根层还是请求组,都可以由应用约定,但同一种事件应保持一致。

标准库的 JSONHandler 把组表达为嵌套对象,TextHandler 用带点号的名称表现分组。自定义 Handler 的具体表示由它实现;不要仅凭某个采集平台展平后的显示形式,判断原始日志没有组。

本文使用 Go 1.21 已提供的 log/slog 基础接口,不依赖较新版本的 GroupAttrs。已有项目无需为了这两个分组操作额外引入日志框架。

用Group只包住一组属性

slog.Group 返回一个 Attr,它包含组名和组内属性。将它传给一次日志调用时,只有传入 Group 的那些字段属于该组;调用中紧随其后的其他属性不会自动进入它。

下例包含三条彼此对照的日志。保存为 main.go 后可以在具备相应Go工具链的项目中运行;代码只演示字段组织,不发起HTTP请求或连接数据库。

package main

import (
    "log/slog"
    "os"
)

func main() {
    // 根日志器保存跨请求不变的服务标识,JSONHandler 展示真实层级。
    base := slog.New(slog.NewJSONHandler(os.Stdout, nil)).
        With(slog.String("service", "catalog"))

    // Group 只包住这一组字段,后面的 status 仍处于根层。
    base.Info("page ready",
        slog.Group("request",
            slog.String("id", "r-demo"),
            slog.String("route", "/items")),
        slog.Int("status", 200))

    // WithGroup 返回派生日志器;后续 With 和日志属性都进入 request。
    requestLog := base.WithGroup("request").
        With(slog.String("id", "r-demo"))
    requestLog.Info("page ready",
        slog.String("route", "/items"),
        slog.Int("status", 200))

    // 希望 request 与 db 平级时,从 base 发出日志并明确列出两组。
    base.Info("lookup complete",
        slog.Group("request", slog.String("id", "r-demo")),
        slog.Group("db", slog.String("name", "inventory")),
        slog.Int("status", 200))
}

第一条日志的业务属性结构是:根层 service 与 status,request 下是 id、route。time、level、msg 等内置字段仍由 Handler 处理。下方只画业务属性关系,不把示意图当成输出截图。

service和status处于根层而request组包含id与route的静态关系
图1:Group 属性结构说明图。request 与根层字段同属一条日志;连线表示包含关系,不表示调用先后或实际运行结果。

可以把一次调用理解为“一个组属性加若干普通属性”。这样阅读长参数列表时,就不会误以为组名会一直延续到日志调用结束。

用WithGroup复用同一作用域

第二条日志先取得 requestLog。这个日志器已经进入 request 组,之后用 With 固定的 id,以及 Info 新传入的 route、status,都处在 request 下。之前绑定到 base 的 service 仍在根层。这里的 status 与第一条位置不同,是为了直接展示两种写法的差别,实际业务应选择并坚持一种结构。

WithGroup 的返回值要接住。原来的 base 不会因为生成了 requestLog 就被改成请求日志器;要记服务级事件,仍可以继续使用 base。连续进入另一个非空组会继续嵌套,例如 requestLog.WithGroup("db") 的后续属性属于 request.db,而不是根层的 db。

派生日志器适合传给只负责某一块业务的函数,函数可以补充同组字段,无需每次重写组名。但若传进去的日志器带了哪些作用域不清楚,调用方很容易再包一层同名组。可以在函数参数约定中说明它接收根日志器还是请求日志器。

调用顺序决定已有字段在哪一层

迁移旧日志时,最容易忽略的是 With 与 WithGroup 的顺序。WithGroup 不会把之前已经固定的全部属性搬到新组;后续新增属性才受到新的组作用域约束。下面是接在完整示例 base 后面的独立对照片段,不是另一个可单独运行的 main 文件。

// 已有固定字段在进入组之前绑定,所以 service 保持在根层。
outer := base.With(slog.String("region", "east")).WithGroup("request")
// id 属于 request;region 仍在根层。
outer.Info("accepted", slog.String("id", "r-demo"))

// 相反,先进入组再 With,region 也属于 request。
inner := base.WithGroup("request").With(slog.String("region", "east"))
inner.Info("accepted", slog.String("id", "r-demo"))

outer 对应根层 service、region 与 request.id;inner 对应根层 service 与 request.region、request.id。这是 API 作用域的区别,不是属性排序风格的区别。调整方法链时,应先看字段路径是否改变,再考虑代码看起来是否简短。

base与requestLog派生日志器的固定字段和新增字段作用域
图2:日志器作用域说明图。base 保留服务标识,requestLog 关联请求字段;边界表达字段归属,没有时间轴或运行输出。

如果期望 request 和 db 平级,完整例子的第三条写法更直观:从 base 发出一次事件,分别传入两个 Group。不要先进入 request,再期待一个 db Group 自动跳回根层。

按任务选择分组并避免重复键

整理已有日志时,可以先列字段归属,再决定哪些值值得固定到派生日志器。跨很多事件不变的 service 放到 base.With;多个连续事件都属于同一作用域时再派生 WithGroup;只出现一次的局部对象则用 Group。最后查看设计中的字段路径,确认它与查询端预期一致。

group 不是同名字段的自动去重器。不要在同一条事件的同一作用域反复写 id,或者先把 request.id 固定到日志器,再在日志调用里又写同名 id 并期待它一定覆盖。采集端对重复键的处理可能不同,应该从写入端避免含糊结构。

也不要用随机请求编号作组名,如按请求生成不同的组名称。把组名固定为 request,把编号放进 id 值,字段结构才便于持续查询。用户输入适合作为经过必要处理的值,不适合随意扩展日志字段名称。

日志分组本身不承诺性能提升。With 允许 Handler 处理复用属性,但分组数量、值类型与采集链路都会影响成本;本文只给字段组织方案,不给未经测量的速度比较。

分组字段速查与常见问题

任务写法注意点
一条事件里包住几项字段slog.Group("request", ...)不改变同一调用中其他普通属性的归属
后续事件都补充同一组字段base.WithGroup("request")接住返回值,之后的With及日志属性受组约束
服务字段留根层先With(service)再WithGroup先固定的字段不自动搬入后来的组
两个组平级在base同一次调用中传两个Group不要在已进入的作用域中误加一层
退出当前组改用保存的base或较外层日志器WithGroup("")不会退出,它返回接收者

组名为空会怎样? WithGroup("") 不会建立新层,也不会弹出已有层。Group 的空键表示将组内属性内联;这是另一种语义,不应拿空字符串模拟退组。

空组会输出一个空对象吗?按 Handler 的约定,没有属性的组会被忽略。不要依靠一个空Group作为“该阶段已执行”的业务信号;需要状态时应明确记录一个有意义的属性或事件。

为什么TextHandler里看到request.id?这是文本Handler表示组字段的方式。JSONHandler会使用对象层级。若应用把键本身写成带点字符串,不能假定它与真正的嵌套组在所有输出和查询端都等价。

所有字段都应该分组吗?没有必要。优先给有稳定语义的对象分组,避免多层嵌套只是为了缩短字段名。让根字段、request和db的职责能被读者直接解释,通常比层数更多更有用。

Go项目结构化日志介绍:https://go.dev/blog/slog

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go fs.FileInfo.Mode 怎样识别目录和符号链接Go fs.FileInfo.Mode 怎样识别目录和符号链接
上一篇
Go fs.FileInfo.Mode 怎样识别目录和符号链接
Go net/http ServeContent 处理 Range 请求与缓存标头
下一篇
Go net/http ServeContent 处理 Range 请求与缓存标头
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1043次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1040次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1187次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1143次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    987次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码