如何基于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
相关产品推荐
相关产品推荐

