如何将 systemd 服务的输出重定向到文件

Posted

技术标签:

【中文标题】如何将 systemd 服务的输出重定向到文件【英文标题】:How to redirect output of systemd service to a file 【发布时间】:2016-10-01 19:56:14 【问题描述】:

我正在尝试将systemd 服务的输出重定向到一个文件,但它似乎不起作用:

[Unit]
Description=customprocess
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/binary1 agent -config-dir /etc/sample.d/server
StandardOutput=/var/log1.log
StandardError=/var/log2.log
Restart=always

[Install]
WantedBy=multi-user.target

请纠正我的做法。

【问题讨论】:

【参考方案1】:

简答:

StandardOutput=file:/var/log1.log
StandardError=file:/var/log2.log

如果您不希望每次运行服务时都清除文件,请改用 append:

StandardOutput=append:/var/log1.log
StandardError=append:/var/log2.log

【讨论】:

与this answer 重复,细节较少 超级有帮助。我使用这种技术将日志记录恢复到 Ubuntu 18.04 和 20.04 的 /var/log/tomcatX/catalina.out。【参考方案2】:

我们正在使用 Centos7,带有 systemd 的 Spring Boot 应用程序。我正在运行java,如下所示。并将 StandardOutput 设置为文件对我不起作用。

ExecStart=/bin/java -jar xxx.jar  -Xmx512-Xms32M

以下解决方案在不设置 StandardOutput 的情况下工作。通过 sh 运行 java,如下所示。


ExecStart=/bin/sh -c 'exec /bin/java -jar xxx.jar -Xmx512M -Xms32M >> /data/logs/xxx.log 2>&1'

【讨论】:

-1 用于以错误的顺序定义 jvm 参数。 -Xmx512M 必须在 -jar 之前定义。你所经历的也是预期的。 Systemd 不使用 shell 调用服务 @SamiKorhonen,我在测试此功能后添加了我的 cmets。甚至我在考虑 -Xmx512M 的命令对你来说也是一样的。请在添加盲 cmets 之前进行测试。【参考方案3】:

我建议在 systemd service 文件本身中添加 stdoutstderr 文件。

参考:https://www.freedesktop.org/software/systemd/man/systemd.exec.html#StandardOutput=

正如你所配置的,它不应该是这样的:

StandardOutput=/home/user/log1.log
StandardError=/home/user/log2.log

应该是:

StandardOutput=file:/home/user/log1.log
StandardError=file:/home/user/log2.log

当您不想一次又一次地重新启动服务时,这很有效

这将创建一个新文件,并且不会附加到现有文件。

改用:

StandardOutput=append:/home/user/log1.log
StandardError=append:/home/user/log2.log

注意:确保您已经创建了目录。我猜它不支持创建目录。

【讨论】:

我想,我写得更直接\容易理解。 对我来说,file: 路由在服务的第一次加载时工作,但在随后的重新启动时它不再写入文件。我从文档中尝试了append:,但根本没有用。 请注意,文档清楚地表明 file: 每次都写入文件的开头,并且不会截断......此外,append: 似乎是一个新的添加(即不存在在 Ubuntu 18.04 上的 man systemd.exec 页面中)。 append: 已在 systemd 版本 240 中引入。要解决 file: 在目标文件开头放置新日志输出的问题,类似这样的操作可能会有所帮助:ExecStartPre=/bin/bash -c 'mv /var/log/my/logs.log /var/log/my/$$(date +%%T)-logs.log'。保持干净的日志并以某种方式模拟日志轮换效果【参考方案4】:

我认为有一个更优雅的方法来解决这个问题:将 stdout/stderr 发送到带有标识符的 syslog 并指示您的 syslog 管理器按程序名称拆分其输出。

在您的 systemd 服务单元文件中使用以下属性:

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=<your program identifier> # without any quote

然后,假设您的发行版使用 rsyslog 来管理 syslog,请在 /etc/rsyslog.d/&lt;new_file&gt;.conf 中创建一个包含以下内容的文件:

if $programname == '<your program identifier>' then /path/to/log/file.log
& stop

现在使日志文件可被 syslog 写入:

# ls -alth /var/log/syslog 
-rw-r----- 1 syslog adm 439K Mar  5 19:35 /var/log/syslog
# chown syslog:adm /path/to/log/file.log

重新启动 rsyslog (sudo systemctl restart rsyslog) 并享受吧!您的程序 stdout/stderr 仍可通过 journalctl (sudo journalctl -u &lt;your program identifier&gt;) 获得,但它们也可在您选择的文件中获得。

Source via archive.org

【讨论】:

在 Ubuntu 16.04 上不适合我。 journalctl -u 仍然有效,但没有任何内容发送到指定文件。 这在 Debian stretch 上效果很好,但是它抱怨 ~ 已被弃用,应该使用 stop 代替。另请注意,如果两者相继出现,则第二行可以缩短为 &amp; stop 使用 systemd 236 或更高版本,您还可以使用 StandardOutput=file:/some/path github.com/systemd/systemd/pull/7198直接写入文件 我通过将 /etc/rsyslog.d/&lt;newfile&gt;.conf 内容更改为::programname, isequal, "&lt;your program identifier&gt;" /var/log/somelog.log 来实现此功能,这是关于 rsyslog 过滤器的文档:rsyslog.com/doc/v8-stable/configuration/filters.html 这里是关于 programname 等属性的文档:rsyslog.com/doc/master/configuration/properties.html 在我发现rsyslog 有自己的用户syslog 并且它必须具有对日志位置的写入权限之前,我在使用此配置时遇到了问题。所以相应地使用chown。希望这对某人有所帮助。【参考方案5】:

