beego中实现x-request-id全链路追踪的关键是入口唯一生成、全程透传不覆盖、并注入日志上下文;需用c.ctx.request.header.get读取,空时生成uuid v4,通过c.ctx.input.setdata存入上下文,手动透传至下游http请求,并绑定logrus等日志字段,websocket需从query参数提取id。

Beego 中实现 X-Request-ID 全局追踪,关键不是“加个中间件就完事”,而是确保 ID 在入口生成、全程透传、不被覆盖、且能注入日志上下文。后端自作主张重生成、HTTP Client 忘记透传、日志没绑定 context —— 这三处一漏,链路就断。
Beego 中间件里怎么安全生成和注入 X-Request-ID
别用 if 判断 header 是否存在再生成,Beego 的中间件执行顺序和并发模型下容易竞态。正确做法是:始终从 c.Ctx.Request.Header.Get("X-Request-ID") 读取,若为空则用标准 UUID v4(推荐 github.com/google/uuid),再通过 c.Ctx.Input.SetData("request_id", id) 存入请求上下文供后续使用。
- 禁止调用
c.Ctx.ResponseWriter.Header().Set("X-Request-ID", ...)—— Beego 默认不自动回写该 header,需显式调用c.Ctx.ResponseWriter.WriteHeader(200)后再设,否则前端收不到 - 若项目已用
logrus或log,需在中间件末尾将 ID 注入 MDC:logrus.WithField("request_id", id).Info("request started");用log的则需配合context.WithValue+ 自定义 logger 包装 - Beego v2.1+ 支持
c.Ctx.Input.SetData和c.Ctx.Input.GetData,比全局 map 更安全,避免 goroutine 间污染
Beego Controller 里如何透传到下游 HTTP 服务
Beego 自身不封装 HTTP Client,你用 http.DefaultClient 或 resty 发起调用时,X-Request-ID 不会自动带上。必须手动提取并注入:
- 在 Controller 方法内,先取 ID:
rid := c.Ctx.Input.GetData("request_id").(string) - 用
resty时:resty.R().SetHeader("X-Request-ID", rid).Get("https://api.example.com/users") - 用原生
http.Client时,需构造http.Request并调用req.Header.Set("X-Request-ID", rid),不能只改http.DefaultClient的Transport - 若下游是另一个 Beego 服务,务必确认对方已关闭自动 ID 生成逻辑(如禁用
beego.BConfig.EnableAdmin相关默认中间件)
日志中怎么让 request_id 自动出现在每条 log 行首
Beego 默认日志(beego.BeeLogger)不支持字段级上下文注入,直接调 beego.Info() 不会带 ID。必须做两件事:
- 替换默认 logger:用
logrus或zerolog初始化一个全局 logger 实例,并在 Beego 启动时调用beego.SetLogger(logrus.StandardLogger()) - 在中间件中,把
request_id绑定到当前 goroutine 的 context:ctx := context.WithValue(c.Ctx.Request.Context(), "request_id", rid),再传给后续 handler;或更简单——用logrus.WithField("request_id", rid)包一层,所有日志都走这个实例 - 注意:Beego 的
beego.Trace()/beego.Debug()等方法仍走旧 logger,除非你重写了整个beego.BeeLogger接口
最易忽略的点:WebSocket 连接。Beego 的 websocket.OnOpen 回调里拿不到 HTTP Header,X-Request-ID 会丢失。解决方案是在握手阶段(/ws?request_id=xxx)把 ID 带进来,或在 OnOpen 里解析 URL Query,再存进连接上下文。否则实时消息链路就断了。











