0Pricing
Go Academy · 课时

日志记录与请求 ID 中间件

结构化日志记录与注入跟踪 ID

日志记录与请求 ID 中间件 是 CoddyKit 上的免费 Go Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。

为什么需要结构化日志?

纯文本日志很难在大规模环境中解析。结构化日志(JSON 或 key=value)允许日志聚合器(Datadog、Loki、ELK)按字段筛选日志条目并建立关联。

生成请求 ID

在中间件中为每个请求生成唯一 ID(UUID 或 ULID),并将其附加到响应标头和上下文:

func RequestID(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        id := r.Header.Get("X-Request-ID")
        if id == "" { id = uuid.New().String() }
        w.Header().Set("X-Request-ID", id)
        ctx := context.WithValue(r.Context(), reqIDKey{}, id)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

使用 slog 的日志中间件

Go 1.21 新增了用于结构化日志记录的 log/slog。在中间件中使用它:

func StructuredLogger(logger *slog.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()
            cw := &captureWriter{ResponseWriter: w}
            next.ServeHTTP(cw, r)
            logger.Info("request",
                "method", r.Method,
                "path", r.URL.Path,
                "status", cw.status,
                "latency_ms", time.Since(start).Milliseconds(),
                "request_id", r.Context().Value(reqIDKey{}),
            )
        })
    }
}

captureWriter

包装 ResponseWriter,以捕获处理程序写入的状态码:

type captureWriter struct {
    http.ResponseWriter
    status int
}
func (cw *captureWriter) WriteHeader(code int) {
    cw.status = code
    cw.ResponseWriter.WriteHeader(code)
}
func (cw *captureWriter) Status() int {
    if cw.status == 0 { return http.StatusOK }
    return cw.status
}

将请求 ID 添加到每一条日志

为每个请求构建一个预先填充 ID 的子日志记录器,然后将其存储到上下文中:

reqLogger := logger.With("request_id", id)
ctx := context.WithValue(r.Context(), loggerKey{}, reqLogger)

从上下文中获取日志记录器

提供辅助函数,以便从任意处理程序或服务中获取请求作用域内的日志记录器:

func LoggerFromContext(ctx context.Context) *slog.Logger {
    l, ok := ctx.Value(loggerKey{}).(*slog.Logger)
    if !ok { return slog.Default() }
    return l
}

日志级别

使用适当的级别:Debug 用于开发期间的详细日志,Info 用于请求摘要,Warn 用于可恢复的异常,Error 用于需要处理的失败。不要记录敏感字段(令牌、密码)。

延迟分桶

在每一条日志中包含请求延迟,并在指标系统(Prometheus)中聚合为直方图分桶,以跟踪 P50、P95 和 P99 百分位数。

微服务中的关联 ID

将传入请求中的 X-Request-ID 标头转发到所有传出的 HTTP 和 gRPC 调用,以便在不引入完整跟踪系统的情况下实现分布式跟踪。

日志采样

对于流量较高的服务,可以对调试级别和信息级别的日志进行采样(例如,每 100 个请求只记录 1 个调试级别日志),在保留完整错误日志的同时降低存储成本。

zap 与 slog 的比较

对于高吞吐量服务,zap(Uber)比 slog 更快,但需要进行更多配置。slog 属于标准库,具有良好的可移植性,足以满足大多数服务的需求。除非基准测试显示存在瓶颈,否则请优先使用 slog。

快速检查

为什么日志记录中间件需要 captureWriter?

回顾:日志记录与请求 ID

要点:

  • 在中间件中生成或转发 X-Request-ID,并将其存储在上下文中
  • 使用 slog 结合键值字段记录结构化日志
  • captureWriter 拦截状态码,以便在处理程序执行后记录日志
  • 将关联 ID 转发到所有下游调用

常见问题解答

「日志记录与请求 ID 中间件」课时是免费的吗?

是的 — 「日志记录与请求 ID 中间件」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「日志记录与请求 ID 中间件」这节课中我会学到什么?

结构化日志记录与注入跟踪 ID 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「日志记录与请求 ID 中间件」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. HTTP 中间件链基础
  2. 日志记录与请求 ID 中间件
  3. 身份验证与速率限制中间件
  4. CORS 与 panic 恢复中间件
← 返回 Go Academy