一、引言(Introduction)
在现代 C++ 开发中,日志系统是不可或缺的一环。无论是调试问题、监控运行状态,还是分析性能瓶颈,一个高效、灵活的日志库都能极大提升开发效率和系统可维护性。
今天我们要介绍的
,正是这样一个优秀的开源日志库。它由 Open Source Software 团队维护,专为高性能场景设计,采用现代 C++(C++17 及以上)编写,具备异步日志、低延迟、线程安全等特性,广泛应用于服务器、嵌入式系统和高性能计算领域。
quill
为什么选择
quill
?
quill
极致性能:基于无锁队列(lock-free queue)实现异步日志,极大降低主线程开销。零成本抽象:利用模板和编译期优化,避免运行时性能损失。功能丰富:支持多日志级别、自定义格式器、文件滚动、彩色控制台输出等。轻量易用:头文件为主的设计,集成简单,文档清晰。跨平台支持:兼容 Linux、Windows、macOS 等主流平台。
目标读者
本文面向:
具备基础 C++ 编程能力的开发者正在寻找高效日志解决方案的工程师希望提升项目可维护性和调试效率的技术人员
无论你是开发后端服务、游戏引擎,还是嵌入式应用,
都能成为你项目中的得力助手。
quill
二、库的核心功能与优势(Key Features & Benefits)
主要功能模块
功能 | 描述 |
---|---|
异步日志 | 使用独立日志线程处理写入,主线程几乎无阻塞 |
多日志级别 | 支持 TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL |
彩色输出 | 控制台支持 ANSI 颜色高亮,便于快速识别日志等级 |
文件滚动 | 支持按大小或时间自动滚动日志文件 |
自定义格式器 | 可灵活定义日志输出格式(时间、线程ID、文件名等) |
模块化设计 | 支持多个 logger 实例,分别配置不同行为 |
零依赖(可选) | 可脱离第三方依赖独立编译 |
与其他库对比(简要)
库名 | 性能 | 易用性 | 异步支持 | 学习成本 |
---|---|---|---|---|
|
⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐☆ | 原生支持 | 中等 |
|
⭐⭐⭐⭐☆ | ⭐⭐⭐⭐⭐ | (需配置) | 低 |
|
⭐⭐⭐☆☆ | ⭐⭐☆☆☆ | (同步为主) | 高 |
在性能上表现尤为突出,特别适合对延迟敏感的场景。
quill
优势总结
高性能异步写入现代 C++ 接口,类型安全编译期格式检查(类似
)极简集成,头文件为主活跃维护,社区友好
fmt
三、安装与配置(Installation & Setup)
平台支持
目前
主要支持 Linux 和 macOS,Windows 支持正在完善中。
quill
Linux 安装方式(源码编译)
# 克隆仓库
git clone https://gitee.com/open-source-software_1/quill.git
cd quill
# 创建构建目录
mkdir build && cd build
# 使用 CMake 配置
cmake ..
-DCMAKE_BUILD_TYPE=Release
-DQUILL_BUILD_EXAMPLES=ON
-DQUILL_BUILD_TESTS=OFF
# 编译并安装
make -j$(nproc)
sudo make install
默认安装路径为
。可通过
/usr/local修改。
-DCMAKE_INSTALL_PREFIX=/your/path
构建系统集成
方法一:CMake 中使用
find_package
find_package
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(my_app)
set(CMAKE_CXX_STANDARD 17)
# 查找 quill
find_package(quill REQUIRED)
add_executable(my_app main.cpp)
# 链接 quill
target_link_libraries(my_app quill::quill)
方法二:
add_subdirectory
(推荐用于项目内嵌)
add_subdirectory
# 将 quill 源码放入 third_party/quill
add_subdirectory(third_party/quill)
target_link_libraries(my_app quill::quill)
常见问题与解决方案
问题 | 解决方案 |
---|---|
|
确保已正确安装,或使用
|
编译报错
|
启用 C++17:
|
日志线程未启动 | 确保调用
|
日志文件未生成 | 检查路径权限或使用绝对路径 |
四、快速入门示例(Hello World)
让我们写一个最简单的程序,在 5 分钟内跑通
!
quill
完整代码:
main.cpp
main.cpp
#include "quill/Quill.h"
int main() {
// 1. 启动日志后台线程
quill::start();
// 2. 获取默认 logger
quill::Logger* logger = quill::get_logger();
// 3. 设置日志格式(可选)
logger->set_pattern("%(time) %(level_name) %(logger_name) - %(message)");
// 4. 输出日志
LOG_INFO(logger, "Hello, {}! Today is {}.", "quill", __DATE__);
LOG_WARNING(logger, "This is a warning message.");
LOG_ERROR(logger, "An error occurred with code: {}", 404);
return 0;
}
编译命令
g++ -std=c++17 main.cpp -lquill -lpthread -o hello_quill
注意:必须链接
,因为
-lpthread使用多线程。
quill
运行结果
2025-08-24 20:15:30.123456 [INFO] root - Hello, quill! Today is Aug 24 2025.
2025-08-24 20:15:30.123501 [WARNING] root - This is a warning message.
2025-08-24 20:15:30.123510 [ERROR] root - An error occurred with code: 404
逐行解释
:必须调用,启动后台日志处理线程。
quill::start()
:获取默认 logger 实例。
quill::get_logger()
:自定义输出格式,支持时间、等级、消息等占位符。
set_pattern()
:宏形式的日志输出,支持
LOG_INFO/LOG_WARNING/LOG_ERROR
风格的格式化。
fmt
五、核心API详解(Core API Walkthrough)
1. Logger 管理
// 获取或创建具名 logger
quill::Logger* file_logger = quill::get_logger("file_logger");
// 设置日志级别
quill::set_log_level(quill::LogLevel::Debug);
// 检查是否启用某级别
if (logger->should_log(quill::LogLevel::Trace)) {
LOG_TRACE(logger, "Expensive trace message");
}
2. 自定义格式器
// 创建带颜色的控制台格式器
quill::ConsoleColoredFormatter formatter{"%(ascii_time) %(color)%(level_name)%(reset_color) %(message)"};
// 应用于 logger
logger->set_formatter(&formatter);
3. 文件日志与滚动
// 创建文件 handler
auto file_handler = quill::file_handler("app.log", "w");
// 支持每日滚动
auto daily_handler = quill::daily_file_handler("daily.log", 0, 0); // 每天0点滚动
// 绑定 handler 到 logger
logger->add_handler(file_handler);
4. 异步日志最佳实践
// 推荐:使用异步 logger
quill::AsyncLogger* async_logger = quill::create_async_logger("async", daily_handler);
LOG_INFO(async_logger, "This is async log, very fast!");
最佳实践:
生产环境优先使用
避免在
AsyncLogger宏中执行耗时操作使用
LOG提前判断日志级别
should_log()
六、实际应用场景
场景:构建一个带日志的 HTTP 服务器(伪代码)
假设我们使用
构建服务器,集成
Boost.Asio
进行日志记录:
quill
class HttpServer {
quill::Logger* _logger;
public:
HttpServer() {
_logger = quill::get_logger("HttpServer");
LOG_INFO(_logger, "Server starting on port 8080");
}
void on_request(const Request& req) {
LOG_DEBUG(_logger, "Received request: {} {}", req.method(), req.path());
if (req.path() == "/admin") {
LOG_WARNING(_logger, "Admin access from {}", req.remote_ip());
}
}
~HttpServer() {
LOG_INFO(_logger, "Server shutting down");
}
};
输出效果:
[INFO] HttpServer - Server starting on port 8080
[DEBUG] HttpServer - Received request: GET /index.html
[WARNING] HttpServer - Admin access from 192.168.1.100
结合
,可实现日志按天归档,便于运维排查。
daily_file_handler
七、性能提示与注意事项
性能优化建议
使用
替代同步 logger复用
AsyncLogger
实例,避免频繁创建在循环中使用
logger
提前过滤避免在日志中拼接大字符串或调用复杂函数
should_log()
常见陷阱
问题 | 建议 |
---|---|
忘记调用
|
程序会卡住或崩溃 |
多线程未正确同步 | 本身线程安全,但自定义 handler 需注意 |
日志级别设置过低 | 导致性能下降,建议生产环境用 或更高 |
未处理异常 | 可通过 设置错误回调 |
内存与线程安全
使用无锁队列,线程安全所有
quill
宏均为线程安全调用建议在
LOG_*
开始时调用
main()
,程序结束前无需显式关闭(会自动 flush)
quill::start()
八、高级应用方案:工业级日志系统设计
根据实际项目代码分析,
可以被设计成一个高度结构化、满足工业级需求的日志系统。以下是一个典型的高级使用方案总结:
quill
1. 整体架构设计
单例模式
采用单例模式封装
类,通过
Logger
统一获取日志实例。所有模块通过该单例访问日志服务,确保配置集中、行为一致。
Logger::instance()
双日志系统
常规日志 (General Log):记录系统运行信息、错误、调试消息,同时输出到日志文件和控制台,便于实时监控和问题排查。操作日志 (Operation Log):专门记录用户关键操作(如参数修改、任务执行、权限变更),仅写入独立的
文件,用于审计、追踪和合规性检查。
.trace
2. 初始化流程
Quill 配置优化
quill::Config cfg;
cfg.backend_thread_name = "logger_backend"; // 后台线程命名,便于调试
cfg.backend_thread_sleep_duration = std::chrono::nanoseconds{ 10000000 }; // 10ms 休眠,平衡性能与响应
cfg.enable_console_colours = true; // 启用控制台颜色
cfg.backend_thread_max_transit_events = 1000; // 控制单次处理事件数
cfg.default_queue_capacity = 524'288; // 设置大容量队列,防丢日志
quill::configure(cfg);
quill::start(); // 启动后台日志线程
日志处理器设置
常规日志处理器:使用
,实现5MB大小轮转,最多保留20个历史文件。操作日志处理器:专用
quill::rotating_file_handler("app.log", 5 * 1024 * 1024, 20)
,追加模式写入加密或受保护目录。控制台处理器:
quill::file_handler("./log/.trace/app.trace", "a")
,支持彩色输出,提升可读性。
quill::stdout_handler()
3. 日志记录方式
宏定义简化调用
封装
系列宏,简化调用:
QUILL_LOG_*
#define QUILL_LOG_DEBUG(...) LOG_DEBUG(Logger::instance()->getLogger(), __VA_ARGS__)
#define QUILL_LOG_INFO(...) LOG_INFO(Logger::instance()->getLogger(), __VA_ARGS__)
// ... 其他级别
日志分类管理
利用
创建不同模块的 logger。结合 Qt 的 logging category 机制,实现细粒度控制:
quill::get_logger("module_name")
:普通日志
"message"
:用户操作日志
"operate"
:数据迁移日志
"migrate"
4. 特色功能实现
操作记录分离
通过自定义接口控制日志流向:
quill::Logger* getLogger(const std::string& name, int type = 0);
当
时,返回的 logger 仅绑定操作日志处理器。普通调用返回同时绑定常规日志和控制台的 logger。
type=1
自定义日志格式
handler->set_pattern(
"%(ascii_time) [%(thread)] %(level_name) %(logger_name) - %(message)",
"%D %H:%M:%S.%Qus",
quill::Timezone::LocalTime);
包含时间戳、线程ID、日志等级、模块名、消息体使用本地时区,避免时区混乱
Qt 集成
qInstallMessageHandler(Logger::customMessageOutput);
将
,
qDebug()
,
qWarning()
等 Qt 日志消息桥接到
qCritical()
系统。实现统一日志入口,避免日志分散。
quill
5. 实际应用效果
日志分类记录
系统运行日志:记录启动、关闭、错误、性能指标。用户操作日志:记录“用户A修改了参数X”、“任务Y执行成功”等关键行为。参数修改日志:专门记录配置变更,便于回溯和版本控制。
多线程支持
的异步后台线程确保主线程和业务线程不受日志写入影响。无锁队列设计保证高并发下的线程安全和低延迟。
quill
6. 文件管理策略
日志轮转
使用
防止单个文件过大(如超过5MB)。保留最近20个文件,自动清理最旧日志,节省磁盘空间。
rotating_file_handler
目录结构
./log/
├── app.log # 当前常规日志
├── app.log.1, app.log.2... # 历史日志文件
└── .trace/
└── app.trace # 操作审计日志(可设置权限保护)
总结:该方案充分利用了
的高性能、灵活性和可扩展性,构建了一个结构清晰、职责分离、安全可靠的日志体系,完全满足工业自动化、机器人控制、金融系统等对日志有严格要求的应用场景。
quill
官方资源
Gitee 仓库:https://gitee.com/open-source-software_1/quill官方文档:仓库内
目录
docs/
暂无评论内容