假设日志已经放到stdout/stderr,并且systemd单元的日志在/var/log/syslog

journalctl -u unitxxx.service

Jun 30 13:51:46 host unitxxx[1437]: time="2018-06-30T11:51:46Z" level=info msg="127.0.0.1
Jun 30 15:02:15 host unitxxx[1437]: time="2018-06-30T13:02:15Z" level=info msg="127.0.0.1
Jun 30 15:33:02 host unitxxx[1437]: time="2018-06-30T13:33:02Z" level=info msg="127.0.0.1
Jun 30 15:56:31 host unitxxx[1437]: time="2018-06-30T13:56:31Z" level=info msg="127.0.0.1

配置 rsyslog(系统日志服务)

# Create directory for log file
mkdir /var/log/unitxxx

# Then add config file /etc/rsyslog.d/unitxxx.conf

if $programname == 'unitxxx' then /var/log/unitxxx/unitxxx.log
& stop

重启 rsyslog

systemctl restart rsyslog.service

【讨论】:

【参考方案6】:

如果您的发行版具有较新的 systemd (systemd version 236 or newer),您可以将 StandardOutputStandardError 的值设置为 file:YOUR_ABSPATH_FILENAME


长篇大论:

在较新版本的 systemd 中,有一个相对较新的选项 (the github request is from 2016 ish and the enhancement is merged/closed 2017 ish),您可以在其中将 StandardOutputStandardError 的值设置为 file:YOUR_ABSPATH_FILENAMEfile:path 选项记录在 most recent systemd.exec man page 中。

这项新功能相对较新,因此不适用于像 centos-7(或之前的任何 centos)这样的旧发行版。

【讨论】:

在 2018 年 3 月 20 日无法在 ubuntu 1604 中工作。 ubuntu 1604的systemd版本只有229。 谢谢,你说的很清楚。我简直不敢相信 ubuntu 1604 中的 systemd 不能仅通过 config 将输出重定向到文件。我必须使用 sh 的方式来解决这个问题。 @bronzeman 功能请求直到 2017 年才关闭,而 Ubuntu 16.04 于 2016 年发布。在给定的 Ubuntu 主要版本(例如 16.04、16.10、17.04 等)中,Ubuntu 维护 ABI其核心系统包的兼容性。因此他们不会升级 systemd(或 Linux 内核、glibc 或其他任何东西),除非它保持与首次发布 Ubuntu 版本时相同的 ABI。 FWIW:我搜索了一下,但此功能似乎没有提供日志轮换的规定,例如重新打开日志文件的功能,必须使用 copytruncate 之类的功能在logrotate. 问题是,无论单位的用户和组如何,该文件始终创建为 root:root...【参考方案7】:

如果由于某种原因不能使用 rsyslog,可以这样做: ExecStart=/bin/bash -ce "exec /usr/local/bin/binary1 agent -config-dir /etc/sample.d/server >> /var/log/agent.log 2>&1"

【讨论】:

bash的-e选项有什么作用? 不幸的是,在我当前的 systemd 版本中,在每次服务重启时不会丢失日志文件的唯一解决方案在我的情况下不可用。【参考方案8】:

您可能会收到此错误:

Failed to parse output specifier, ignoring: /var/log1.log

来自systemd.exec(5) 手册页:

StandardOutput=

控制已执行进程的文件描述符 1 (STDOUT) 连接到的位置。采用inheritnullttyjournalsyslogkmsgjournal+consolesyslog+consolekmsg+consolesocket 之一。

systemd.exec(5) 手册页解释了与日志记录相关的其他选项。另请参阅 systemd.service(5)systemd.unit(5) 手册页。

或者也许你可以尝试这样的事情(全部在一条线上):

ExecStart=/bin/sh -c '/usr/local/bin/binary1 agent -config-dir /etc/sample.d/server 2>&1 > /var/log.log' 

【讨论】:

在选项中,推荐登录到 systemd 日志。您可以使用journalctl -u your-unit-name 在日志中查看您的流程的日志。 要指定文件,还有另一个更简洁的选项,如文档所示:The fd option connects the output stream to a single file descriptor provided by a socket unit. A custom named file descriptor can be specified as part of this option, after a ":" (e.g. "fd:foobar"). 真棒的答案,它解决了我的问题。我只是想扩展,因为目前如果服务重启覆盖旧日志,必须将这部分:2&gt;&amp;1 &gt; /var/log.log 替换为:2&gt;&amp;1 &gt;&gt; /var/log.log。谢谢 坦率地说,在 ExecStart 中使用命令字符串调用 shell 听起来确实是错误的做法。 "/bin/sh" 是一个很好的解决方法,但您必须使用 "exec",否则服务将无法正确重启,因为 SIGTERM 不会传递给子进程。见veithen.io/2014/11/16/sigterm-propagation.html

以上是关于如何将 systemd 服务的输出重定向到文件的主要内容,如果未能解决你的问题,请参考以下文章

显示 Windows 命令提示符输出并将其重定向到文件

sudo 与输出重定向

将特定日志从systemd服务重定向到单独的文件不起作用

重定向与管道

重定向和管道

重定向和管道