![syslog() 写入 /dev/log 时使用什么格式?](https://linux22.com/image/189021/syslog()%20%E5%86%99%E5%85%A5%20%2Fdev%2Flog%20%E6%97%B6%E4%BD%BF%E7%94%A8%E4%BB%80%E4%B9%88%E6%A0%BC%E5%BC%8F%EF%BC%9F.png)
我最初想将其发布到 StackOverflow,因为我的兴趣主要来自编程焦点,但在阅读了历史标签(和它所链接的问题), 我将其发布到 softwareengineering.se深入了解实用/当今的“软件工程”方面。然而,我的问题的根本历史性质是不可避免的,所以现在就到这里了。
我目前正在了解日志记录在 Linux 上的工作原理,并且对似乎未涵盖的实现细节有点困惑RFC 5424或者RFC 3164:用于将数据写入的格式/dev/log
。
长话短说:我试图确定a)“格式/dev/log
”的名称,以及b)其语义的正式指定位置。我无法识别任何 RFC 或其他可引用的参考文献 - 仅代码实现特定规则,没有押韵或理由。
syslog()
虽然我最初对这个主题感兴趣,同时评估与其他日志记录策略(写入 stdout/stderr 和/或文件;发送到其他类型的日志服务器等)的优缺点,但有一次我意识到我完全无法确定“/dev/log
格式”如何/在何处适应更大的情况。关于 syslog 格式或协议的每个问题都讨论了 RFC,并且完全忽略了控制数据如何离开syslog()
并发送到 syslog 服务器的规则。
几乎就像 UNIX 的这个特定角落已经完全消失在木制品中一样,就像不再存在一样……而每天都有数万亿条日志消息使用这种“格式”(?)写入。
因此,这个问题是试图消除这个特定细节的歧义,只是为了这样做,描述格式,并确定可引用的参考文献。
日志行似乎需要以非常特定的方式进行格式化才能被接受(在我的例子中是 systemd-journald - 首先是优先级/设施信息(包含在 中<>
),然后是一个非常特定格式的时间戳,这似乎是通常提到的作为标签,然后是消息:
# 记录器 -s 你好 <13>3月5日14:04:11 i336:嗨 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:04:11.430504+1100 h0nk i336[2505]:嗨
# 记录器-s-t 标签你好 <13>3月5日 14:04:37标签: 你好 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:04:37.050891+1100 h0nk标签[3151]:嗨
# 记录器-s-t 标签 -我你好 <13>3月5日 14:04:40标签[3248]: 你好 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:04:40.278630+1100 h0nk标签[3248]: 你好
(-s
导致logger
复制它发送到 stderr 的内容;-t tag
将标签logger
使用从我的用户名更改为我指定的任何名称;并-i
添加 PID。)
我注意到systemd-journald
解析并丢弃提供的时间戳和 PID(如果提供)并确定此信息本身(这就是 PID 始终存在的原因,以及为什么我能够在short-iso-precise
没有提供时请求毫秒):
# 回显'<13>1 月 1 日 00:00:00测试[1234]:嗨'| ncat -uU /dev/log #journalctl-qn1 3 月 5 日 14:06:12h0nk 测试[5593]: 你好 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:06:12.538712+1100 h0nk 测试[5593]:嗨
从安全角度来看,这很有意义。
然而,我发现如果我偏离轻微地从格式
<p>Mmm _d HH:MM:SS tagwithnospaces: message
然后一切都会很快变糟。
看起来和logger
选项专门用于通过 UDP 或 TCP 连接到系统日志服务器。发送数据时使用这些选项会产生灾难性的结果。--rfc5424
--rfc3164
/dev/log
RFC 5424 格式(标头定义在第6节)从一开始就爆炸了:
# logger -s --rfc5424 hi
<13>1 2021-03-05T15:05:04.773304+11:00 h0nk i336 - - [timeQuality tzKnown="1" isSynced="1" syncAccuracy="648500"] hi
# journalctl -qn1 -o short-iso-precise
2021-03-05T15:05:04.773384+1100 h0nk logger[29306]: 1 2021-03-05T15:05:04.773304+11:00 h0nk i336 - - [timeQuality tzKnown="1" isSynced="1" syncAccuracy="648500"] hi
奇怪的是,虽然 RFC 3164 格式(定义在第4.1.2节) 很大程度上类似于...无论logger
使用什么格式没有指定 RFC 选项,即使添加 RFC 3164 的主机名字段也足以破坏事情:
# 记录器-s --rfc3164 你好 <13>3月5日14:20:51 h0nk i336:嗨 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:20:51.638518 + 1100 h0nk 未知[27148]:h0nk i336:嗨 ^哦
附带问题:我猜 RFC 格式保留了领先的<nnn>
优先级字段,以便状态信息(“紧急”、“关键”等)始终正确传输。它是否正确?
日期本身似乎也是一个特别敏感的值。即使稍微改变它也会立即导致systemd-journald
将整行视为已损坏:
# echo '<13>1 月 1 日 00:00:00 测试[1234]:嗨' | ncat -uU /dev/log ^两个空格 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:17:04.484309+1100 h0nk 测试[21585]:嗨
# echo '<13>1 月 1 日 00:00:00 测试[1234]:嗨' | ncat -uU /dev/log ^一个空间 # Journalctl -qn1 -o 短 ISO 精确 2021-03-05T14:06:23.414986+1100 h0nk ncat[5877]: Jan 1 00:00:00 测试[1234]: hi
有趣的是,BusyBox 的 syslogd 还仔细检查了日期(系统日志.c:829),以对某些字符的位置做出精确的、硬编码的假设:
/* Jan 18 00:11:22 消息... */ /* 01234567890123456 */ if (len >= 16 && msg[3] == ' ' && msg[6] == ' ' && 消息[9] == ':' && 消息[12] == ':' && 消息[15] == ' ' ){
(Busybox syslogd 还提到了 15 个字符长的时间L286.)
我很感兴趣为什么日期似乎需要如此仔细地指定。
我发现 glibc 的实现syslog()
(系统日志.c:223)有洞察力的研究:
它使用格式“
%h %e %T
”%h
->%b
;%b
= 根据区域设置的缩写月份名称%e
= 月份中的某天,带前导空格%T
= 时间就像%H:%M:%S
- 时间戳后面的尾随空格与 BusyBox syslogd 的
msg[15] == ' '
检查(!)相匹配(完全令人麻木)
它使用 (glibc-internal?) 函数
strftime_l()
,该函数(如时间.h:101),“从提供的语言环境而不是全局语言环境中获取信息";strftime_l()
此处已通过_nl_C_locobj_ptr
(定义于locale.h:17),这是一个内部指针_nl_C_locobj
(定义在xlocale.c:34) 对 C 语言环境定义的(也是内部的)glibc 全局引用。
logger
根据调用的模式使用不同的标头格式化函数:syslog_rfc3164_header()
,syslog_rfc5424_header()
, 和syslog_local_header()
。其中重要的部分syslog_local_header()
是:
if (ctl->pid) snprintf(pid, sizeof(pid), "[%d]", ctl->pid); ... xasprintf(&ctl->hdr, "<%d>%s %s%s: ", ctl->pri, rfc3164_current_time(), ctl->标签,pid);
又是这个格式。优先级、时间、标签、PID。
rfc3164_current_time()
是 的包装器gettimeofday()
,localtime()
它捆绑了缩写的英文月份名称列表,作为 glibc 语言环境舞蹈的便携式替代品。
鉴于 RFC 3164 的日期格式与“本地”“格式”中使用的日期非常相似/dev/log
,因此重用日期格式化函数非常有意义。但值得注意的是,这是唯一可以重用的,因为“本地”格式作为一个整体仍然与 RFC 3164 格式不同。
问题:该/dev/log
格式的来源是什么?
logger
的“本地”规范化是我能够用/dev/log
与网络/RFC 格式不同的名称来消除格式歧义的最接近的规范化。我没有发现任何其他地方试图命名这种区别,它只是使用它。
我还没有测试过其他 syslog 守护进程(如 rsyslog、syslog-ng 等),并且我不知道它们是否接受 RFC 格式的文本行/dev/log
,但考虑到 BusyBox 的准确性,如果这是这样,我不会感到惊讶某种违规行为...
...但与此同时,什么标准或政策首先将这种行为定义为违规?
我的印象是这是“按照惯例”的标准,并且由于在实践中使用了精确特定的解析器,因此明确的批准已被回避。
这个假设正确吗?
注意。我偶然发现/dev/log
这是对文件本身意义问题的一个很好的回答,它指出在 chroot 中运行的应用程序可能会发送到相对/dev/log
路径。这是一个很好的观点,我/dev/log
非常使用“格式”,因为需要给它正确的名称。
答案1
也许man rsyslogd
给出了一个重要的提示:
/dev/log
The Unix domain socket to from where local syslog messages are
read.
笔记 ”当地的syslog messages”。这意味着 RFC 3164 的 HOSTNAME 丢失,但其余部分似乎遵循该格式。此外,syslog 守护程序似乎在记录消息之前添加了丢失的主机名字段。
如果您的strace
应用程序创建系统日志消息,您可以看到类似这样的内容(来自strace -f logger -t demo foobar
):
...
socket(AF_UNIX, SOCK_DGRAM, 0) = 3
connect(3, {sa_family=AF_UNIX, sun_path="/dev/log"}, 110) = 0
...
sendto(3, "<13>Apr 28 11:34:21 demo: foobar", ...) = 32
...
确切的作用systemd-journald
没有记录,而且很可能不是标准的(正如 Lennart Poettering 在邮件列表主题“Q:系统日志中的非 ASCII”中承认的那样)。