skynet 日志切割到底怎么做:一条 SIGHUP 换掉正在写的文件
【免费下载链接】skynetA lightweight online game framework项目地址: https://gitcode.com/GitHub_Trending/sk/skynet
凌晨 3 点告警弹出来:磁盘 98%,游戏服上那个 skynet.log 已经 11GB。我试着 mv 了一下,进程还在往老 inode 里写,新文件是空的。后来我把 skynet 日志轮转的源码翻了一遍,发现它靠的就是消息队列加一条信号。本文把这条链路走通,照着改,10 分钟内就能把一台正在跑的服务器日志切走。
效果速览:skynet 日志轮转前后差在哪
| 指标 | 各服务自己 print / 各自写文件 | skynet logger + SIGHUP |
|---|---|---|
| 写日志的位置 | 散在各服务协程里,抢同一个 fd | 单个 logger 服务,worker 线程统一落盘 |
| 溯源 | 一行看不出谁打的 | 每行自带[:0000000x]源 handle |
| 换文件 | 重启进程 | mv + kill -HUP,不停服 |
| 磁盘兜底 | 无上限 | 内置仍无上限,靠 cron 自清(第 4 节说) |
前三行是源码里白送的,最后一行要诚实说:skynet 不管文件数量和大小,磁盘安全得你自己兜。
它背后到底怎么转的
先建立感觉:skynet 里不存在"服务直接写文件"这回事。整栋楼共用一个收发室窗口——每个服务要打日志,就是把一张纸条(PTYPE_TEXT 消息)塞进窗口的托盘(logger 的消息队列);收发室管理员,也就是 logger 服务,在 worker 线程的调度下逐张取走,盖上时间和"寄件人房号",誊进那本总账。所以并发再高,日志 IO 也不会挤进业务协程,磁盘等待只由 logger 的队列承担。
看代码非常直白:skynet-src/skynet_error.c里的skynet_error把文本格式化后skynet_context_push给名叫 "logger" 的服务,不碰任何 FILE*;另一端service-src/service_logger.c收到消息,写时间戳(1/100 秒精度)加[:%08x]源 handle 加正文,然后按行 fflush。轮转部分更短:进程注册了 SIGHUP 处理,收到信号就向 logger 推一条 PTYPE_SYSTEM,logger 对着同一个文件名freopen重绑 fd。
skynet_context_push(logger, &smsg); // 一条日志 = 往 logger 队列塞一条消息两段拼起来就是操作顺序:先 mv 老文件,再发 HUP,fd 重绑到同名新文件上,老文件原地冻结,新文件自然是空的。再提一句:logger 是整个进程的第一个服务(skynet-src/skynet_start.c里第一个skynet_context_new),配置里logger为 nil 时句柄就是 stdout,日志全打控制台。
最小可用配置——从空目录到跑起来
examples/config默认logger = nil(即控制台输出),要落到文件总共动 3 处。
改哪个文件:logger 一行指向日志路径
-- examples/config logger = "./skynet.log" -- 文件名直接作为 logger 服务的启动参数 thread = 8人话:logger的值不是模块名,是文件名,它会被原样传给 logger 服务的 init 函数去 fopen。
启动时挂什么
make # 产物就是根目录的 ./skynet ./skynet examples/config跑常驻进程前记得在配置里加一行daemon = "./skynet.pid"写出 pid 文件,后面的 kill -HUP 靠它定位进程。
怎么确认生效
tail -1 skynet.log # 01/09/26 05:41:03.12 [:00000002] LAUNCH snlua launcher两个标志:行首有 1/100 秒精度的时间戳,行里有[:0000000x]handle,两个都在就说明队列链路是通的。
上生产前,先把这 3 个坑绕开 📌
日志不见了,而且行上没有 handle。根因:服务里print()是直奔 stdout,绕开了队列,只有skynet.error走 logger(lualib/skynet.lua里skynet.error = c.error是接线点)。解法:业务日志统一换 skynet.error,print 只留本地调试。
skynet.error("pay fail, uid:", uid) -- 走 logger 队列,自动带源地址我起项目第一个月栽在这,查了半天为什么线上日志对不上。
mv 之后老文件还在长。现象:文件挪走了,磁盘占用却纹丝不动。根因:mv 只是改目录项,fd 还指着老 inode,logger 只在收到 SIGHUP 时才重绑。解法:mv 完必须跟一条 HUP,logrotate 的话放进 postrotate 钩子:
mv skynet.log skynet.log.1 kill -HUP $(cat skynet.pid) # 触发 freopen,fd 绑回同名新文件没配 daemon pid 文件就只能 pgrep 兜底。
一周后日志目录堆满 .1 / .2 / .3。现象:磁盘又慢慢涨。根因:内置 logger 只会追加,无大小上限、无自动删除,且每行都 fflush(service-src/service_logger.c),量大的时候这是最大的一笔 I/O。解法:cron 清 7 天前的旧文件,业务侧先降日志级别。
find ./log -name "skynet.log.*" -mtime +7 -delete # 每天跑一次凌晨 3 点那次磁盘告警再没响过,现在就是 mv 加一条 HUP,睡到自然醒。下一步建议把 cron 里的 7 改成 14,观察一周 I/O 再谈要不要给旧日志上压缩。
【免费下载链接】skynetA lightweight online game framework项目地址: https://gitcode.com/GitHub_Trending/sk/skynet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考