本文最后更新于 28 天前,其中的信息可能已经有所发展或是发生改变。
参考文档
RFC 3164: The BSD Syslog Protocol (rfc-editor.org)
概述
syslog是一个协议规范,需要有server端和client端,由server端收集日志并记录
Syslog服务端通常是syslogd或更现代的rsyslog、syslog-ng或systemd-journald)负责接收来自客户端的日志消息,存储它们,并可能根据配置转发到其他日志服务器或应用程序。服务端是syslog的核心,它处理日志的存储、转发和管理Syslog客户端是指发送日志消息的应用程序、服务或内核。它将日志消息发送给syslog服务端(日志守护进程)。客户端通过syslog()函数或者类似工具将日志消息发送到syslogd或其他日志守护进程
日志信息的获取
syslog的获取主要通过以下的几种方式

/dev/log句柄/dev/log是一个特殊的Unix域套接字,用于在用户空间应用程序和系统日志守护进程之间传递日志消息。它是syslog系统的一部分,允许应用程序(包括内核和用户空间程序)将日志消息传递给日志守护进程(如rsyslog、syslog-ng或systemd-journald),这些日志守护进程再将消息存储到日志文件中或转发到其他位置 通常,任意进程都可以使用syslog(3)库函数来记录消息。这个函数会使用传入的参数以标准的格式构建一条消息,然后将这条消息写入/dev/log socket以供syslogd读取/dev/log中的消息的另一个来源是Kernel Log daemon klogd,它会收集内核日志消息(内核使用printk()函数生成的消息)。这些消息的收集可以通过两个等价的Linux特有的接口中的一个来完成(即/proc/kmsg文件和syslog(2)系统调用),然后使用syslog(3)库函数将它们写入/dev/log查询Linux源码可以在devices.rst文件中找到关于这个句柄的描述
Non-transient sockets and named pipes may exist in /dev. Common entries are:
=============== =============== ===============================================
/dev/printer socket lpd local socket
/dev/log socket syslog local socket
/dev/gpmdata socket gpm mouse multiplexer
=============== =============== ===============================================
UDP 514端口514号端口是syslog使用的默认端口号。用于接收从其他计算机上发送的syslog日志消息/dev/kmsg句柄/dev/kmsg是一个可读写的字符设备文件,且允许多个进程进行读取,每个进程都能得到完整的数据流。 而且由于它是可读写的,因此你还可以插入消息到内核日志中 查询Linux源码可以在dev-kmsg文件中找到关于这个句柄的描述,这里只摘抄一小段,详细描述需要查看源码Description: The /dev/kmsg character device node provides userspace access to the kernel's printk buffer./proc/kmsg句柄/proc/kmsg是一个只读的伪FIFO文件,且里面的消息只能被读取一次。当多个进程同时尝试读取该文件时,每个进程将只能读取到一部分日志内容
busybox实现
分析busybox源码可以在syslogd.c文件中看到,关于syslog的实现,在此仅列出关键的核心流程
函数调用栈
syslogd_main
->do_syslogd
->signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
->signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
->create_socket()//创建一个socket绑定到/dev/log上,并通过xmove_fd函数将这个sockrt绑定到STDIN_FILENO上,用于后续读取
->strcpy(sunx.sun_path, _PATH_LOG);//define _PATH_LOG /dev/log
->xmove_fd(create_socket(), STDIN_FILENO)
/*从STDIN_FILENO中读出内容,并将内容记录为syslog
bb_got_signal信号被绑定到SIGTERM和SIGINT */
->while(!bb_got_signal) {
read(STDIN_FILENO, recvbuf, MAX_READ - 1);
split_escape_and_log(recvbuf, sz)
}
关键函数
xmove_fd函数
实际上是对dup2的一层封装
void FAST_FUNC xdup2(int from, int to)
{
if (dup2(from, to) != to)
bb_simple_perror_msg_and_die("can't duplicate file descriptor");
}
void FAST_FUNC xmove_fd(int from, int to)
{
if (from == to)
return;
xdup2(from, to);
close(from);
}
do_syslogd函数
syslogd的核心函数,主要实现读取/dev/log句柄的内容并将其记录为syslog
笔者注:下文仅保留核心逻辑,删除部分不影响阅读的部分
static void do_syslogd(void)
{
#define recvbuf (G.recvbuf)
signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
signal(SIGHUP, SIG_IGN);
xmove_fd(create_socket(), STDIN_FILENO);
while (!bb_got_signal) {
ssize_t sz;
read_again:
sz = read(STDIN_FILENO, recvbuf, MAX_READ - 1);
while (1) {
if (sz == 0)
goto read_again;
if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
break;
sz--;
}
if (!ENABLE_FEATURE_REMOTE_LOG || (option_mask32 & OPT_locallog)) {
recvbuf[sz] = '\0';
// 将读取到的内容作为syslog记录
split_escape_and_log(recvbuf, sz);
}
} /* while (!bb_got_signal) */
timestamp_and_log_internal("syslogd exiting");
remove_pidfile_std_path_and_ext("syslogd");
kill_myself_with_sig(bb_got_signal);
}
核心思想
do_syslogd函数的核心流程非常简单
- 配置信号量
- 配置
socket用于读取/dev/log,并将读取的结果作为syslog记录 - 循环读取
/dev/log的内容直到,有外部信号量使syslogd进程退出