fiber 的 logger 中间件默认记录请求耗时(latency 字段,单位 ms),但需禁用颜色(enablecolors: false)并限定路径前缀(如 /api)避免刷屏和 ansi 问题;静态资源中间件 static.new() 必须置于 logger 之后以防耗时统计失真;细粒度耗时需用 time.now() + c.locals() 在主 goroutine 同步打点。

用 logger 中间件默认就能打耗时,但得关掉颜色和调对作用域
Fiber 的 logger.New() 默认会输出请求耗时(字段叫 latency),但生产环境直接 app.Use(logger.New()) 全局启用会刷屏日志——/favicon.ico、/health、静态资源全被记录,干扰监控。更关键的是:logger.Config.EnableColors 默认为 true,CI/CD 日志管道遇到 ANSI 转义字符可能解析失败或截断。
正确做法是显式配置并限定路径前缀:
- 只对
/api下的路由生效:app.Use("/api", logger.New(logger.Config{EnableColors: false})) - 若需记录所有 API 请求但排除健康检查,可加中间件过滤:
app.Use("/api", func(c *fiber.Ctx) error { if c.Path() == "/api/health" { return c.Next() }; return logger.New()(c) })(不推荐嵌套,优先用前缀) - 耗时单位默认是
ms,不可改;字段名固定为latency,日志格式里用${latency}引用
自己写中间件测更细粒度耗时,比如只看 DB 查询那段
logger 只能打整条请求生命周期,如果你要定位 handler 里某段逻辑(如 SQL 查询、远程调用)是否拖慢响应,就得手写中间件或直接在 handler 里用 time.Now() 打点。Fiber 的 ctx 是复用对象,不能存自定义字段传给下个中间件——必须用 c.Locals()。
示例:测 DB 查询耗时
app.Get("/users/:id", func(c *fiber.Ctx) error {
start := time.Now()
user, err := db.FindByID(c.Params("id"))
if err != nil {
return c.Status(500).SendString("DB error")
}
c.Locals("db_latency", time.Since(start))
return c.JSON(user)
})
后续中间件或全局 logger 都可通过 c.Locals("db_latency") 拿到这个值,但注意它不会自动写进日志,得你自己拼进响应头或日志行。
别把 logger 放在 static 前面,否则耗时统计会失真
static.New() 是短路型中间件:匹配到文件就直接返回,不再执行后续中间件。如果你把 logger.New() 放在 static.New() 前面,它确实能打到耗时;但放反了(static 在前),logger 就完全收不到静态资源请求的日志——这本身不是 bug,但会导致你误判“API 平均耗时低”,其实是因为大量图片/CSS 请求被 static 吃掉、没进 logger。
常见错误配置:
-
app.Use(static.New("./public"))→ 所有请求先过 static,/login 这种 API 也会被它白跑一次文件查找,徒增延迟 - 正确顺序:
app.Use("/api", logger.New())+app.Use("/assets", static.New("./public")),路径前缀明确隔离 - 如果非要用全局 logger,确保
static不挂载在根路径,避免覆盖
ctx.Locals() 存耗时数据要小心并发复用
Fiber 复用 ctx 实例,c.Locals() 内部用的是 map,**不是线程安全的**。如果你在 goroutine 里异步写 c.Locals("key", val),而主流程又在读,可能 panic 或读到脏数据。
安全做法只有两种:
- 所有耗时打点都在主 goroutine 同步完成(最常用,也最稳)
- 真需要异步采集,用
sync.Map或 channel 把结果传回主流程再塞Locals,别在 goroutine 里直接操作c
另外,Locals 生命周期仅限当前请求,不用手动清理,但别指望它跨中间件自动传递——每个中间件都得自己调 c.Locals() 取。











