Fiber 的 fiber.Logger() 仅负责日志格式化,不处理文件写入、并发安全、切割归档等生产需求;需用 lumberjack.Logger 替换 Output 并配置时区以满足生产要求。

Fiber 的 fiber.Logger() 中间件只做格式化输出,不负责文件写入或轮转;直接用它写到 os.Stdout 是开发调试可行的,但生产环境必须替换为带切割能力的 io.Writer(如 lumberjack.Logger),否则日志会无限增长、无法归档、查问题时卡死。
为什么 fiber.Logger() 不能直接用于生产日志文件
默认 fiber.Logger() 初始化时把日志写进 os.Stdout,你手动改写到 os.File 后——看似能落盘,实则埋雷:
- 并发写入时没有锁保护,容易出现
write /path/app.log: file already closed或日志行错乱 - 没实现按大小/时间切割逻辑,单个日志文件动辄几 GB,
tail -f卡顿、grep慢、磁盘爆满 - 没自动清理旧文件,
ls logs/列出几百个app.log.1~app.log.247
根本原因:Fiber 不处理 I/O 管理,它只管“怎么格式化一行日志”,剩下的得你填。
用 lumberjack.Logger 替换 Writer 实现安全切割
别自己封装文件打开+大小判断——那是并发 bug 温床。lumberjack.Logger 内部已用 sync.Mutex 保证原子重命名和写入,且零依赖、API 极简:
-
Filename必须是绝对路径或确保进程有写权限的相对路径(比如"./logs/app.log") -
MaxSize单位是字节,写成10 * 1024 * 1024,不是"10MB"或10_000_000(易读性差) -
MaxBackups: 7表示最多保留 7 个压缩后的旧文件(.gz后缀),超出的自动删 -
MaxAge: 28是单个日志文件最长存活天数,和切割触发无关——切完的新文件才开始计时
初始化示例:
import (
"os"
"github.com/natefinch/lumberjack"
"github.com/gofiber/fiber/v2"
)
<p>w := &lumberjack.Logger{
Filename: "./logs/app.log",
MaxSize: 10 <em> 1024 </em> 1024,
MaxBackups: 7,
MaxAge: 28,
Compress: true,
}</p><p>app := fiber.New()
app.Use(fiber.Logger(fiber.Config{
Format: "[${time}] ${status} - ${method} ${path} ${latency}\n",
Output: w, // ← 关键:把 lumberjack 实例传进去
}))</p>
请求日志里漏掉关键字段怎么办
默认 fiber.Logger() 格式不包含客户端 IP、User-Agent、响应体大小,排查时经常抓瞎。用 Format 字段自定义即可:
-
${ip}取真实客户端 IP(自动识别 X-Forwarded-For,需配ProxyHeader) -
${ua}是 User-Agent,${bytesSent}是响应字节数(含 header) - 注意:
${body}默认不打印请求体——太敏感,要打必须自己写中间件,且仅限调试环境
推荐生产用格式:
Format: "[${time}] ${status} ${method} ${path} ${ip} ${ua} ${bytesSent} ${latency}\n"
如果用了反向代理(如 Nginx),记得在 fiber.Config 里设 ProxyHeader: "X-Forwarded-For",否则 ${ip} 总是 127.0.0.1。
日志时间戳比系统时间快/慢几个小时
这是 fiber.Logger() 内部用 time.Now().UTC() 导致的——它不读系统时区。解决方法只有两个:
- 启动前调用
time.LoadLocation("Asia/Shanghai")并在Format里用${time:2006-01-02 15:04:05}(注意冒号后跟 layout) - 更稳的做法:不用内置时间变量,自己写个中间件,用本地时区格式化后塞进
c.Locals,再通过CustomTime钩子注入(需 Fiber v2.50+)
多数人卡在这一步:看到日志时间不对,第一反应是改系统 /etc/timezone,其实没用——Fiber 进程根本不看那个。











