如何从 Raspbian 上的 systemd 服务正确重定向 stdout/stderr?

Posted

技术标签:

【中文标题】如何从 Raspbian 上的 systemd 服务正确重定向 stdout/stderr?【英文标题】:How do I properly redirect stdout/stderr from a systemd service on Raspbian? 【发布时间】:2017-09-21 23:43:33 【问题描述】:

我使用 systemd 在 Raspbian (Jessie) 上设置了一项服务,使其在启动后启动。守护进程配置如下所示:

[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 身份运行。这可能与我的打印问题有关吗?)

【问题讨论】:

【参考方案1】:

通常你只是直接运行一个服务(确保它的可执行文件有一个 shebang 行):

 ExecStart=/home/pi/sources/mydaemon.py

并使用StandardOutput=的默认重定向到systemd日志,这样你就可以用journalctl -u mydaemon.service阅读日志。

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

与您的服务以 root 身份运行无关。

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

 journalctl

这是必要的,因为存在一个已知问题,即在脚本存在之前的最后几行日志记录可能不会归因于脚本。这对于在退出前只运行几分之一秒的脚本来说尤其明显。

【讨论】:

这似乎只在部分时间内有效。我不知道 Raspbian Jessie 上的 systemd 或 journalctl 有什么问题,但我怀疑存在干扰日志记录的问题。我的脚本发推文,所以我知道它什么时候被激活,但脚本中的打印语句通常不会出现在日志中。 结合你的回答,我将在我的脚本中尝试这种方法,看看我是否有更好的运气(明确向期刊发送消息)***.com/questions/34588421/…【参考方案2】:

我需要使用 -u 参数运行 python 以确保消息没有被缓冲。

使用这些行,打印行会立即添加到日志中:

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

(我在 virtualenv 中运行,但这不重要)

【讨论】:

对我来说就是这样 您是在systemd 服务文件中仍然使用StandardOutput=,还是在脚本中使用Python systemd.journal 模块? harperville,我在服务文件中设置了 StandardOutput。我已更新答案以包含该信息。【参考方案3】:

@mark-stosberg 接受的答案是正确的。我想扩展他的答案,而 cmets 太短了。

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 输出 (whether in systemd or from the shell) 时,output is buffered by default 和文件不会被更新,直到缓冲区刷新或进程结束。


更新 20190411 - Raspbian 的早期版本的 systemd 不接受 file:/ 目标 StandardOutput (感谢@nak 指出这一点),所以我的回答对于关于 Raspbian 的 OP 问题并不真正适用(我在 openSUSE Tumbleweed 中测试)。 This SO question "How to redirect output of systemd service to a file" 提供了使用旧 systemd 的替代方法的详细信息。

【讨论】:

这对我来说在 2018 年 11 月 13 日发布的 Raspbian Stretch 上失败,并出现以下错误:“无法解析输出说明符,忽略:文件:” 我没有最近的 Raspbian Stretch 可以玩。文件:/路径是added in Systemd 236,您可能想要check which version is in Stretch。杰西was 215.【参考方案4】:

我有同样的问题。 使用StandardOutput=journal+console 解决。

【讨论】:

以上是关于如何从 Raspbian 上的 systemd 服务正确重定向 stdout/stderr?的主要内容,如果未能解决你的问题,请参考以下文章

raspbian 如何启动系统修复

Raspbian 上的 Alsa 配置

markdown Raspbian上的K8s

Raspbian 上的 USB DAC 产生错误

OS.Walk For Raspbian 上的循环难题

如何在 Ubuntu 上播放来自 systemd 服务的声音?