最轻量做法是中间件开头用fiber.UUID()生成ID存c.Locals并c.Set("X-Request-ID", id),确保每个请求(含静态文件、健康检查、错误响应)必有唯一ID,避免日志链路断裂。

中间件里怎么生成并透传请求ID
直接用 fiber.UUID() 生成字符串,存进 c.Locals,再通过 c.Set() 写入响应头——这是最轻量、无外部依赖的做法。别用时间戳+随机数拼接,UUIDv4 足够唯一且 Fiber 已内置封装。
-
c.Locals["req_id"] = fiber.UUID()是安全的,Locals是请求生命周期独享的 map,不会跨请求污染 - 必须在中间件开头就生成,否则下游 handler 或后续中间件读不到;如果放在
c.Next()后面,等于只对响应阶段生效 - 写响应头推荐
c.Set("X-Request-ID", id),不是c.Append()——重复设置会叠加,而Set()是覆盖语义,更可控
为什么不能在 handler 里生成 ID
因为中间件链可能提前中断(比如鉴权失败返回 401),如果 ID 只在业务 handler 里生成,那些被中间件拦截的请求就压根没 ID,日志链路断掉。所有可观测性基础设施(如日志聚合、链路追踪)都依赖“每个请求必有 ID”这个前提。
- 典型错误:把
fiber.UUID()放在app.Get("/api/users", handler)的handler函数里 → /api/users/123 有 ID,但 /api/users 404 或 /health 401 就没有 - 正确姿势:全局中间件
app.Use(reqIDMiddleware),哪怕静态文件路由/assets/logo.png也会带上 ID,方便排查 CDN 缓存或 Nginx 转发问题 - 注意:Fiber 的
logger中间件默认不打印X-Request-ID,得手动配logger.Config.CustomTags才能透出
和分布式追踪(如 Jaeger)怎么共存
如果项目已接入 OpenTracing 或 OpenTelemetry,不要另起一套 UUID。优先复用 traceID —— 它本质也是全局唯一字符串,且已由 tracing SDK 注入 c.Locals 或 c.Context()。
- 检查是否已有 traceID:
span := otel.Tracer("app").Start(c.Context(), "http")后,可用span.SpanContext().TraceID().String() - 若没上 tracing,别为了“看起来像微服务”硬加 Jaeger 客户端——单体服务用
fiber.UUID()更稳,启动快、无网络调用、无上下文传播开销 - 切忌同时设
X-Request-ID和traceparent两个头:前者用于日志关联,后者用于 span 关联,用途不同但可共存;不过如果 traceparent 存在,建议直接用它当 request ID,避免冗余
常见错误:ID 在重定向或错误响应里丢失
生成 ID 后没主动塞进错误响应体,或重定向时没保留 header,会导致前端或下游服务拿不到 ID,查日志时对不上号。
- 返回 401 时:别只写
c.Status(401).Send("unauthorized"),要补上c.Set("X-Request-ID", id),否则 Nginx access_log 里只有 status,没有 ID 字段 - 重定向时:
c.Redirect("/login", 302)不会自动携带原请求 header,得手动传:c.Vary("X-Request-ID"); c.Set("X-Request-ID", id)再 redirect - 静态文件中间件(
static.New())默认不走你写的 reqID 中间件——必须把app.Use(reqIDMiddleware)放在app.Use(static.New(...))前面,否则 /assets/xxx.js 请求压根不经过你的中间件


















