如何使用 systemd 高效轮换非日志记录?

如何使用 systemd 高效轮换非日志记录?

我正在将一项服务从 AWS 上的 ECS 迁移到 EC2,其中需要弄清楚日志处理。该服务每个实例每天生成大约 30GB 的日志。我使用 systemd 启动该服务,如下所示:

[Unit]
Description=Proxy Service
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/java -jar /app/proxy-all.jar
Restart=on-failure
User=proxy
StandardOutput=file:/var/log/proxy/output.log
StandardError=file:/var/log/proxy/error.log

[Install]
WantedBy=multi-user.target

我的问题是如何进行日志轮换?如果我运行 logrotate 来轮换日志,那么由于服务器仍将文件句柄保持为对旧文件开放,因此它将继续在那里写入。systemd 本身是否以某种方式支持日志轮换?

一个肮脏的解决方案是在每次日志轮换后重新启动服务,但这是一个关键服务,它维护着超过十万个持久的 websocket 连接,重新启动会杀死所有连接,所以这是不可行的。

转发到例如 rsyslog 是一种选择,但我想尽量减少使用的组件数量以保持事情简单,这样我就可以专注于编码而不是操作。

我在 EC2 实例上运行 Ubuntu 20.04,其 Systemd 版本为 245。

答案1

Systemd 没有内置代理文件写入功能。使用StandardOutput=file:***将始终导致服务直接持有该文件的打开句柄,此时 systemd 无法强制其关闭并重新打开该文件。

不计算 syslog,解决这个问题的方法是将您的服务更改为直接进行文件日志记录而不是标准输出日志记录 - 这样就可以很容易地在服务本身中构建日志轮换(关闭/重命名/打开),或者至少让它对某些外部信号做出反应并关闭/重新打开当前文件。

另一种方法是使用外部的可以在管道中使用的工具可以完成完全相同的任务。(这样的工具还需要直接记录到文件中,而不是依赖于 systemd 的 stdout 重定向。当然,即使 stdout 已被重定向,您也可以关闭它,但是当您尝试重新打开它时,您会使用什么路径呢?)

有几种这样的工具,例如 Apache httpd 附带一个旋转日志工具非常通用,可以与任何服务结合使用。我发现的另一个工具是纪年法

ExecStart=/bin/sh -c "exec java -jar /app/proxy-all.jar | rotatelogs /var/log/proxy.%Y-%m-%d.log"
StandardOutput=none

但是,由于这现在依赖于作为管道的一部分运行的 Java 服务,它实际上可能会干扰 systemd 对“主进程”的跟踪,所以我不推荐它 - 最好让服务本身记录到文件中。

相关内容