当前位置:首页 > 文章列表 > Golang > Go教程 > Go log/slog 如何把请求字段加入结构化日志

Go log/slog 如何把请求字段加入结构化日志

来源:17golang原创 2026-09-12 20:37:37 0浏览 收藏

如果每条 Go 请求日志都要补上 trace_idrequest_id 和租户编号,最稳妥的做法不是在每次 Info 调用时手写一遍,而是在请求入口创建一个带公共属性的 slog.Logger,随后沿着 context.Context 传递。这样既能保持 JSON 字段统一,也不会把业务函数改成“先拼日志字符串再输出”。

要点速览
  • Logger.With 适合固化一条请求链路都会出现的字段。
  • 有上下文时优先使用 InfoContextErrorContextLogAttrs
  • trace 字段可以检索,但密码、Cookie 和完整令牌不能直接写入日志。

先把日志分成请求字段和事件字段

log/slog 的一条记录由消息、级别和键值属性组成。请求字段描述“这条链路是谁”,例如 trace_idrequest_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)
}
Go log slash slog 请求入口把 trace_id 和 request_id 绑定到 logger 并通过 context 传入业务处理器的结构示意图
图1:请求级字段绑定到 slog.Logger,再沿 context 进入业务处理器的操作示意图。

为什么要用 With,而不是每行重复传参

With 会返回一个使用同一 Handler、但已经附带公共属性的新 Logger。后续日志调用都会带上这些属性,代码只需要在事件位置添加订单号、结果或耗时。它还比每行重复传参更容易统一字段名,避免一处写 requestId、另一处写 request_id

如果一个子系统还会使用通用键名,可以再调用 WithGroup,例如把支付模块的 id 放到 payment.id 或 JSON 的 payment 对象下,减少跨模块键名冲突。

有 context 时优先用 Context 方法

InfoContextErrorContextLogAttrs 接受调用点的上下文,便于自定义 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
}
Go log slash slog JSONHandler 输出 trace_id request_id http_path 和 order_id 的结构化日志结果示意图
图2:同一请求的公共字段与业务事件字段出现在一条可检索的 JSON 日志中的结果示意图。

字段命名和敏感数据要先定规则

建议在团队内固定一份字段表:trace_id 用于跨服务串联,request_id 用于单次入口定位,http_methodhttp_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 也足够清楚。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
赤陶色沙丘月影手机壁纸怎么做出极简构图赤陶色沙丘月影手机壁纸怎么做出极简构图
上一篇
赤陶色沙丘月影手机壁纸怎么做出极简构图
MySQL 联合索引中间列范围条件为什么影响后列
下一篇
MySQL 联合索引中间列范围条件为什么影响后列
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    107次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    22次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    34次使用
  • AGI-Eval大模型评测平台:权威榜单、数据集与人机协同评测方案
    AGI-Eval
    AGI-Eval是由上海交大等高校联合发布的大模型评测社区,提供公正透明的LLM能力榜单、多领域评测集及Data Studio数据服务,助力AI模型性能评估与NLP科研开发。
    23次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    260次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码