POSIX 日志系统
当没有控制终端时,我们不能将错误信息简单地写到标准错误上,也不希望将错误信息写到指定文件中,所以需要有一种集中记录错误信息的方法。syslog 可以将错误信息写到终端,也可以写到指定文件中,并且可以发送给指定主机。
syslog 是一个工业标准的协议,可用来记录设备的日志。在路由器、交换机等网络设备中,系统日志记录系统中任何时间发生的事件,用户可以通过查看系统记录,随时掌握系统状况。操作系统通过系统守护进程或者系统线程记录系统有关事件记录和应用程序运作事件,通过适当的配置,我们还可以实现和运行 syslog 协议的机器间通信,通过分析这些网络行为日志,藉以追踪掌握与设备和网络有关的状况。
syslog 协议提供了一种传递方式,允许一个设备通过网络把事件信息传递给事件信息接收者(也称作日志服务器),由于每个进程、应用程序和操作系统都或多或少地被独立完成,在 syslog 信息内容中会有一些不一致的地方,因此协议中并没有任何关于信息格式或内容的规范。这个协议就是简单地被设计用来传递事件信息。事实上,syslog 信息的传递可以在接收器没有被配置甚至没有接收器的情况下开始。反过来,在没有被清晰配置或者定义的情况下,接收器也可以接收到信息。
几乎所有的网络设备都可以通过 syslog 协议将日志信息通过 UDP 协议传送到远端服务器,远端接收日志服务器必须通过 syslog 守护进程来监听 UDP 端口(514),并根据 syslog.conf 中的配置来处理本机和接收访问系统的日志信息,把指定的事件写入特定档案中,供后台数据库管理和响应之用。也就是说,可以让任何产生的事件都记录到一台或多台服务器上,以便后台数据库可以以离线的方法分析事件。
设备必须通过一些规则来配置,以便显示或者传递事件信息。将日志信息发送到 syslog 接收者的过程一般需要完成以下几个工作:
- 决定哪个信息要被发送。
- 要被发送的级别。
- 定义远程的接收者。
被传输的 syslog 信息的格式主要有三部分组成,分别是 PRI、HEADER、MSG。数据包的长度小于 1024 个字节,PRI 部分必须有 3、4、5 个字符,以“<”开头,然后是一个数字,并以“>”结尾,括号内的数字被称为优先级(Priority),由 facility 和 severity 两个值构成。
表是 facility 名字及对应的说明。
文件 | 描述(进程属性) |
---|---|
affinity | 多核亲和度信息文件 |
objects | 内核对象信息文件 |
tick | 系统时钟嘀嗒信息文件 |
每个信息优先级都包含一个十进制 Severity 参数,描述了各 Severity 等级信息。
块设备 | MTD 设备 |
---|---|
由扇区组成 | 由可擦除的块组成 |
扇区尺寸较小(512 或 1024 Byte) | 可擦除扇区尺寸较大(128KB) |
读扇区和写扇区 | 读块,写块和擦除块 |
坏的扇区被硬件隐藏或重新映射 | 坏块不能被隐藏且需要软件处理 |
扇区的读写次数没有限制 | 块的擦除次数有限制 |
注意:
Priority(优先级)= facility | Severity 值。
标题(HEADER)部分由称为 TIMESTAMP 和 HOSTNAME 的两个域组成,PRI 结尾的“>”会马上跟着一个 TIMESTAMP,任何一个 TIMESTAMP 或者 HOSTNAME 域后面都必须包含一个空格字符。HOSTNAME 包含主机的名称,若无主机名或无法识别则显示 IP 地址。如果一个主机有多个 IP 地址,它通常会使用它传递信息的那个 IP 地址。TIMESTAMP 是本机时间,采用的格式是“Mmm dd hh:mm:ss”表示月日时分秒。
MSG 部分是 syslog 数据包剩下的部分。这通常包含了产生信息进程的额外信息,以及信息的文本部分。MSG 部分有两个域,分别是 TAG 域和 CONTENT 域,TAG 域的值是产生信息的程序或者进程的名字,CONTENT 包含了这个信息的详细内容。传统上来说,这个域的格式较为自由,并且给出一些时间的具体信息。TAG 是一个不允许超过 32 个字符的字母数字字符串,任何一个非字母数字字符都将会终止 TAG 域,并且被假设是 CONTENT 域的开始。
SylixOS 会检查环境变量 SYSLOGD_HOST,如果是有效的 syslog 服务器,例如 SYSLOGD_HOST="192.168.0.1:514",则会将信息发送给 syslog 服务器,如果需要重新确定服务器,则需要首先设置环境变量,然后调用 closelog,这样系统在发送下一条信息时,将会重新确定服务器。
调用下面函数可以连接到一个日志服务器。
#include <syslog.h>
void openlog(const char *ident, int logopt, int facility);
void syslog(int priority, const char *message, ...);
函数 openlog 原型分析:
- 参数 ident 是每条消息的前缀。
- 参数 logopt 是选项标志,如下表所示。
facility | 说明 |
---|---|
LOG_AUTO | 认证相关的日志 |
LOG_KERN | 内核相关的日志 |
LOG_MAIL | 邮件相关的日志 |
LOG_DAEMON | 守护进程相关的日志 |
LOG_USER | 用户相关的日志 |
LOG_SYSLOG | syslog 本身相关的日志 |
LOG_LPR | 打印相关的日志 |
LOG_NEWS | 新闻相关的日志 |
LOG_UUCP | Unix to Unix cp 相关的日志 |
LOG_CRON | 任务计划相关的日志 |
LOG_AUTHPRIV | 权限、授权相关的日志 |
LOG_FTP | FTP 相关的日志 |
LOG_LOCAL0~LOG_LOCAL7 | 用户自定义使用 |
- 参数 facility 是能力参数如下表所示。
文件 | 描述(进程属性) |
---|---|
affinity | 多核亲和度信息文件 |
objects | 内核对象信息文件 |
tick | 系统时钟嘀嗒信息文件 |
函数 syslog 原型分析:
- 参数 priority 是日志优先级,如下表所示。
- 参数 message 是日志消息。
- 参数 ... 是可变参数,可以传递更多信息。
当不需要向日志服务器发送消息时,可调用 closelog 函数将连接断开。
块设备 | MTD 设备 |
---|---|
由扇区组成 | 由可擦除的块组成 |
扇区尺寸较小(512 或 1024 Byte) | 可擦除扇区尺寸较大(128KB) |
读扇区和写扇区 | 读块,写块和擦除块 |
坏的扇区被硬件隐藏或重新映射 | 坏块不能被隐藏且需要软件处理 |
扇区的读写次数没有限制 | 块的擦除次数有限制 |
#include <syslog.h>
void closelog(void);
#include <syslog.h>
int setlogmask(int maskpri);
函数 setlogmask 原型分析:
- 此函数返回先前的掩码值。
- 参数 maskpri 是新的掩码值。
以上函数调用都存在对全局变量的操作,因此在多线程环境下是非线程安全的,为了解决这种问题 syslog 增加了 syslog_data 结构,该结构描述如下:
struct syslog_data {
int log_file;
int connected;
int opened;
int log_stat;
const char *log_tag;
int log_fac;
int log_mask;
};
下面是 syslog_data 结构成员含义:
- log_file:套接字。
- connected:建立了连接。
- opened:打开标志。
- log_stat:选项标志。
- log_tag:TAG 域值。
- log_fac:facility 值。
- log_mask:优先级掩码。
下面是 syslog 可重入版本函数:
#include <syslog.h>
int setlogmask_r(int maskpri, struct syslog_data *data);
void syslog_r(int priority, struct syslog_data *data,
const char *message, ...);
函数 setlogmask_r 原型分析:
- 此函数成功返回先前的掩码,失败返回-1 并设置错误号。
- 参数 maskpri 是新的掩码。
- 输出参数 data 是 syslog_data 结构指针。
函数 syslog_r 原型分析:
- 参数 priority 是 Severity 优先级值,如下表所示。
- 参数 data 是 syslog_data 结构指针,需要应用程序填写相应的值。
- 参数 message 是发送的日志消息。
下面程序展示了 syslog 的使用方法,syslog 接收端程序通过创建 UNIX 域套接字进行通信,syslog 默认的 UNIX 域套接字文件名为“/dev/log”,syslog 日志发送程序中,openlog 函数的选项标志 LOG_CONS 为了调试目的,设置该标志后 syslog 发送的消息将在终端显示。
块设备 | MTD 设备 |
---|---|
由扇区组成 | 由可擦除的块组成 |
扇区尺寸较小(512 或 1024 Byte) | 可擦除扇区尺寸较大(128KB) |
读扇区和写扇区 | 读块,写块和擦除块 |
坏的扇区被硬件隐藏或重新映射 | 坏块不能被隐藏且需要软件处理 |
扇区的读写次数没有限制 | 块的擦除次数有限制 |
#include <stdio.h>
#include <socket.h>
#include <sys/un.h>
#include <syslog.h>
int main (int argc, char *argv[])
{
int sockfd;
struct sockaddr_un unixaddr, unixfrom;
socklen_t fromlen = sizeof(unixaddr);
socklen_t len = fromlen;
int ret;
char buf[LOG_DEFAULT_SIZE] = {0};
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return (-1);
}
unixaddr.sun_family = AF_UNIX;
strcpy(unixaddr.sun_path, "/dev/log");
unixaddr.sun_len = (uint8_t)(SUN_LEN(&unixaddr));
ret = bind(sockfd, (struct sockaddr *)&unixaddr, len);
if (ret < 0) {
perror("bind");
return (-1);
}
while (1) {
ssize_t len;
len = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&unixfrom, &fromlen);
fprintf(stdout, "MSG len: %ld\n", len);
fprintf(stdout, "[R-MSG]: %s", buf);
}
return (0);
}
#include <stdio.h>
#include <syslog.h>
int main (int argc, char *argv[])
{
int i;
openlog("SylixOS-SYSLOG", LOG_CONS | LOG_PID, LOG_USER);
for (i = 0; i < 10; i++) {
syslog(LOG_INFO, "[%d]syslog running...\n", i);
sleep(1);
}
closelog();
return (0);
}
发送端显示:
# ./Syslog_Sender
<14>Dec 29 11:29:00 sylixos SylixOS-SYSLOG[40100f2]: [0]syslog running...
<14>Dec 29 11:29:01 sylixos SylixOS-SYSLOG[40100f2]: [1]syslog running...
<14>Dec 29 11:29:02 sylixos SylixOS-SYSLOG[40100f2]: [2]syslog running...
<14>Dec 29 11:29:03 sylixos SylixOS-SYSLOG[40100f2]: [3]syslog running...
<14>Dec 29 11:29:04 sylixos SylixOS-SYSLOG[40100f2]: [4]syslog running...
<14>Dec 29 11:29:05 sylixos SylixOS-SYSLOG[40100f2]: [5]syslog running...
<14>Dec 29 11:29:06 sylixos SylixOS-SYSLOG[40100f2]: [6]syslog running...
<14>Dec 29 11:29:07 sylixos SylixOS-SYSLOG[40100f2]: [7]syslog running...
<14>Dec 29 11:29:08 sylixos SylixOS-SYSLOG[40100f2]: [8]syslog running...
<14>Dec 29 11:29:09 sylixos SylixOS-SYSLOG[40100f2]: [9]syslog running...
接收端显示:
# ./Syslog_Receiver
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:00 sylixos SylixOS-SYSLOG[40100f2]: [0]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:01 sylixos SylixOS-SYSLOG[40100f2]: [1]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:02 sylixos SylixOS-SYSLOG[40100f2]: [2]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:03 sylixos SylixOS-SYSLOG[40100f2]: [3]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:04 sylixos SylixOS-SYSLOG[40100f2]: [4]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:05 sylixos SylixOS-SYSLOG[40100f2]: [5]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:06 sylixos SylixOS-SYSLOG[40100f2]: [6]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:07 sylixos SylixOS-SYSLOG[40100f2]: [7]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:08 sylixos SylixOS-SYSLOG[40100f2]: [8]syslog running...
MSG len: 74
[R-MSG]: <14>Dec 29 11:29:09 sylixos SylixOS-SYSLOG[40100f2]: [9]syslog running...