syslog() 写入 /dev/log 时使用什么格式?

syslog() 写入 /dev/log 时使用什么格式?

我最初想将其发布到 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”中承认的那样)。

相关内容