一、spdlog日志库详解
c++的 spdlog 日志库。这是一款高性能、功能丰富且易于使用的开源日志库。
1. 概述与核心优势
- 高性能: 这是
spdlog的核心设计目标之一。它采用了异步日志记录机制(可选),将日志消息的格式化、写入等耗时操作放入后台线程执行,极大地减少了对主线程性能的影响。其性能通常优于许多其他c++日志库。 - 极简的头部引用: 只需包含一个头文件
#include <spdlog/spdlog.h>即可开始使用基本功能。 - 丰富的特性:
- 支持多种日志级别(trace, debug, info, warn, error, critical)。
- 支持多种日志目标(控制台、文件、系统日志、tcp套接字、udp套接字、rotating files - 滚动文件、daily files - 每日文件、windows debugger 等)。
- 支持自定义格式(使用类似
printf的格式化语法或自定义模式)。 - 支持多线程安全。
- 支持异步日志(默认是同步的,但可以轻松配置为异步)。
- 支持日志刷新策略。
- 支持自定义日志器(logger)和接收器(sink)。
- 支持全局和特定日志器。
- 易用性: api设计简洁直观,学习曲线平缓。
2. 核心概念
- 日志器(logger): 日志记录的核心对象。每个日志器可以有:
- 名称: 用于标识和检索。
- 日志级别: 决定哪些级别的消息会被实际记录。低于设置级别的消息会被忽略。
- 接收器(sinks)列表: 一个或多个输出目标。日志消息会被发送到所有关联的接收器。
- 错误处理器: 当写入失败时的回调函数。
- 格式化器: 控制日志消息的输出格式。
- 接收器(sink): 负责将格式化后的日志消息输出到具体的目标。
spdlog提供了多种内置接收器:stdout_sink_mt,stderr_sink_mt: 输出到标准输出/标准错误(多线程安全)。basic_file_sink_mt: 输出到文件(多线程安全)。rotating_file_sink_mt: 基于文件大小的滚动日志文件。daily_file_sink_mt: 基于时间的每日日志文件(每天或指定时间创建新文件)。syslog_sink_mt: 输出到unix系统日志(如/var/log/syslog)。win_eventlog_sink_mt: 输出到windows事件查看器。msvc_sink_mt: 输出到visual studio调试窗口(仅windows)。tcp_sink_mt,udp_sink_mt: 输出到网络套接字。dist_sink_mt: 分布式接收器,将消息分发到多个其他接收器。null_sink_mt: 丢弃所有日志消息(用于测试或禁用)。ostream_sink_mt: 输出到任意std::ostream。ansicolor_sink: 支持ansi颜色代码的控制台输出。android_sink: android平台日志输出。
- 格式化器(formatter): 定义了每条日志消息的最终输出格式。它使用特定的模式字符串来指定如何组合时间戳、日志级别、日志器名称、线程id、消息内容等。
spdlog默认使用类似以下格式:对应:[%y-%m-%d %h:%m:%s.%e] [%l] [%t] %v
[日期 时间] [级别] [线程id] 消息正文
你可以使用set_pattern自定义格式。 - 日志级别: 定义了日志消息的严重性,从低到高依次为:
spdlog::level::tracespdlog::level::debugspdlog::level::infospdlog::level::warnspdlog::level::errspdlog::level::criticalspdlog::level::off(关闭所有日志)
每个日志器都有自己的级别,低于该级别的消息不会被记录。全局默认级别可以通过spdlog::set_level设置。
3. 基本用法
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h> // 文件接收器
#include <spdlog/sinks/stdout_color_sinks.h> // 带颜色的控制台接收器
int main() {
// 创建一个同时输出到控制台(带颜色)和文件的日志器
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/basic-log.txt", true); // true 表示覆盖而非追加
std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};
auto logger = std::make_shared<spdlog::logger>("my_logger", sinks.begin(), sinks.end());
// 设置日志级别 (全局设置也可以使用 spdlog::set_level)
logger->set_level(spdlog::level::debug);
// 设置输出格式 (可选,这里设置一个简洁格式)
logger->set_pattern("[%h:%m:%s] [%l] %v");
// 注册此日志器,之后可通过名称获取 (可选)
spdlog::register_logger(logger);
// 使用默认全局日志器 (快速开始)
spdlog::info("this is an info message using the default global logger.");
spdlog::warn("this is a warning message.");
// 使用我们创建的日志器
logger->debug("this is a debug message.");
logger->error("some error occurred: {}", 42); // 格式化,类似 `printf`
logger->critical("critical error! exiting...");
// 刷新日志 (确保所有消息都写入目标,特别是对于文件。异步日志器通常自动处理)
logger->flush();
// 通过名称获取已注册的日志器
auto same_logger = spdlog::get("my_logger");
same_logger->info("retrieved logger by name.");
return 0;
}
4. 高级用法与特性
异步日志: 创建异步日志器可以显著提升性能,尤其在高频日志场景。
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>
int main() {
// 创建一个异步日志器工厂 (通常全局一个足够)
auto async_factory = spdlog::create_async<my_logger_name>("async_logger");
// 添加接收器 (例如一个滚动文件接收器)
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("logs/rotating-log.txt", 1024 * 1024 * 5, 3); // 5mb大小,保留3个备份
async_factory->add_sink(rotating_sink);
// 设置级别、格式等
async_factory->set_level(spdlog::level::info);
async_factory->set_pattern("%^[%l]%$ %v"); // %^ %$ 用于颜色控制
// 获取并使用异步日志器
auto async_logger = spdlog::get("async_logger");
async_logger->info("this message is logged asynchronously.");
// 结束时,确保关闭异步日志器 (释放后台线程)
spdlog::drop_all(); // 或者单独 drop("async_logger")
return 0;
}- 自定义格式化: 使用
set_pattern方法。模式字符含义:%v- 实际消息文本。%t- 线程id。%p- 进程id。%n- 日志器名称。%l- 日志级别 (短字符:t, d, i, w, e, c)。%l- 日志级别 (长字符:trace, debug, info, warn, error, critical)。%a- 缩写的星期几。%a- 完整的星期几。%b,%h- 缩写的月份。%b- 完整的月份。%c- 日期时间 (e.g., thu aug 23 15:35:46 2018)。%c- 年份 (2位数)。%y- 年份 (4位数)。%d,%x- 日期 (e.g., 08/23/18)。%t,%x- 时间 (e.g., 15:35:46)。%r- 12小时制时间。%r- 24小时制时间 (hh:mm)。%m- 月份 (数字)。%d- 日 (数字)。%h- 小时 (24小时制)。%i- 小时 (12小时制)。%m- 分钟。%s- 秒。%e- 毫秒。%f- 微秒。%f- 纳秒。%p- am/pm。%z- utc偏移 (e.g., +0200)。%z- 时区名称。%s- 自纪元以来的秒数。%g- 自纪元以来的毫秒数。%u- 自纪元以来的微秒数。%w- 星期几 (数字,0=周日)。%j- 一年中的第几天。%%- 百分号本身。%^- 开始颜色范围。%$- 结束颜色范围。
日志宏: spdlog 提供了方便的宏,可以自动记录 __file__, __line__, __func__ 等信息。
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h> // 对于自定义类型输出可能需要
// 基本宏
log_trace(...);
log_debug(...);
log_info(...);
log_warn(...);
log_error(...);
log_critical(...);
// 带位置信息的宏 (文件名、行号、函数名)
spdlog_trace(...);
spdlog_debug(...);
spdlog_info(...);
spdlog_warn(...);
spdlog_error(...);
spdlog_critical(...);
// 示例
int value = 42;
spdlog_info("the value is {}", value);
spdlog_error("error in function {} at line {}", __func__, __line__);- 自定义接收器: 你可以继承
spdlog::sinks::base_sink类并实现其sink_it和flush方法,将日志输出到任何你想要的地方(如数据库、消息队列等)。 - 错误处理: 可以设置
logger->set_error_handler([](const std::string &msg) { ... })来处理写入失败等错误。 - 自动注册与获取: 使用
spdlog::create或spdlog::create_async创建的日志器会自动注册到全局注册表。之后可以通过spdlog::get("name")获取。使用spdlog::drop_all()或spdlog::drop("name")释放资源(特别是异步日志器的后台线程)。
5. 性能考量与最佳实践
- 评估是否需要异步: 对于性能要求极高的应用,或者日志量非常大的场景,使用异步日志器。但异步日志器在程序异常退出时可能会丢失最后几条日志(虽然可以通过设置队列大小和刷新策略缓解)。
- 合理设置日志级别: 生产环境通常设置为
info或warn,开发调试时可用debug或trace。避免在循环中记录大量低级别日志。 - 选择合适的接收器: 文件写入通常比控制台慢。
rotating_file_sink和daily_file_sink有助于管理日志文件大小和数量。 - 避免昂贵的操作: 在日志语句中尽量避免执行耗时的操作(如复杂计算、io操作),尤其是在同步日志模式下。可以先判断日志级别是否启用
if (logger->should_log(spdlog::level::debug)) { ... }。 - 注意格式化开销: 复杂的格式化字符串或大量参数也会带来开销。
6. 编译与依赖
- 依赖:
spdlog是一个头文件库(header-only),但需要依赖一个c++11兼容的编译器和一个格式化库(默认使用其内置的fmt库分支)。因此,通常只需要包含头文件即可使用。 - 编译选项: 可以通过定义宏来控制
spdlog的行为:spdlog_compiled_lib: 将其编译为静态库(需要包含spdlog.cpp)。spdlog_fmt_external: 使用外部的fmt库(需确保已安装)而不是spdlog内置的版本。spdlog_no_exceptions: 禁用异常(在异常被禁用的环境中使用)。spdlog_no_thread_id: 禁用线程id记录(如果获取线程id开销大)。spdlog_clock_coarse: 使用更快的coarse时钟源(精度较低)。
- 安装: 可以通过包管理器(如vcpkg, conan)、git子模块或直接下载源码放入项目目录。
7. 总结
spdlog 是一个强大、高效且灵活的c++日志库,几乎能满足所有常见的日志记录需求。其简洁的api、出色的性能表现(尤其是异步模式)以及丰富的功能(多接收器、自定义格式、多级别)使其成为c++项目中日志组件的优秀选择。无论是快速原型开发还是大型高性能应用,spdlog 都值得考虑。
二、库的下载与编译
参考我这篇博文:c++ spdlog日志库编译与安装详解
三、示例
1、测试代码
以下是一个较为复杂的c++ spdlog日志库使用示例,包含多线程、异步日志、多接收器(sink)和自定义格式设置:
#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/async.h>
#include <thread>
#include <iostream>
int main() {
try {
// 创建异步日志线程池(队列大小1024,线程数1)
spdlog::init_thread_pool(1024, 1);
// 创建文件接收器(每天轮转,最大3个文件,单个文件最大50mb)
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
"logs/mylog.log", 50 * 1024 * 1024, 3
);
file_sink->set_pattern("[%y-%m-%d %h:%m:%s.%e] [%l] [%t] %v");
// 创建控制台彩色输出接收器
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_pattern("[%y-%m-%d %h:%m:%s.%e] [%^%l%$] %v");
// 创建异步日志器(组合两个接收器)
std::vector<spdlog::sink_ptr> sinks{file_sink, console_sink};
auto async_logger = std::make_shared<spdlog::async_logger>(
"async_logger", sinks.begin(), sinks.end(), spdlog::thread_pool()
);
async_logger->set_level(spdlog::level::trace); // 设置日志级别
// 注册全局日志器
spdlog::register_logger(async_logger);
spdlog::set_default_logger(async_logger);
// 自定义错误处理
spdlog::flush_every(std::chrono::seconds(3)); // 每3秒刷新到磁盘
spdlog::set_error_handler([](const std::string& msg) {
std::cerr << "log error: " << msg << std::endl;
});
// 多线程日志测试
auto log_func = []() {
for (int i = 0; i < 100; ++i) {
spdlog_trace("trace message {}", i);
spdlog_debug("debug message {}", i);
spdlog_info("info message {}", i);
spdlog_warn("warning message {}", i);
spdlog_error("error message {}", i);
spdlog_critical("critical message {}", i);
}
};
std::thread t1(log_func);
std::thread t2(log_func);
t1.join();
t2.join();
// 显式刷新确保所有日志写入
spdlog::shutdown();
} catch (const spdlog::spdlog_ex& ex) {
std::cerr << "log exception: " << ex.what() << std::endl;
}
return 0;
}2、运行结果



3、功能说明:
- 多接收器配置:
- 文件接收器:带轮转功能(每天/按大小)
- 控制台接收器:带彩色级别显示
- 异步日志:
- 使用线程池处理日志写入
- 避免阻塞主线程
- 自定义格式:
- 时间格式:
%y-%m-%d %h:%m:%s.%e - 包含线程id:
%t - 级别高亮:
%^%l%$
- 时间格式:
- 多线程支持:
- 两个线程同时写入日志
- 线程安全保证
- 异常处理:
- 自定义错误回调
- 全局异常捕获
- 性能优化:
- 定期刷新(
flush_every) - 队列大小控制
- 定期刷新(
到此这篇关于c++ spdlog日志库详解的文章就介绍到这了,更多相关c++ spdlog日志库内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论