You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于zerolog抽象上下文Request ID日志逻辑?

在zerolog中统一注入Request ID的两种实践方案

方案一:扩展zerolog.Event实现RequestId(ctx)链式调用

通过给zerolog的Event类型添加自定义方法,封装从Context提取Request ID并注入日志的逻辑,完全匹配你想要的log.Info().RequestId(ctx).Msg("xxx")调用形式。

实现代码

package yourpkg

import (
	"context"
	"github.com/rs/zerolog"
)

// 替换成你实际存储Request ID的Context Key
type requestIDKey struct{}

// GetRequestID 从Context中提取Request ID
func GetRequestID(ctx context.Context) string {
	rid, ok := ctx.Value(requestIDKey{}).(string)
	if !ok {
		return ""
	}
	return rid
}

// RequestId 给zerolog.Event添加Request ID字段
func (e *zerolog.Event) RequestId(ctx context.Context) *zerolog.Event {
	if rid := GetRequestID(ctx); rid != "" {
		e.Str("request_id", rid)
	}
	return e
}

使用方式

在任何需要打日志的地方,直接链式调用即可:

// Handler中
func SomeHandler(w http.ResponseWriter, r *http.Request) {
	log.Info().RequestId(r.Context()).Msg("处理请求开始")
	// ...业务逻辑
}

// Service层方法中
func (s *SomeService) DoSomething(ctx context.Context) error {
	log.Debug().RequestId(ctx).Msg("执行DoSomething方法")
	// ...业务逻辑
	return nil
}

方案二:将携带Request ID的Logger存入Context

在Request ID中间件中,基于原Logger创建一个已注入Request ID的新Logger实例,存入Context,后续直接从Context获取Logger打日志,无需额外处理。

实现代码

package yourpkg

import (
	"context"
	"net/http"
	"github.com/rs/zerolog"
)

// 替换成你实际生成Request ID的逻辑
func generateRequestID() string {
	return "unique-request-id-xxx"
}

func RequestIDMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		rid := generateRequestID()
		// 1. 将Request ID存入Context(保持原有逻辑)
		ctx := context.WithValue(r.Context(), requestIDKey{}, rid)
		// 2. 创建携带Request ID的Logger并存入Context
		reqLogger := zerolog.Ctx(r.Context()).With().Str("request_id", rid).Logger()
		ctx = reqLogger.WithContext(ctx)
		// 3. 传递新的Context
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

使用方式

从Context中取出Logger直接使用,日志会自动携带Request ID:

// Handler中
func SomeHandler(w http.ResponseWriter, r *http.Request) {
	logger := zerolog.Ctx(r.Context())
	logger.Info().Msg("处理请求开始")
}

// Service层方法中
func (s *SomeService) DoSomething(ctx context.Context) error {
	logger := zerolog.Ctx(ctx)
	logger.Debug().Msg("执行DoSomething方法")
	// ...业务逻辑
	return nil
}

两种方案的合理性分析

  • 方案一:灵活性更高,适合需要选择性给日志添加Request ID的场景,或者不想改变现有Logger调用习惯的项目。但缺点是需要确保每次打日志都调用.RequestId(ctx),否则会丢失追踪ID。
  • 方案二:更省心,一次注入后所有从Context获取的日志都会自动携带Request ID,不会遗漏。但要求项目统一规范使用zerolog.Ctx(ctx)获取Logger,避免混用全局Logger导致日志缺失Request ID。

如果你的项目能严格统一Logger的获取方式,优先选方案二;如果需要灵活控制日志字段,方案一是更好的选择。

内容的提问来源于stack exchange,提问作者Sovak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:16:10