Linux 系统编程-syslog
syslog
这里说的 syslog 仅仅是api,man 3 syslog 有详细的说明。简单来说,如下图所示,应用通过 syslog() 这个调用将日志信息传递给 syslogd(我们可以称为日志收集器)。syslogd 可以根据配置文件(一般位于 /etc/syslog.conf 来决定如何处理日志信息)。比如保存到某个文件、发送到远程主机、显示到控制台等。
1 | app1 app2 app3 (产生日志) |
我们要把日志信息传递给 syslogd 这个守护进程,实际上就是两个进程之间的通信,这里采用的是本地套接字 AF_UNIX这种方式实现。可以参考 [Linux 系统编程-进程通信](./Linux 系统编程-进程通信.md)
基础用法
对使用来说,很简单✨,就只涉及到 3 个 API 调用。
虽然说
syslog协议是可以通过网络将日志信息传递给其他网络地址,但这是syslogd这个守护进程的工作,对应用而言,它只需要将日志信息传递给syslogd,后者会根据配置文件来决定如何处理这些日志信息。
1 |
|
openlog:为当前应用和system logger建立连接!其实就是和syslogd通过本地套接字实现进程间通信。syslog/vsyslog:生成一条日志信息,它将被传递给日志收集器syslogd;closelog:断开和system logger的连接。
openlog
建立本地套接字连接。对
musl这个库而言,本地套接字文件位于/dev/log。
1
2 [root@cvitek]/dev# ls -lh log
srw-rw-rw- 1 root root 0 Jan 1 08:00 log
1 | void openlog(const char *ident, int option, int facility); |
ident:指向的字符串被添加到每条日志信息的前面,一般指定为程序名。如果ident为NULL,则使用程序名称。因为
syslog服务端(一般为syslogd)会接收到很多客户端的消息,所以日志前面一定要带有程序名或其他标识字符串,这样才能确定日志消息的来源。option:指定这个连接的一些特性。就像
open打开文件时可以指定一些flags,用来控制读写权限、非阻塞等等。不过这只是个比喻,与打开文件毫无关系。facility:用于指定记录消息的程序类型,或者说消息来源。这是下面调用syslog时的一个默认值,不过实际调用时仍然可以改为其他类型。与
ident细分来源哪个程序不同,facility是按大类划分,比如来自内核的消息,或者来自用户态的消息。通过这样划分,可以为不同的大类设置不同的输出级别(通过配置文件设置)。比如内核的日志只记录Info级别,而用户态的日志则设置为Debug。
openlog() 的使用是可选的,如果没有调用,它将在第一次调用 syslog() 时自动调用,在这种情况下,ident 将默认为 NULL。
option 类型
LOG_CONS:如果发送到system logger时出现错误,则直接写入系统控制台console。LOG_NDELAY:立即打开连接(通常,在记录第一条消息时打开连接)。这可能是有用的,例如,如果后续的chroot(2)将使日志记录工具内部使用的路径名不可达。LOG_NOWAIT:不要等待在记录消息时可能已经创建的子进程。(GNU C 库不创建子进程,所以这个选项对 Linux 没有影响。)LOG_ODELAY:LOG_NDELAY的逆函数;连接的打开被延迟,直到syslog()被调用。(这是默认值,不需要指定。)LOG_PERROR:将消息记录到stderr。(POSIX.1-2001 或 POSIX.1-2008 中没有。)LOG_PID:在每条消息中包含呼叫者的 PID。
facility 类型
LOG_AUTH: security/authorization messagesLOG_AUTHPRIV: security/authorization messages (private)LOG_CRON: clock daemon (cron and at)LOG_DAEMON: system daemons without separate facility valueLOG_FTP: ftp daemonLOG_KERN: kernel messages (these can’t be generated from user processes)LOG_LOCAL0 through LOG_LOCAL7: reserved for local use。这个可以用户自定义。LOG_LPR: line printer subsystemLOG_MAIL: mail subsystemLOG_NEWS: USENET news subsystemLOG_SYSLOG: messages generated internally by syslogd(8)LOG_USER (default): generic user-level messagesLOG_UUCP: UUCP subsystem
level 日志级别
LOG_EMERG: system is unusableLOG_ALERT: action must be taken immediatelyLOG_CRIT: critical conditionsLOG_ERR: error conditionsLOG_WARNING: warning conditionsLOG_NOTICE: normal, but significant, conditionLOG_INFO: informational messageLOG_DEBUG: debug-level message
syslog
1 | void syslog(int priority, const char *format, ...); |
priority参数由一个facility值和一个level值一起组成。如果优先级中没有用到
facility,则使用openlog()设置的默认值,或者,如果之前没有openlog()调用,则使用默认值LOG_USER。format:一个格式字符串,后面跟该格式所需的任何参数,除了%m将被错误消息字符串strerror(errno)取代。格式字符串不需要包含结束换行符。
比如输出一条 DEBUG 级别的日志如下:
1 | syslog(LOG_USER|LOG_DEBUG, "hello world\n"); |
配置文件
syslog 配置文件用于控制 syslogd 守护进程如何处理日志消息。该文件通常位于 /etc/syslog.conf 中。
syslog 配置文件由一行一行的配置项组成。每一行包含一个或多个关键字组成 + action 组成。关键字之间使用分号;分割,关键字和 action 之间使用空格 分割。
action:指定消息的处理方式。常见的 action 包括:
file:将消息写入文件。remote:将消息发送到远程主机。console:将消息显示在控制台。null:丢弃消息。
配置文件示例
1 | # 将所有 info 级别和内核 debug 级别的消息写入 `/var/log/messages` 文件。 |
配置文件注意事项
syslog配置文件是全局性的,对所有系统上的应用程序都有效。- 可以使用
-f选项指定syslogd守护进程使用的配置文件。
syslogd
syslogd 守护进程参数介绍:
1 | -n Run in foreground |
Output File ⬇️(必须要加引号 "-" 才能到标准输出)
1 | $ syslogd -n -l 8 -O "-" |
Smaller Output 演示结果:
1 | Sep 2 23:50:01 panda user.debug syslog-test[2438]: hello world |
封装
使用 syslog 时需要指定 facility|level 比较麻烦,在 middleware/v2/include/cvi_debug.h 对 syslog 通过宏函数进行了一层封装☠️,在日志消息的基础上添加了“文件名、行号、函数名”,本意是好的,但是名字太长了!TRACE 和 DBG 毫无意义!
1 | CVI_TRACE_LOG(CVI_DBG_INFO, "LT9611_MIPI_Input_Digtal: lt9611 set mipi port = 2\n"); |
相比之下,openvswitch 的命名更加简洁:
1 | VLOG_DBG("xxx"); |
参照此方式,加个 CVI_ 前缀也比较简洁啊!CVI_VLOG_ERR("XXX");
相关资料
- rfc 5424 – The Syslog Protocol
- musl-syslog.h
- musl-syslog.c
- [Linux 系统编程-进程通信](./Linux 系统编程-进程通信.md)
middleware/v2/include/cvi_debug.h
简单宏日志
1 |
|
为什么下面的这个
"\e[0;31m"能决定输出的颜色呢?
这段代码是使用 ANSI 转义码来控制终端文本的颜色和样式。在 ANSI 转义码中,\e用于表示ASCII码中的Escape字符(十进制为27),它在终端中告诉终端模拟器去执行一些特定的操作,比如改变颜色或者样式。在 ANSI 转义码中,数字部分对应不同的颜色和样式。例如,0;31表示红色文字(0是样式代码,31是颜色代码)。同样地,1;33表示亮黄色文字,0;34表示蓝色文字。样式代码和颜色代码分别是啥?
ChatGPT
在 ANSI 转义码中,用于控制文本样式和颜色的代码分别是样式码和颜色码。
样式码:
0:重置所有文本属性,恢复默认状态。1:粗体或者高亮(取决于终端的实现)。2:暗色(并非所有终端都支持)。3:斜体(并非所有终端都支持)。4:下划线。5:闪烁(并非所有终端都支持)。7:反转显示,即前景色变背景色,背景色变前景色。8:隐藏(不可见)。颜色码(前景色):
30:黑色31:红色32:绿色33:黄色34:蓝色35:洋红色(品红)36:青色37:白色这些代码可以通过组合使用来产生不同的效果。例如,
"\e[1;33m"表示亮黄色文本(1代表高亮,33代表黄色);"\e[0;31m"表示普通红色文本(0代表默认样式,31代表红色)。请注意,并非所有终端都支持所有的样式码和颜色码,所以在不同的终端中可能会有不同的效果。
或者更简单一点,不区分日志级别。可以用在简单库的 .c 文件中用于输出错误,警告等日志。
1 | /* LOG MACRO */ |
注意事项
printf 本身是一个不可重入函数,不能在中断、信号处理函数中使用。所以,这里的 mlog 同样不能在这些地方使用。
因为printf函数内部调用了malloc函数,而malloc之前会利用静态mutex对其进行lock。设想这样一种情况,某线程在调用printf(内部执行至对静态mutex加锁操作)时中断,而在信号处理函数中同样调用了printf,则会产生死锁。






