Fiber 框架需手动实现 RequestID 中间件,应作为首个中间件注册,用 ctx.Locals 存储 ksuid 等可靠 ID,并在 LoggerConfig.Format 中用 ${locals:request_id} 引用,异步协程需手动传递 ID。

Fiber 框架本身不内置 RequestID 中间件,但可通过标准中间件机制轻松注入自定义逻辑。你不需要依赖第三方插件,直接用 f.Context 和 ctx.Locals 或 ctx.Set 就能完成绑定,关键在于生成规则是否可预测、是否线程安全、是否避免冲突。
fiber.New() 启动时注册中间件必须在路由前
Fiber 的中间件执行顺序严格依赖注册顺序,RequestID 必须是第一个中间件(或至少早于所有业务逻辑),否则后续 handler 可能读不到 ID。
- 中间件需调用
ctx.Next()才会继续执行后续链 - 若忘记调用
ctx.Next(),请求会卡死,无响应、无错误日志 - 不建议在中间件里做耗时操作(如调用 DB 或 HTTP),否则阻塞整个请求链
示例:
app := fiber.New()
app.Use(func(c *fiber.Ctx) error {
// 生成 ID 并存入上下文
reqID := generateRequestID()
c.Locals("request_id", reqID)
// ⚠️ 必须调用 Next(),否则请求终止
return c.Next()
})
generateRequestID() 函数要避开常见陷阱
默认用 uuid.NewString() 看似简单,但在高并发下可能因熵源竞争导致性能抖动;若用时间戳+随机数拼接,又容易在毫秒级重复(尤其容器重启后时钟回拨)。
推荐组合策略:
- 使用
github.com/google/uuid(v1 或 v4) - 或用
github.com/segmentio/ksuid:带时间戳、全局唯一、可排序、无碰撞风险 - 避免用
math/rand:默认 seed 是固定值,多 goroutine 共享状态会产出重复 ID - 若需 trace 上下文透传(如跨服务),ID 应兼容
W3C TraceContext格式(16 进制 32 位)
简短示例(ksuid):
import "github.com/segmentio/ksuid"
<p>func generateRequestID() string {
return ksuid.New().String() // 27 字符,含时间信息,天然去重
}
日志中打印 request_id 要和中间件解耦
Fiber 默认日志中间件 fiber.Logger 不自动读取 ctx.Locals,必须手动扩展格式字符串:
- 使用
ctx.Locals("request_id")获取值 - 在
fiber.LoggerConfig.Format中插入占位符,例如${locals:request_id} - 若用结构化日志(如
zerolog),需在每条日志写入前显式添加字段:.Str("request_id", reqID)
常见错误:
- 直接在
Format里写${req_id}—— Fiber 不识别该变量名 - 在
ctx.Locals存了 ID,但日志中间件注册在RequestID中间件之前 —— 此时值为空
正确写法:
app.Use(fiber.Logger(&fiber.LoggerConfig{
Format: "${time} ${locals:request_id} ${status} - ${latency} ${method} ${path}\n",
}))
Fiber 的 RequestID 实现轻量,但真正难的是生成规则的可靠性与日志链路的完整性。很多团队卡在异步 goroutine 场景——比如用了 go func() { ... }(),新协程里 ctx.Locals 不可见,这时必须手动传递 ID,或改用 context.WithValue 包装原始 ctx.Context。这点容易被忽略,上线后发现部分日志缺失 ID,排查成本远高于初期多写几行代码。


















