我们有许多生产应用程序(第一方和第三方),它们将日志记录留给运行该应用程序的进程,并且仅记录到stdout
forINFO
和stderr
forERROR
日志(即,只有 2 个日志优先级INFO|ERROR
:)。
使用应用程序的 systemd 服务单元可以像这样设置:
StandardOutput=journal
StandardError=journal
这使得 devops 能够通过 systemd 单元和日志管理一切,以便于集中收集日志、监控一切……而且他们不必担心为每个部署的应用程序定位和解析不同格式/位置的不同日志。
systemd 的日志具有与 syslog 的 7 级消息优先级系统兼容的消息优先级系统。 INFO
是级别6
和ERROR
是级别3
。有关更多详细信息,请参阅参考资料。
问题是... systemd/journal 显然不区分从 stdout 和 stderr 写入日志的消息。stdout 和 stderr 消息都以默认优先级 6 ( INFO
) 写入日志。
例如:“精美应用程序”
/opt/log-test.sh
#!/bin/bash
echo "This is ERROR" 1>&2
echo "This is INFO"
exit 0
/etc/systemd/system/log-test.service
[Unit]
Description=log test for journal
[Service]
Type=simple
ExecStart=/opt/log-test.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=log-test
运行并检查日志
$ systemctl start log-test
$ journalctl -u log-test
-- Logs begin at Thu 2022-04-07 08:17:16 UTC, end at Thu 2022-04-07 16:35:02 UTC. --
Apr 07 16:34:58 host.example.com systemd[1]: Started log test for journal.
Apr 07 16:34:58 host.example.com log-test.sh[29909]: This is ERROR
Apr 07 16:34:58 host.example.com log-test.sh[29909]: This is INFO
$ journalctl -u log-test -p 6 # syslog info priority
-- Logs begin at Thu 2022-04-07 08:17:16 UTC, end at Thu 2022-04-07 16:35:08 UTC. --
Apr 07 16:34:58 host.example.com systemd[1]: Started log test for journal.
Apr 07 16:34:58 host.example.com log-test.sh[29909]: This is ERROR
Apr 07 16:34:58 host.example.com log-test.sh[29909]: This is INFO
$ journalctl -u log-test -p 3 # syslog error priority
-- No entries --
$
您可以看到,在写入日志时,stderr 和 stdout 消息都被设置为优先级6
( )。INFO
stdio->journal
这是一个问题,因为当用作主要日志记录工具时,我们没有简单的方法来区分 stdout 和 stderr 上的输出。
这已经之前讨论过和解决方案是可能的,但是未实现。我希望 systemd 团队最终能够实现这一点,但同时我需要一个解决方案。
有没有人找到一个合理的解决方案,让写入 stdout 和 stderr 的消息在日志中具有不同的优先级无需修改应用程序的日志记录方式?
我不希望我们部署的所有应用程序(并非全部由我们编写)都必须实现日志或系统日志集成才能获得日志优先级,而我们实际上只需要两个级别:INFO
(stdout)和ERROR
(stderr)。
我们部署的大部分内容都不是容器化的,因此依赖容器的日志记录功能对我们来说也不是一个解决方案。
默认情况下,让 stderr 和 stdout 以不同的优先级进入日志/系统日志对于简化分布式日志错误监控至关重要(假设开发人员有良好的卫生习惯,只编写需要关注 stderr 的内容)。
参考:
答案1
如果必须使用stdout
/ stderr
,则可以使用sd-daemon
日志前缀。
在您的stderr
前面加上<3>
以发送ERROR
优先journald
日志。
使用你的log-test.sh
和log-test.service
:
#!/bin/bash
>&2 echo "<3>This is ERROR"
echo "This is INFO"
exit 0
并journalctl
输出:
$ journalctl -u log-test -p 3
May 02 01:22:58 host.example.com log-test.sh[29909]: This is ERROR
如果您fancy-app
有任何 API 可以写入syslog
,您可以使用它来写入 UNIX 数据报/dev/log
(通常默认情况下可写,并记录到journald
)而不是stdout
/ stderr
。使用 syslog 标签来识别您的fancy-app
,系统日志优先级error
或info
取决于您的需要,以及任何系统日志设施。
例如,在 Bash 中我们可以使用logger
:
# emit INFO message to journalctl
$ logger -t fancy-app -u /dev/log -p user.info "This is INFO"
# emit ERROR message to journalctl
$ logger -t fancy-app -u /dev/log -p user.error "This is ERROR"
# show journald messages for fancy-app
$ journalctl -t fancy-app
May 02 01:23:38 host.example.com fancy-app[27302]: This is INFO
May 02 01:23:39 host.example.com fancy-app[27303]: This is ERROR
# show journald ERROR messages for fancy-app
$ journalctl -t fancy-app -p 3
May 02 01:23:39 host.example.com fancy-app[27303]: This is ERROR
请注意,在大多数发行版中journald
,条目通常会转发到本地 syslog 守护进程(syslog-ng
,,rsyslog
...),因此可能需要检查您的 syslog 过滤器,或者使用local0
...local7
设施。
我们有许多生产应用程序(第一方和第三方)将日志记录保留到容器中,并且仅将 INFO 日志记录到 stdout,将 ERROR 日志记录到 stderr(即只有 2 个日志优先级:INFO|ERROR)。
大多数容器引擎应该能够记录到 syslog。在不知道你的容器引擎的情况下,我将使用 Docker 作为示例。
Docker 有syslog 日志驱动程序可用于将使用 syslog 格式的日志消息发送到任何 syslog 目标。您应该能够journald
使用类似以下内容进行登录:
docker run \
--log-driver syslog \
--log-opt syslog-address=unix:///dev/log \
--log-opt syslog-facility=user \
--log-opt tag=fancy-app \
fancy-app:latest
Docker 还有journald 日志驱动程序可用。例如:
docker run \
--log-driver journald \
--log-opt tag=fancy-app \
fancy-app:latest
两个日志记录驱动程序(系统日志和日志stdout
) 支持和之间的分离stderr
;即,stdout
消息将按INFO
优先级记录,并且stderr
消息将按ERROR
优先级记录。
抛开哲学和口水战,为什么不记录到真正的系统日志中呢?它更简单,以文本格式存储,并且通常由日志管理软件支持(请参阅格雷洛格,Logstash,纸质记录)。