3. 使用syslog记录日志
任务说明
本案例要求将守护进程的日志输出接入系统syslog服务,并演示不同日志设施(facility)与级别(level)的配置方式。完成目标是:编写一个可独立运行的守护进程程序,它通过openlog/syslog/closelog接口记录启动、运行和退出事件,日志同时包含进程标识与时间信息,并可通过修改代码中的设施常量切换日志分类。
实现方案
程序采用标准daemon化流程(fork、setsid、chdir、umask、重定向标准描述符),随后调用openlog设置进程名与日志选项。日志记录点覆盖三种典型场景:启动成功(LOG_INFO)、周期性心跳(LOG_DEBUG,默认不输出)、收到终止信号(LOG_NOTICE)。设施通过LOG_DAEMON定义,如需切换为LOG_LOCAL0~LOG_LOCAL7或LOG_USER,仅需修改一处宏。
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
static volatile sig_atomic_t g_running = 1;
static void handle_signal(int sig) {
(void)sig;
g_running = 0;
}
static void daemonize(void) {
pid_t pid = fork();
if (pid < 0) { exit(EXIT_FAILURE); }
if (pid > 0) { exit(EXIT_SUCCESS); }
if (setsid() < 0) { exit(EXIT_FAILURE); }
pid = fork();
if (pid < 0) { exit(EXIT_FAILURE); }
if (pid > 0) { exit(EXIT_SUCCESS); }
umask(0);
if (chdir("/") < 0) { exit(EXIT_FAILURE); }
int fd = open("/dev/null", O_RDWR);
if (fd < 0) { exit(EXIT_FAILURE); }
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO) { close(fd); }
}
int main(void) {
daemonize();
/* 设置日志设施与选项 */
openlog("syslog_demo", LOG_PID | LOG_NDELAY, LOG_DAEMON);
/* 注册信号处理 */
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_signal;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
/* 启动日志 */
syslog(LOG_INFO, "daemon started, pid=%d", getpid());
int heartbeat = 0;
while (g_running) {
sleep(5);
heartbeat++;
/* 心跳日志默认级别为DEBUG,需在syslog配置中开启debug才能看到 */
syslog(LOG_DEBUG, "heartbeat #%d", heartbeat);
}
/* 退出日志 */
syslog(LOG_NOTICE, "daemon stopped, total heartbeats=%d", heartbeat);
closelog();
return EXIT_SUCCESS;
}
编译与运行
# 编译
gcc -Wall -O2 -o syslog_demo syslog_demo.c
# 启动守护进程
./syslog_demo
# 查看日志(不同发行版路径可能不同)
tail -f /var/log/syslog # Debian/Ubuntu
tail -f /var/log/messages # RHEL/CentOS
# 停止守护进程
pkill -TERM syslog_demo
验证方法与判定标准
验证步骤:
- 启动程序后,执行
ps -ef | grep syslog_demo确认进程已daemon化(PPID为1,且无终端关联)。 - 查看系统日志文件,应出现一条包含
syslog_demo、进程PID和daemon started的INFO级别记录。 - 执行
pkill -TERM syslog_demo停止进程,日志中应新增一条包含daemon stopped的NOTICE级别记录。 - 默认情况下LOG_DEBUG心跳日志不会写入(取决于
/etc/rsyslog.conf或/etc/syslog.conf中*.debug规则)。如需验证,可临时将守护进程的LOG_DEBUG改为LOG_INFO重新编译运行,观察心跳记录是否周期性出现。
判定标准:启动与停止两条日志均出现在系统日志中,且包含正确的进程名、PID和事件描述,即视为通过。若需切换设施,将代码中LOG_DAEMON改为LOG_LOCAL0并重新编译,日志将归属local0设施,可通过logger -p local0.info "test"对比验证分类效果。
日志设施与级别对照
关键说明
本案例代码完整实现了daemon化与syslog日志记录闭环。日志级别过滤由系统syslog服务配置决定(如/etc/rsyslog.conf中的规则),程序本身不做过滤。若需调整输出详细程度,可修改syslog()调用中的级别参数,或通过setlogmask()在程序内屏蔽低级别日志。验证时注意系统日志写入可能有数秒延迟,属正常现象。