守护进程存在的一个问题是如何处理出错消息。因为它本就不应该有控制终端,所以不能只是简单地写到标准错误上。我们不希望所有守护进程都写到控制台设备上,因为在很多工作站上控制台设备都运行着一个窗口系统。我们也不希望每个守护进程将它自己的出错消息写到一个单独的文件中。对任何一个系统管理人员而言,如果要关心哪一个守护进程写到哪一个记录文件中,并定期地检查这些文件,那么一定会使他感到头痛。所以,需要有一个集中的守护进程出错记录设施。
BSD syslog 设施是在伯克利开发的,广泛应用于 4.2BSD。从 BSD 派生的很多系统都支持syslog。在 SVR4 之前,SystemV 中从来没有一个集中的守护进程记录设施。在 Single UNIX Specification的XSI扩展中包括了syslog函数。
自4.2BSD以来,BSD的syslog设施得到了广泛的应用。大多数守护进程都使用这一设施。图13-2显示了syslog设施的详细组织结构。
图13-2 BSD的syslog设施
有以下种产生日志消息的方法。
- 内核例程可以调用
log
函数。任何一个用户进程都可以通过打开open
并读取read
/dev/klog
设备来读取这些消息。因为我们无意编写内核例程,所以不再进一步说明此函数。 - 大多数用户进程(守护进程)调用
syslog(3)
函数来产生日志消息。我们将在下面说明其调用序列。这使消息被发送至UNIX域数据报套接字/dev/log
。 - 无论一个用户进程是在此主机上,还是在通过TCP/IP网络连接到此主机的其他主机上,都可将日志消息发向UDP端口514。注意,
syslog
函数从不产生这些UDP数据报,它们要求产生此日志消息的进程进行显式的网络编程。
关于UNIX域套接字以及UDP套接字的细节,请参阅Stevens、Fenner和Rudoff[2004]。
通常,syslogd
守护进程读取所有3种格式的日志消息。此守护进程在启动时读一个配置文件,其文件名一般为/etc/syslog.conf
,该文件决定了不同种类的消息应送向何处。例如,紧急消息可发送至系统管理员(若已登录),并在控制台上打印,而警告消息则可记录到一个文件中。
该设施的接口是syslog
函数。
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
int setlogmask(int maskpri);
//返回值:前日志记录优先级屏蔽字值
调用openlog
是可选择的。如果不调用openlog
,则在第一次调用syslog
时,自动调用openlog
。调用 closelog
也是可选择的,因为它只是关闭曾被用于与syslogd
守护进程进行通信的描述符。
调用openlog
使我们可以指定一个ident
,以后,此ident
将被加至每则日志消息中。ident
一般是程序的名称(如cron
、inetd
)。option
参数是指定各种选项的位屏蔽。图13-3介绍了可用的option
(选项)。若在Single UNIX Specification的openlog
定义中包括了该选项,则在XSI列中用一个黑点表示。
图13-3 openlog
的option
参数
openlog
的facility
参数值选取自图13-4。注意,SingleUNIX Specification只定义了facility
所有参数值中的一个子集,该子集一般只能用在一个给定的平台上。设置facility
参数的目的是可以让配置文件说明,来自不同设施的消息将以不同的方式进行处理。如果不调用openlog
,或者以facility
为0
来调用它,那么在调用syslog
时,可将facility
作为priority
参数的一个部分进行说明。
调用syslog
产生一个日志消息。其priority
参数是facility
和level
的组合,它们可选取的值分别列于facility
(见图13-4)和level
(见图13-5)中。level
值按优先级从最高到最低依次排列。
图13-4 openlog的facility参数
图13-5 syslog中的level(按序排列)
将format
参数以及其他所有参数传至vsprintf
函数以便进行格式化。在format
中,每个出现的%m
字符都先被代换成与errno
值对应的出错消息字符串(strerror)。
setlogmask
函数用于设置进程的记录优先级屏蔽字。它返回调用它之前的屏蔽字。当设置了记录优先级屏蔽字时,各条消息除非已在记录优先级屏蔽字中进行了设置,否则将不被记录。注意,试图将记录优先级屏蔽字设置为0并不会有什么作用。
很多系统也将logger(1)
程序作为向syslog
设施发送日志消息的方法。虽然Single UNIX Specification 没有定义任何可选参数,但某些实现允许将该程序的可选参数指定为 facility
、level
和ident
。logger命令是专门为以非交互方式运行的需要产生日志消息的shell脚本设计的。
实例
在一个(假定的)行式打印机假脱机守护进程中,可能包含有下面的调用序列:
openlog("lpd", LOG_PID, LOG_LPR);
syslog(LOG_ERR, "open error for %s: %m", filename);
第一个调用将ident
字符串设置为程序名,指定该进程ID要始终被打印,并且将系统默认的facility
设定为行式打印机系统。对 syslog
的调用指定一个出错条件和一个消息字符串。如若不调用openlog
,则第二个调用的形式可能是:
syslog(LOG_ERR | LOG_LPR, "open error for %s: %m",filename);
其中,将priority
参数指定为level
和facility
的组合。
除了syslog
,很多平台还提供它的一种变体来处理可变参数列表。
#include <syslog.h>
#include <stdarg.h>
void vsyslog(int priority, const char *format, va_listarg);
本书说明的所有4种平台都提供vsyslog,但Single UNIX Specification中并不包括它。注意,如果要使它的声明对应用程序可见,可能需要定义一个额外的符号,例如,在FreeBSD中定义__BSD_VISIBLE或在Linux中定义__USE_BSD。
大多数syslog
实现将使消息短时间处于队列中。如果在此段时间中有重复消息到达,那么syslog
守护进程不会把它写到日志记录中,而是会打印输出一条类似于“上一条消息重复了N次”的消息。