在Raspbian上如何正确地重定向systemd服务的stdout/stderr输出?

11

我在 Raspbian (Jessie) 上使用 systemd 搭建了一个服务,使其可以在开机后自动启动。守护进程的配置如下:

[Unit]
After=multi-user.target

[Service]
Type=idle
User=root
ExecStart=/bin/sh -c "exec /home/pi/sources/mydaemon.py >> /home/pi/mydaemon.log 2>&1"

[Install]
WantedBy=multi-user.target
那个重定向符号>>没有起作用。我已经尝试了大多数可用的StandardOutputStandardError选项,但它们从未将脚本的输出打印到/var/log/daemon.log,而journalctl -u mydaemon.service仅显示关于启动和停止服务的消息。
目前我在脚本中不使用文件描述符。我只希望我的print()logging.info()语句出现在我可以阅读的某个地方。有任何想法吗?
(为清楚起见,该守护进程必须以root身份运行。这是否可能与我的打印问题有关?)
3个回答

15

我需要使用 -u 参数来运行 Python,以确保消息不会被缓冲。

有了这些代码,print 输出的行会立即添加到日志中:

StandardOutput=journal+console
ExecStart=/home/pengman/scripts/mqtt_monitor/venv/bin/python -u home/pengman/scripts/mqtt_monitor/src/mqtt_monitor.py

(我正在虚拟环境中运行,但这并不重要)


1
你还在systemd服务文件中使用StandardOutput=吗?或者你在脚本中使用Python的systemd.journal模块了吗? - harperville
1
harperville,我已经在服务文件中设置了StandardOutput。我已更新答案以包含该信息。 - Pengman

15

@mark-stosberg的答案完全正确。我想对他的回答进行扩展,因为评论太短了。

systemd-journald.service手册在STREAM LOGGING部分提到了这一点:

systemd服务管理器默认将所有服务进程的标准输出和标准错误连接到日志记录中。可以通过StandardOutput=/StandardError=单元文件设置来更改此行为,请参阅systemd.exec(5)获取详细信息。

因此,在@eric的原始单元文件中,为了将输出写入/home/pi/mydaemon.log,您可以在单元文件中执行以下操作:

ExecStart=/usr/bin/python -u /home/pi/sources/mydaemon.py
StandardOutput=file:/home/pi/mydaemon.log
StandardError=inherit

请注意StandardError的值。这来自于systemd.exec(5)手册,它的意思是将stderr钩到与stdout相同的句柄上,就像在shell语法中使用2>&1一样。
此外,如果您希望日志立即更新,必须告诉Python不要缓冲输出(使用其-u开关)。这就是为什么我也更改了ExecStart=。当流式传输Python输出(无论是在systemd中还是从shell中),默认情况下输出会被缓冲,文件直到缓冲刷新或进程结束后才会更新。

更新20190411 - Raspbian早期版本的systemd不接受StandardOutputfile:/目标(感谢@nak指出),因此我的答案对于Raspbian的OP问题并不真正有效(我在openSUSE Tumbleweed中进行了测试)。这个SO问题"How to redirect output of systemd service to a file"有关于使用旧版systemd的替代方法的详细信息。


这在我使用的2018-11-13发布的Raspbian Stretch上失败,并显示以下错误:“无法解析输出说明符,忽略:file:” - nak
我没有最近的Raspbian Stretch来玩。文件:/path是在Systemd 236中添加的,您可能想要检查Stretch中的版本。Jessie是215。 - mike

9
通常情况下,您只需要直接运行服务(确保其可执行文件有一个 shebang 行):
 ExecStart=/home/pi/sources/mydaemon.py

使用StandardOutput=的默认重定向到systemd日志,以便您可以使用journalctl -u mydaemon.service读取日志。

Systemd会很好地控制日志文件的增长和文件轮换。

这与您的服务作为root用户运行无关。

如果您在上述内容中没有看到任何日志输出,请还要检查整个日志,以查找可能归因于您的服务的附近日志:

 journalctl

这是必要的,因为已知问题是在脚本退出之前记录的某些最后一行可能不被归属于该脚本。对于只在运行了几分之一秒后退出的脚本尤其明显。


这似乎只有部分时间能正常工作。我不知道systemd或journalctl在Raspbian Jessie上出了什么问题,但我怀疑有一个干扰日志记录的问题。我的脚本会发推文,所以我知道它何时被激活,但脚本中的打印语句通常不会显示在日志中。 - Eric
除了您的答案之外,我将尝试在我的脚本中使用此方法,以查看是否有更好的运气(通过显式发送消息到日志记录器)https://dev59.com/DVsW5IYBdhLWcg3wyJt3 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接