必须用 os.OpenFile 以追加模式写日志,配置 SkipPaths 过滤健康检查路径,结合 lumberjack 轮转与 JSON 结构化输出,并为 panic 日志单独配置 RecoveryWithWriter。

gin.DefaultWriter 直接写文件会清空旧日志
默认用 os.Create("gin.log") 赋值给 gin.DefaultWriter,每次启动都会覆盖原文件。线上服务重启一次,历史日志就丢一次。这不是“持久化”,是“定期擦除”。
必须改用 os.OpenFile("gin.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644),否则运维查不到前一天的请求链路。
还要确认进程对目标目录有写权限——没权限时 os.OpenFile 不报错,但日志完全不落盘,连 lsof -p $PID 都看不到句柄。
高频健康检查路径必须 Skip
没加 SkipPaths 的 Gin 日志,/healthz、/readyz 这类接口每秒打几十次,日志量轻松压垮磁盘 IO。它不是“多记几行”,而是让写入变成瓶颈。
正确做法是在 gin.LoggerWithConfig 里传入 SkipPaths: []string{"/healthz", "/readyz"};
如果用了自定义中间件做健康检查,路径名要和实际路由完全一致(注意结尾斜杠);
别信“反正只是 INFO 级别”——哪怕全设成 info,高频小日志照样刷爆磁盘,因为系统调用开销在那儿。
生产环境必须轮转 + 结构化
用 lumberjack.Logger 替代裸 *os.File 是底线。只设 MaxSize: 10(MB)不够,还得配 MaxBackups: 7 和 MaxAge: 7(天),不然归档文件越积越多。
纯文本日志进不了 ELK 或 Loki,得自己套一层 JSON:用 logrus.JSONFormatter 或手写 Formatter 函数返回 json.Marshal 后的字节;
别忘了禁用控制台颜色:gin.DisableConsoleColor(),否则文件里全是 \x1b[32m 这种 ANSI 字符,grep 和日志系统解析全乱。
panic 日志必须单独捕获到文件
gin.Recovery() 默认只打屏,一旦进程挂了,panic 记录就没了。必须用 gin.RecoveryWithWriter 指向独立文件,比如 error_panic.log。
这个文件不能和普通访问日志混用——panic 是偶发但关键事件,需要高优先级保留,而访问日志可能每秒上千行;
文件句柄要单独打开,别和 gin.DefaultWriter 共用同一个 *os.File,否则 panic 日志可能被缓冲阻塞,等不到写入就进程退出。
lumberjack 时),df 显示已释放空间,但 du 还算着大小。这时候得靠 lsof +L1 找出 deleted 状态的句柄,再决定是否要 kill -USR1 或重启服务。


















