我必须处理一个专有的遗留服务(运行在Debian 10上),它将各种(大致)INFO和NOTICE级别的东西记录到标准输出中,并(大致)WARNING和ERROR级别的东西记录到标准输出中。两者都可以使用--stdout <path>
和--stderr <path>
命令行参数写入文件。这个守护进程的systemd单元文件如下所示(只保留最基本的内容):
[Unit]
Description=Some legacy proprietary thing
After=network.target
[Service]
ExecStart=/usr/local/bin/thing
--stdout /var/log/thing/stdout.log
--stderr /var/log/thing/stderr.log
[Install]
WantedBy=multi-user.target
问题是写入stdout.log和stderr.log的行不包含我们现在需要的时间戳。这个应用程序对我们来说是一个黑盒,修改它的日志输出来包含时间戳是不可行的。
在我的研究中,我遇到了两种可能在这里有效的选择。如果你还有其他的,我很想听听。
选项1
为stdout和stderr使用mkfifo
创建命名管道,并使遗留应用程序使用它们作为--stdout
和--stderr
目标。然后,设置一个进程来读取这些管道,将每一行都传输到ts
命令,该命令添加了一个漂亮的时间戳,如下所示:
$ echo foo | ts '[%Y-%m-%d %H.%M.%.S]'
[2021-08-10 16.16.21.506571] foo
这些带时间戳的行将被写入日志文件,一个用于标准输出,一个用于标准错误,可能通过管道。
选项2
在systemd单元文件中,我们可以将StandardOutput
和StandardError
设置为syslog
,然后将SyslogIdentifier
设置为可以用于rsyslog的字符串,添加时间戳并将最后一行写入日志文件。
不幸的是,我还没有找到一种方法来让systemd区分标准输出和标准输出:它们都只会被发送到rsyslog,而没有任何关于哪个是源的信息。(我知道SyslogLevelPrefix
,但这需要从我们的黑盒应用程序传入的日志消息包含sd-daemon(3)
样式的前缀,这目前是不可行的。)
由于选项2固有的问题,我将进一步讨论选项1。如果您知道syslog方法的变通方法,请务必分享。
所以,命名管道是伟大的,但我不知道如何在系统单元文件中实现它们。我想我必须有两个独立的读取器进程,一个用于标准输出管道,一个用于标准输出管道,因此需要两个新的单元文件。进一步说明:
- 命名管道应该在写入应用程序启动之前就存在。我可以事先用
mkfifo
创建它们,所以这应该不是问题。没有必要用systemd管理管道的存在,除非它使单元文件更简单或更健壮。 - 执行时间戳扩展和日志文件写入的读取进程应该在写入应用程序启动之前启动并运行,我认为这是单元文件中
Before/After
和Requires/Wants
的工作。 - 整个设备应该在写入应用程序或读取进程重启后仍然有效。例如,如果在读进程中使用
cat
,写进程退出,cat
看到EOF并退出,读进程应该恢复并在写进程恢复时准备好。 - 应用程序在各自的读取器进程关闭时写入指定管道的任何日志行都将丢失,但这是可以接受的。
如果写进程具有参数--stdout /path/to/outfifo --stderr /path/to/errfifo
,那么理论上读进程可以像这样简单:
cat /path/to/outfifo | ts '[%Y-%m-%d %H.%M.%.S]' > /var/log/thing/stdout_ts.log
cat /path/to/errfifo | ts '[%Y-%m-%d %H.%M.%.S]' > /var/log/thing/stderr_ts.log
加上logrotate
规则和copytruncate
规则,我们就成功了!只是我不知道如何在systemd单元文件中做到这一点,同时满足上述可重新启动性要求。
如果你能帮助单元文件的构建,或者建议一个替代的方法,我将不胜感激。
journald
可以为您添加时间戳等。
systemd-cat
:
ExecStart=/bin/systemd-cat
--priority=info
--stderr-priority=warning
--identifier=thing
/usr/local/bin/thing
如果你的thing
为优先级级别发出记录器前缀(例如warning
消息的行以<4>
开头),那么你可以使用--level-prefix
标志而不是手动设置优先级。
- 在你的单位使用流记录
- 编写一个自定义拆分器来读取日志,并将它们以正确的级别管道到
systemd-cat
。您可以将其作为thing
将与之通信的服务运行。 - 变体:编写自定义解析器,并通过
sd_journal_send(3)
将解析后的日志直接发送到journald。您可以使用systemd将thing
的输出管道传输到parser
。 - 使用systemd通过
StandardOutput=
和StandardError=
向套接字单元写入标准输出和标准错误。