Logloom 的日志系统负责以统一的格式记录系统运行过程中的关键信息,具备高可读性、可追溯性和国际化能力,支持用户态和内核态的并发调用。设计目标如下:
- ✅ 支持多级别日志输出(debug, info, warn, error)
- ✅ 支持使用多语言 Key 作为模板进行格式化输出
- ✅ 支持控制台与文件双通道输出,可配置启用/禁用
- ✅ 支持日志文件按大小自动轮转,防止无限增长
- ✅ 接口简洁、线程安全、可嵌入内核模块使用
void log_debug(const char* lang_key, ...);
void log_info(const char* lang_key, ...);
void log_warn(const char* lang_key, ...);
void log_error(const char* lang_key, ...);-
所有接口都使用语言系统的 key(如
LOGLOOM_LANG_AUTH_LOGIN_FAILED) -
内部通过
lang_getf()获取对应语言格式化文本并写入日志 -
如果语言查询失败或格式化出错,将回退为:
[FORMAT ERROR: ...] [KEY: auth.login_failed]
void log_set_level(int level); // 设置全局日志等级
void log_set_output_console(bool enabled); // 开启/关闭控制台输出
void log_set_output_file(const char* filepath); // 设置日志输出文件路径
void log_set_max_file_size(size_t max_bytes); // 设置单文件最大大小,超限轮转- 支持按运行时动态控制输出策略
- 文件输出可设定大小限制(如 1MB),轮转时生成新文件
日志等级按严重性从低到高定义为:
typedef enum {
LOG_LEVEL_DEBUG = 0,
LOG_LEVEL_INFO = 1,
LOG_LEVEL_WARN = 2,
LOG_LEVEL_ERROR = 3
} log_level_t;log_set_level(level)可设置最小输出等级,低于该等级的日志将被忽略。- 默认等级建议为
LOG_LEVEL_INFO
每条日志应包含来源模块的标签,方便归类与分析。例如:
[INFO] [CORE] 初始化完成
[WARN] [LANG] 未找到语言条目:xxx
[ERROR] [IO] 文件写入失败-
模块名称建议使用大写英文字母,不超过 8 个字符
-
标签可通过注册机制或静态宏定义设定,例如:
#define LOG_MODULE_TAG "LANG"
并在日志实现中自动附加
-
支持运行时动态设置当前模块标签(如线程上下文绑定)
-
日志格式统一为:
[LEVEL] [MODULE] 时间戳 消息内容
- 控制台输出通过
stdout(用户态)或printk(内核态)实现 - 可通过
log_set_output_console(bool)开启或关闭 - 每条日志带换行符,适用于实时调试与终端监控
- 通过
log_set_output_file(path)设置目标日志文件路径 - 所有日志写入该文件,自动追加
- 若未设置路径或路径非法,则文件输出禁用
- 支持按文件大小自动轮转,需配置最大文件大小:
log_set_max_file_size(1024 * 1024); // 1MB 限制-
当日志文件超过设定大小时:
- 当前文件重命名为
log.txt.1(最新的备份)或使用时间戳格式log.txt.YYYYMMDD-HHMMSS - 新日志写入
log.txt - 可设置最多保留 N 个历史日志(如 5)
- 当前文件重命名为
两种轮转模式示例:
log.txt ← 当前写入文件
log.txt.1 ← 上一份
log.txt.2 ← 再上一份
log.txt.3 ← 最旧,超过后删除
log.txt ← 当前写入文件
log.txt.20250505-152339 ← 带时间戳的历史文件
log.txt.20250505-151234 ← 更早的历史文件
- 建议使用缓冲写入(
fwrite+fflush控制) - 在内核态需使用
vfs_write或kernel_write封装接口 - 多线程访问需加锁保护(后续设计同步策略)
- 用户态建议使用标准输出缓冲区(如
fwrite()+fflush())以减少 I/O 开销。 - 内核态可采用写入缓冲区 + 定时刷新(可选,需权衡内存占用与实时性)。
- 日志缓冲建议由内部维护,外部不感知,默认开启。
typedef struct {
char buffer[LOG_BUFFER_SIZE]; // 例如 4096 bytes
size_t offset;
bool dirty;
} log_buffer_t;- 每个输出通道(控制台/文件)各维护一个缓冲结构体
- 满或强制刷新(如轮转)时自动写出
-
使用
pthread_mutex_t log_mutex对日志输出临界区加锁 -
保护内容包括:
- log_level 判断
- 多线程写 buffer 和轮转文件
- 控制台与文件输出过程
- 内核模块中使用
spinlock_t或seqlock_t(不可睡眠上下文) - 写入函数需避免阻塞操作,特别是在中断上下文中调用时
- 若允许 sleep 环境,可使用
mutex或rw_semaphore
void log_lock(); // 显式加锁(如多条连续日志)
void log_unlock(); // 解锁- 对外暴露锁接口为可选项,仅在高级用户场景中暴露(如事务式日志)
- 普通日志调用无需感知锁机制,库内部自动封装
-
日志系统支持从配置文件中加载初始设置,如:
log_level = INFO log_file = /var/log/logloom.log log_max_size = 1048576 log_console = true
-
可选配置格式:INI / YAML / JSON(建议 INI 简洁易解析)
-
加载函数接口:
void log_load_config(const char* config_path);-
内核态不支持动态加载外部文件,可使用:
- 编译时宏定义(如
#define LOG_DEFAULT_LEVEL LOG_LEVEL_INFO) - 初始化结构体传参(由上层驱动/模块传入)
- 编译时宏定义(如
- 日志系统允许运行时调整关键参数:
void log_set_level(int level);
void log_set_output_file(const char* filepath);
void log_set_output_console(bool enabled);
void log_set_max_file_size(size_t bytes);- 更改后立即生效,所有后续日志遵循新配置
- 配置修改线程安全,内部自动加锁同步
- 提供查询函数获取当前状态(用于调试、状态导出)
int log_get_level();
bool log_is_console_enabled();
const char* log_get_file_path();
size_t log_get_max_file_size();-
当
lang_getf()返回 NULL 或格式化失败时:-
日志内容自动替换为:
[FORMAT ERROR] Key: <lang_key> -
日志系统不会崩溃,确保健壮性
-
-
文件路径无权限、磁盘满或句柄失效时:
- 控制台输出错误信息
[LOG FILE WRITE FAILED] - 自动禁用文件输出,仅保留控制台日志
- 可在后续尝试
log_set_output_file()重新开启
- 控制台输出错误信息
-
保留当前日志文件继续写入,并在日志中输出一条错误提示:
[LOG ROTATE FAILED] Will continue appending to current log file.
-
无配置文件或配置格式非法时:
- 使用内置默认配置(等级 INFO,控制台启用,文件关闭)
- 输出警告日志
[CONFIG LOAD FAILED, USING DEFAULTS]
- 所有初始化函数(如
log_load_config,log_set_*)具备幂等性 - 多次调用不会重复分配资源或泄露句柄
-
若锁失败(如死锁检测)或内部状态不一致:
- 使用
assert()抛出开发时警告 - 或输出
[INTERNAL LOGGING FAILURE]并忽略该条日志(保守失败)
- 使用
确保日志系统具备以下特性:
- 多语言模板格式化准确无误
- 日志等级、轮转、通道控制逻辑正常
- 并发环境下日志不丢失、无竞态
- 异常处理分支安全降级
| 测试类型 | 内容 | 方法 |
|---|---|---|
| 单元测试 | log_info(), log_set_*() 等接口行为 |
使用断言与 mock 文件系统 |
| 格式化测试 | lang_getf() 多参数组合测试 |
构造 YAML + 对比结果字符串 |
| 并发测试 | 多线程连续写日志轮转 | 使用 pthread 模拟高并发场景 |
| 异常测试 | 写盘失败、格式错误、权限丢失 | 注入故障点、观察 fallback 行为 |
| 性能测试 | 轮转与写入频率性能统计 | 加入时间戳、日志吞吐量计量 |
-
提供
make test-log测试入口,执行:- 生成测试 YAML 模板
- 执行日志轮转与异常验证脚本
- 输出结果统一比对(含 diff、内容校验)
-
CI 集成建议(GitHub Actions/GitLab CI):
- 每次提交时验证日志格式、输出行为、构建可用性
- 对比日志样本与 golden file,检查一致性