我们知道守护进程完全脱离终端控制,即守护进程不能将错误信息直接输出到控制终端上。例如, printf()
打印不能显示到终端。因此,该如何通过输出信息对守护进程进行程序调试是一个问题。这里介绍使用 syslog
服务,将程序的出错信息输入系统日志,从而可以直观的看到程序的问题所在。在不同的 Linux
发行版中,系统日志文件路径可能有所不同。例如,可能是 /var/log/syslog
。
syslog
是 Linux
的系统日志管理服务,通过守护进程 syslogd
来维护。该守护进程在启动时会读取配置文件 /etc/syslog.conf
。该文件决定了不同类型的消息发送到何处。例如,紧急消息可被送到系统管理员并在控制台上显示,而警告信息则可被记录到一个文件中。系统日志文件只能由管理员用户查看。
提示
Ubuntu 20.04
版本中的配置文件为: /etc/rsyslog.conf
,其中又将部分配置拆分到了 /etc/rsyslog.d/
文件夹下,例如: /etc/rsyslog.d/20-ufw.conf
该机制提供了 3
个 syslog
相关函数, openlog()
函数用于打开系统日志服务的一个连接; syslog()
函数用于向日志文件中写入消息,并可以规定消息的优先级、消息输出格式等; closelog()
函数用于关闭系统日志服务的连接。
#include <syslog.h> void openlog(const char *ident, int option, int facility); void syslog(int priority, const char *format, ...); void closelog(void);点击复制复制失败已复制
openlog() 函数
openlog()
函数中,参数 ident
表示要向每个消息加入的字符串,通常为程序的名称;参数 option
用来指定 openlog()
函数如何控制消息的标志;参数 facility
用来指定程序发送的消息类型。
option
参数配置如下表所示:
具体参数选项 | 表示含义 |
LOG_CONS |
如果消息无法发送到系统日志,则直接输出到系统控制终端 |
LOG_NDELAY |
立即打开系统日志服务的连接(通常,直接发送第一条消息时才打开连接) |
LOG_PERROR |
将消息同时发送到 stderr |
LOG_PID |
在每条消息中包含进程的 PID |
facility
参数配置如下:
具体参数选项 | 表示含义 |
LOG_AUTHPRIV |
安全/授权消息 |
LOG_CRON |
时间守护进程 |
LOG_DAEMON |
其他系统守护进程 |
LOG_KERN |
内核信息 |
LOG_LOCAL0~7 |
保留供本地使用 |
LOG_LPR |
行打印机子系统 |
LOG_MAIL |
邮件子系统 |
LOG_NEWS |
新闻子系统 |
LOG_SYSLOG |
syslog 内部产生的信息 |
LOG_USER |
一般使用者等级信息 |
LOG_UUCP |
UUCP 子系统 |
syslog()函数
syslog()
函数中,参数 priority
用来指定消息的等级。参数 format
等同于 printf()
函数,即格式化输出。 priority
参数配置如下所示:
具体参数选项 | 表示含义 |
LOG_EMERG |
系统无法使用 |
LOG_ALERT |
需要立即采取措施 |
LOG_CRIT |
有重要情况发生 |
LOG_ERR |
有错误发生 |
LOG_WARNING |
有警告发生 |
LOG_NOTICE |
正常情况,但也有重要情况 |
LOG_INFO |
信息消息 |
LOG_DEBUG |
调试消息 |
示例
下面将Linux守护进程中的示例程序用 syslog
服务进行重写,从而选择将程序出错的调试信息输入系统日志文件,便于追踪错误,如下所示:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <syslog.h> #include <unistd.h> int main(int argc, const char *argv[]) { pid_t pid, sid; int i, fd; char *buf = "This is a Daemon\n"; pid = fork(); // 第一步 if (pid < 0) { perror("fork error"); return -1; } else if (pid > 0) { exit(0); // 父进程退出 } else { openlog("daemon_syslog", LOG_PID, LOG_DAEMON); if ((sid = setsid()) < 0) { // 第二步 syslog(LOG_ERR, "%s\n", "setsid"); exit(1); } if ((sid = chdir("/")) < 0) { // 第三步 syslog(LOG_ERR, "%s\n", "chdir"); exit(1); } umask(0); // 第四步 for (i = 0; i < getdtablesize(); i++) { close(i); // 第五步 } if ((fd = open("/tmp/daemon.log", O_CREAT | O_WRONLY | O_TRUNC, 0600)) < 0) { syslog(LOG_ERR, "open"); return -1; } syslog(LOG_INFO, "%s\n", "open daemon.log"); while (1) { write(fd, buf, strlen(buf)); sleep(3); } close(fd); closelog(); } return 0; }点击复制复制失败已复制
编译并运行,查看 /var/log/syslog
即可看到日志:
$ cat syslog Oct 11 19:45:03 iric-MS-7B89 daemon_syslog[527770]: open daemon.log