我的 systemd 服务定义如下,工作正常:
[Unit]
Description=my service
After=network.target
[Service]
User=myuser
Group=mygroup
WorkingDirectory=/home/myuser/myapp
Environment="PATH=/home/myuser/myapp/.venv/bin"
ExecStart=/home/myuser/myapp/.venv/bin/python3 /home/myuser/myapp/run.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
这是一个基于 Flask 框架的 Python Web 应用程序。通常在应用程序的标准输出中,我可以“实时”看到传入的请求,我的意思是当我运行应用程序时,例如python run.py
.
现在启动服务后,我想跟踪应用程序的日志,我这样做:
sudo journalctl -f -u my_app.service
并且传入的日志非常慢 - 有时需要几分钟或更长时间它们才会出现在日志中。但之后它们都有了正确的时间戳,所以它们并不是消失了,而是消失了很长一段时间。
我尝试过的:
将 systemd 服务输出重定向到文件:
StandardOutput=file:/var/log/my_app/output.log
StandardError=file:/var/log/my_app/error.log
运气不好 - 他们节省得很好,但速度同样慢
尝试将journalctl日志转储到离线更快设置
SyncIntervalSec
从默认5m
到5s
- 也没有帮助
有什么方法可以更快地将这些日志从我的应用程序传递到日志?我在使用其他服务(例如系统身份验证服务)时没有遇到任何问题 - 我可以立即看到记录。我的journald.conf
文件具有默认参数(除上述参数外),我的 systemd 版本为 237,运行的是 Ubuntu 18.04。
答案1
问题实际上在于 Flask 应用程序的缓冲,而不是 systemd 或 Journald 如何摄取这些日志。
这可能是违反直觉的,因为正如您所提到的,python3 run.py
直接在命令行上运行可以正常工作并正确显示日志,而且时间戳在日志上看起来也是正确的。
发生前者是因为 Unix/Linux 通常会将 stdout 设置为在连接到终端时不缓冲(因为它期望与用户交互),但在连接到文件(在 的情况下StandardOutput=file:...
)或管道(如果您正在记录到日志,这是默认的。)
后者是因为 Python/Flask 记录器正在添加时间戳,因此即使它正在缓冲该输出,当它最终将其发送到日志中时,所有时间戳都在那里。
一些应用程序会知道这通常是一个问题,并且在将其用于日志时会在标准输出上适当地设置缓冲,但对于您正在使用的特定 Python/Flask 设置来说,情况似乎并非如此。
在 Python 上,将 stdout 全局更改为无缓冲模式相当容易,您可以通过以下方式执行此操作:
- 通过一个
-u
旗帜在python3
你的指挥下。 - 环境
PYTHONUNBUFFERED=1
在您的环境中(您可以在 systemd 服务单元中使用附加Environment=PYTHONUNBUFFERED=1
行执行此操作。)
您确认这适用于您的具体案例,所以太好了!
对于遇到类似问题的非 Python 应用程序,有一些命令行工具(例如unbuffer
和 )stdbuf
通常可以解决同样的问题。
解决方案通常特定于应用程序的类型,这有点不幸,但经常在 Stack Exchange 中搜索或寻找其他答案(一旦您知道缓冲是问题)通常会给你带来有用的建议。