# uvlogplus **Repository Path**: ztwlla/uvlogplus ## Basic Information - **Project Name**: uvlogplus - **Description**: No description available - **Primary Language**: C++ - **License**: MulanPubL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-09-14 - **Last Updated**: 2026-05-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # uvLogPlus + 基于libuv实现日志,接口仿照log4j ## 编译 + windows使用vs2012 + linux使用makefile + Cmake ## demo * https://gitee.com/ztwlla/uvMoudles.git * https://github.com/BigPig0/uvMoudles.git ## 第三方库 * libUV: https://github.com/libuv/libuv.git * cJson: https://github.com/DaveGamble/cJSON.git * pugixml: https://github.com/zeux/pugixml.git ## 设计说明 ### 1. 整体架构 uvLogPlus 采用分层架构设计,主要分为以下几个层次: ``` ┌─────────────────────────────────────────────────────────────┐ │ 接口层 (API Layer) │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ CLog类 │ │ EasyLog模块 │ │ │ │ (高级API) │ │ (简易API) │ │ │ └─────────────┘ └─────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 配置解析层 (Config Layer) │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ JSON解析器 │ │ XML解析器 │ │ │ │ (cJSON) │ │ (pugixml) │ │ │ └─────────────┘ └─────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 核心引擎层 (Core Engine) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ Logger管理 │ │ Appender管理 │ │ 异步事件循环(libuv) │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 输出适配层 (Appender Layer) │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────────┐ │ │ │ 控制台 │ │ 单文件 │ │ 滚动文件(RollingFile) │ │ │ │输出 │ │输出 │ │ 输出 │ │ │ └──────────┘ └──────────┘ └──────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ### 2. 核心组件 #### 2.1 日志级别 (Level) 支持8个日志级别,从低到高依次为: - **All**: 输出所有日志 - **Trace**: 追踪信息 - **Debug**: 调试信息 - **Info**: 普通信息 - **Warn**: 警告信息 - **Error**: 错误信息 - **Fatal**: 严重错误 - **OFF**: 关闭日志输出 #### 2.2 日志器 (Logger) Logger是日志记录的入口,每个Logger具有: - **name**: 日志器名称,用于标识不同的日志类别 - **level**: 日志级别阈值,低于该级别的日志将被过滤 - **appender_ref**: 关联的输出器列表 - **additivity**: 是否继承root日志器的输出器 #### 2.3 输出器 (Appender) Appender负责实际的日志输出,支持三种类型: | 类型 | 说明 | 特性 | |------|------|------| | ConsolAppender | 控制台输出 | 支持彩色输出,区分stdout/stderr | | FileAppender | 单文件输出 | 支持追加/覆盖模式 | | RollingFileAppender | 滚动文件输出 | 支持按文件大小滚动,自动清理旧文件 | ### 3. 异步架构设计 #### 3.1 异步模式工作流程 ``` ┌──────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────┐ │ 业务线程 │────▶│ 日志队列 │────▶│ libuv事件循环│────▶│ 输出设备 │ │ (多线程) │ │ (线程安全) │ │ (独立线程) │ │ │ └──────────┘ └──────────────┘ └─────────────┘ └──────────┘ ``` 1. **业务线程**调用日志接口,格式化日志内容 2. 将日志消息放入**线程安全队列**(使用uv_mutex保护) 3. 通过`uv_async_send`通知libuv事件循环 4. **libuv工作线程**从队列取出日志,执行实际的IO输出 5. 使用**分散/聚集IO**(writev)提高写入效率 #### 3.2 同步模式 同步模式下,日志直接在调用线程中完成输出,适用于: - 简单应用场景 - 调试阶段 - 对实时性要求高的场景 ### 4. 配置文件格式 #### 4.1 JSON格式示例 ```json { "configuration": { "appenders": { "console": { "name": "ConsoleAppd", "target": "SYSTEM_OUT" }, "RollingFile": { "name": "RollingFileAppd", "fileName": "logs/app.log", "Policies": { "size": "10MB", "max": 100 } } }, "loggers": { "root": { "level": "DEBUG", "appender-ref": { "ref": "ConsoleAppd" } }, "commonLog": { "level": "DEBUG", "appender-refs": [ "ConsoleAppd", "RollingFileAppd" ] } } } } ``` #### 4.2 XML格式示例 ```xml ``` ### 5. 滚动文件策略 RollingFileAppender支持以下策略: #### 5.1 基于大小的滚动 - 当文件大小达到设定阈值时,自动创建新文件 - 旧文件重命名为 `filename.1`, `filename.2` ... - 支持设置最大保留文件数,超出时自动删除最旧的 #### 5.2 文件命名规则 ``` app.log (当前写入文件) app.log.1 (最近归档) app.log.2 ... app.log.N (最旧归档,N=max时会被删除) ``` ### 6. 日志格式 文件输出的默认格式为: ``` [线程ID] [时间] [级别]\t[文件名]:[行号]\t[函数名]\t[消息内容] ``` 示例: ``` 00001234 20240115123045 DEBUG\tmain.cpp:00000042\tmain\tApplication started ``` 控制台输出支持彩色显示: - **Fatal**: 紫色 - **Error**: 红色 - **Warn**: 黄色 - **Info**: 绿色 - **Debug/Trace**: 默认颜色 ### 7. 线程安全设计 - **日志队列**: 使用`uv_mutex_t`保护多线程并发访问 - **libuv句柄**: 所有uv句柄操作都在同一个事件循环线程中执行 - **智能指针**: 使用`std::shared_ptr`管理日志消息生命周期 ### 8. 使用方式 #### 8.1 高级API (CLog类) ```cpp #include "uvlogplus.h" // 从配置文件创建 uvLogPlus::CLog* log = uvLogPlus::CLog::Create(false); // 异步模式 // 写入日志 log->Write("loggerName", uvLogPlus::Level::Info, __FILE__, __LINE__, __FUNCTION__, "Message: %s", "test"); delete log; ``` #### 8.2 简易API (EasyLog) ```cpp #include "easylog.h" // 初始化 Log::open(Log::Print::both, uvLogPlus::Level::Debug, "app.log"); // 使用宏写入日志 info("Server started on port %d", 8080); error("Connection failed: %s", errorMsg); // 关闭 Log::close(); ``` ### 9. 文件组织 | 文件 | 说明 | |------|------| | uvlogplus.h/cpp | 核心日志接口实现 | | uvlogpublic.h | 公共头文件,定义日志级别和导出宏 | | uvlogprivate.h | 内部数据结构定义 | | uvlogconf.h/cpp | 配置文件解析器(JSON/XML) | | easylog.h/cpp | 简易日志接口封装 | | uvlogutil.h/cpp | 工具函数 | | uvlogconsolappender.cpp | 控制台输出器实现 | | uvlogfileappender.cpp | 文件输出器实现 | | uvlogrollingfileappender.cpp | 滚动文件输出器实现 | ### 10. 性能特点 - **异步IO**: 基于libuv的高性能异步IO,避免阻塞业务线程 - **批量写入**: 使用分散/聚集IO减少系统调用次数 - **零拷贝**: 尽量减少内存拷贝操作 - **线程安全**: 使用mutex保护日志队列,确保多线程并发安全 ### 11. 性能优化 #### 11.1 批量写入优化 - **批量收集**: 每次最多收集32条日志(文件)或16条日志(控制台)进行批量写入 - **字节限制**: 批量写入总大小限制在64KB以内,避免单次IO过大 - **单次系统调用**: 多条日志合并为一次writev系统调用,显著减少内核态切换 #### 11.2 减少通知频率 - **智能触发**: 仅当日志队列从空变为非空时才触发`uv_async_send` - **pending标记**: 使用`pending`标志避免重复通知,减少事件循环压力 #### 11.3 优化效果 | 优化项 | 优化前 | 优化后 | 提升 | |--------|--------|--------|------| | 系统调用次数 | N次(每条日志1次) | 1次(N条日志批量) | N倍 | | 锁竞争频率 | 每条日志触发 | 批量触发 | 显著降低 | | 内存分配 | 每次new/delete | 批量分配/释放 | 减少碎片 | ### 12. 内部优化实现 #### 12.1 批量写入结构 ```cpp // 批量写入请求 struct BatchWriteReq { Appender *appender; size_t count; // 日志条数 LogMsgReq *entries; // 日志条目数组 }; ``` #### 12.2 批量收集逻辑 ```cpp // 批量取出日志,限制总字节数 size_t total_bytes = 0; while(!msg_queue.empty() && items.size() < BATCH_WRITE_MAX && total_bytes < BATCH_WRITE_BYTES) { auto item = msg_queue.front(); msg_queue.pop_front(); items.push_back(item); total_bytes += 100 + item->msg.size(); // 预估大小 } ``` #### 12.3 分散/聚集IO 使用`uv_fs_write`配合多个`uv_buf_t`,将多条日志的数据一次性写入文件: ```cpp uv_buf_t *bufs = new uv_buf_t[max_bufs]; // 填充多条日志的缓冲区... uv_fs_write(uv_loop, req, file_handle, bufs, buf_count, 0, callback); ```