文章目录
一、syslog 机制原理
1. 基本概念
- syslog 是 Linux 系统中实现日志记录的标准机制,用于系统进程、内核及应用程序将日志信息发送到中央日志服务器或本地文件。
- syslog 三要素:
- 日志生成者:内核模块、用户进程通过特定 API 生成日志。
- 日志传输:通过 Unix 域套接字、网络协议(UDP/TCP)传输日志。
- 日志处理者:syslog 守护进程接收并存储日志。
2. 工作流程
- 日志生成:
- 内核通过
printk
将消息写入内核环形缓冲区。 - 用户进程通过
syslog()
API 发送日志到本地 syslog 套接字。
- 内核通过
- 日志收集:
- syslog 守护进程监听本地套接字(如
/dev/log
)和网络端口(UDP 514/TCP 514)。
- syslog 守护进程监听本地套接字(如
3. 日志优先级
syslog 定义了 8 个日志级别,从高到低为:
LOG_EMERG (0) 系统不可用
LOG_ALERT (1) 必须立即处理
LOG_CRIT (2) 严重情况
LOG_ERR (3) 错误情况
LOG_WARNING (4) 警告情况
LOG_NOTICE (5) 正常但值得注意
LOG_INFO (6) 一般信息
LOG_DEBUG (7) 调试信息
二、syslog 在内核中的使用方法
1. 使用 printk 输出内核日志
基本语法:
printk(KERN_LEVEL "日志消息\n");
示例:
printk(KERN_ERR "磁盘I/O错误: 设备未响应\n");
2. 常用内核日志级别
#define KERN_EMERG "<0>" /* 系统崩溃 */
#define KERN_ALERT "<1>" /* 必须立即处理 */
#define KERN_CRIT "<2>" /* 严重错误 */
#define KERN_ERR "<3>" /* 错误 */
#define KERN_WARNING "<4>" /* 警告 */
#define KERN_NOTICE "<5>" /* 正常但需注意 */
#define KERN_INFO "<6>" /* 信息 */
#define KERN_DEBUG "<7>" /* 调试 */
3. 动态调整日志级别
查看当前日志级别:
cat /proc/sys/kernel/printk
输出格式:
当前控制台日志级别 默认日志级别 最低日志级别 默认控制台日志级别
临时修改日志级别:
echo "8 4 1 7" > /proc/sys/kernel/printk # 提高控制台日志级别
4. 内核模块示例
示例代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static int __init demo_init(void) {
printk(KERN_INFO "syslog_demo: 模块加载成功\n");
return 0;
}
static void __exit demo_exit(void) {
printk(KERN_INFO "syslog_demo: 模块卸载成功\n");
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
三、syslog 在用户空间的使用方法
1. C 语言 API 使用
步骤:
调用
openlog()
初始化连接使用setlogmask(LOG_UPTO(log_level))设置日志掩码(只记录高于或等于指定级别的日志)
使用
syslog()
发送日志调用
closelog()
关闭连接注:
LOG_UPTO
宏是通过位操作来实现的,它会根据传参生成一个掩码。#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */
示例代码:
#include <syslog.h> #define DEFAULT_LOG_LEVEL LOG_INFO int main() { int log_level = DEFAULT_LOG_LEVEL; // 初始化syslog连接 openlog("myapp", LOG_PID | LOG_CONS, LOG_USER); // 设置日志掩码(只记录高于或等于指定级别的日志) setlogmask(LOG_UPTO(log_level)); // 记录不同级别的日志 syslog(LOG_INFO, "程序启动成功"); syslog(LOG_INFO, "hello zgl"); syslog(LOG_WARNING, "配置文件不存在,使用默认配置"); // 带参数的日志 int error_code = 500; syslog(LOG_ERR, "处理请求失败,错误码: %d", error_code); // 关闭连接 closelog(); return 0; }
运行两次,预计打印两次 log 信息。
查看 log :
sudo sh -c 'journalctl SYSLOG_IDENTIFIER=myapp > /var/log/myapp.log'
cat myapp.log
2. 命令行工具
logger 基本语法:
Usage: logger [OPTIONS] [MESSAGE]
Write MESSAGE (or stdin) to syslog
-s Log to stderr as well as the system log
-t TAG Log using the specified tag (defaults to user name)
-p PRIO Priority (numeric or facility.level pair)
- -s:不仅写入系统日志,同时输出到标准错误(stderr)(方便终端查看)
- -t TAG:指定日志的标签(默认是当前用户名)
- -p PRIO:设置优先级,格式为 facility.level(如 user.error)或数字
应用示例:在终端输入以下命令
logger -s "Debug message"
logger -t myapp "zgl_app_test"
logger -p user.info "zgltest"
然后查看日志:
tail -f syslog | grep "Debug message"
tail -f syslog | grep "zgl_app_test"
tail -f syslog | grep "zgltest"
四、在开发板示例
应用层:syslog_demo_user.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#define DEFAULT_LOG_LEVEL LOG_INFO
#define APP_NAME "syslog_demo_user"
int main(int argc, char *argv[]) {
int log_level = DEFAULT_LOG_LEVEL;
// 打开syslog连接
openlog(APP_NAME, LOG_PID | LOG_CONS, LOG_USER);
// 设置日志掩码(只记录高于或等于指定级别的日志)
setlogmask(LOG_UPTO(log_level));
// 记录不同级别的日志
syslog(LOG_EMERG, "TEST syslog This is emerg");
syslog(LOG_ALERT, "ALERT: This is ALERT");
syslog(LOG_CRIT, "CRIT: This is CRIT");
syslog(LOG_ERR, "ERR: This is CRIT");
syslog(LOG_WARNING, "WARNING: This is WARNING");
syslog(LOG_NOTICE, "NOTICE: This is NOTICE");
syslog(LOG_INFO, "INFO: This is INFO");
syslog(LOG_DEBUG, "DEBUG: This is DEBUG");
// 关闭syslog连接
closelog();
return 0;
}
驱动层:syslog_demo_kernel.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
static char *module_name = "syslog_demo_kernel";
module_param(module_name, charp, 0644);
MODULE_PARM_DESC(module_name, "syslog_demo name");
static char *message = "syslog message test";
module_param(message, charp, 0644);
MODULE_PARM_DESC(message, "message text param");
static int __init syslog_demo_init(void)
{
printk(KERN_EMERG "[%s] emerg: syslog_demo module load\n", module_name);
printk(KERN_ALERT "[%s] alert: syslog_demo module load\n", module_name);
printk(KERN_CRIT "[%s] crit: syslog_demo module load\n", module_name);
printk(KERN_ERR "[%s] err: syslog_demo module load\n", module_name);
printk(KERN_WARNING "[%s] warning: module_name = %s\n", module_name, module_name);
printk(KERN_INFO "[%s] info: text = %s\n", module_name, message);
printk(KERN_DEBUG "debug: syslog_demo debug\n");
return 0;
}
static void __exit syslog_demo_exit(void)
{
printk(KERN_ERR "[%s] err: syslog_demo module rmmod\n", module_name);
printk(KERN_WARNING "[%s] warning: syslog_demo module rmmod\n", module_name);
printk(KERN_INFO "info: syslog_demo rmmod finish\n");
printk(KERN_DEBUG "debug: syslog_demo release ipc\n");
}
module_init(syslog_demo_init);
module_exit(syslog_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxxx");
MODULE_DESCRIPTION("Syslog demo");
Makefile:
# 基础配置
a-cockpit_DIR ?= /home/zgl/New/a-cockpit
KERNEL_DIR ?= $(a-cockpit_DIR)/kernel/
ARCH ?= arm
CROSS_COMPILE ?= $(a-cockpit_DIR)/prebuilts/gcc/linux-x86/arm/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
export ARCH CROSS_COMPILE
CROSS_COMPILE_USER ?= $(a-cockpit_DIR)/prebuilts/gcc/linux-x86/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
# 模块定义
obj-m := syslog_demo_kernel.o
USER_MODULE := syslog_demo_user
# 输出目录配置
OUTPUT_DIR ?= $(CURDIR)/output
KERNEL_MODULE_DIR := $(OUTPUT_DIR)/kernel_modules
USER_MODULE_DIR := $(OUTPUT_DIR)/user_binaries
# 创建输出目录
$(shell mkdir -p $(KERNEL_MODULE_DIR) $(USER_MODULE_DIR))
# 内核模块编译目标
all: kernel_module user_module
kernel_module:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
@cp -f *.ko $(KERNEL_MODULE_DIR)/ 2>/dev/null || true
# 用户空间模块编译目标
user_module: $(USER_MODULE).c
$(CROSS_COMPILE_USER)gcc -o $(USER_MODULE_DIR)/$(USER_MODULE) $(USER_MODULE).c
.PHONY: clean
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
rm -rf $(OUTPUT_DIR)
编译生成:syslog 可执行程序和可加载的内核模块
将生成的可执行程序和内核模块传输到开发板:
在开发板上运行可执行程序和加载内核模块,并实时查看 log:
tail -n 0 -f syslog