文章目录
- 3.3.1 spdlog异步日志
- 1. spdlog
- 1. 日志作用
- 2 .同步日志和异步日志区别
- 2. spdlog是什么
- 下载命令:
- 2. spdlog为什么高效
- 3. spdlog特征
- 5. spdlog输出控制
- 6. 处理流程
- 7. 文件io
- 8.问题
- 2. 如何创建logger
- 3. 如何创建sink
- 4. 如何自定义格式化
- 5. 如何创建异步日志
- 6. 刷新策略
3.3.1 spdlog异步日志
1. spdlog
1. 日志作用
- 追踪层序运行状态
- 出现问题提供现场运行信息
- 分析性能信息瓶颈和潜在系统故障
2 .同步日志和异步日志区别
- 同步日志:日志函数调用 会直接执行写入操作(比如写到控制台、文件等)写入完成后函数才返回,日志写入和业务线程在同一线程同步执行
- 异步日志:日志调用将日志消息 推入一个队列 中实际的写入操作由 后台线程 异步完成,业务线程不会阻塞
2. spdlog是什么
下载命令:
# 项目下载
git clone https://github.com/gabime/spdlog.git
#编译
cd spdlog
mkdir build
cd build
cmake ..
make -j
#安装
sudo make install
#或者直接下载
sudo apt-get install libspdlog-dev
2. spdlog为什么高效
- 零成本抽象:通过模板和内联函数实现零成本抽象,确保只有在真正需要的时候才进行日志记录, 日志格式化和消息组装大多在编译期就完成
- 异步消息记录:可以将 日志消息发送到线程池进行处理,从而减少对主线程性能的影响
- 高效格式化:fmt 库进行高效的字符串格式化, 减少了格式化日志消息所需的时间
3. spdlog特征
类别 | 特征描述 |
---|---|
🚀 性能 | 超高性能(同步支持百万级,异步千万级日志/秒) |
⚙️ 格式化 | 基于 fmtlib ,支持 Python 风格的 {} 格式化 |
🌐 多线程支持 | 原生支持多线程安全写入,同步 + 异步两种模式 |
💾 输出灵活 | 支持多个输出目标(控制台、文件、滚动文件、syslog、自定义 sink) |
📦 头文件库 | 完全 头文件-only,无需链接 .lib 或 .so |
🪵 日志级别 | trace、debug、info、warn、error、critical、off 全部支持 |
🕓 滚动日志支持 | 支持按 大小 / 日期 滚动日志文件 |
📚 异常处理 | 默认捕获日志错误(可自定义处理器) |
🧵 线程池 | 异步日志使用轻量级线程池 + 无锁队列,提高吞吐量 |
📖 自定义格式 | 时间戳、线程号、进程号、源文件、函数名、行号……可以全部格式化输出 |
🔐 线程安全 | 日志器默认线程安全;可以创建非线程安全版本(提高性能) |
🔧 易用性 | 接口简洁,易于集成;支持自定义宏、封装自己的日志框架 |
🧪 单元测试友好 | 可重定向输出到 std::ostream ,便于测试验证输出 |
📱 兼容性强 | 支持 C++11、C++14、C++17、Windows、Linux、Mac 全平台 |
5. spdlog输出控制
fmt :格式化输出(C++20)
-
多种日志级别:trace,debug,info,warn,error,critial
-
多种输出目标:可以将日志输出到控制台、文件或通过网络 发送到远程服务器
-
格式化输出:允许用户以结构化的方式 输出日志消息
6. 处理流程
Loggers 负责记录日志消息,Sinks 决定了日志消息的输出位 置,Formatters 负责将日志消息转换为特定格式,Async Logger 异步地将日志消息写入到目标 Sink 中,Registry 用于管 理这些组件
7. 文件io
用户态内核态
8.问题
- 多线程使用日志库,跟同步和异步是否有关联
没有什么关联 - 同一个线程处理的,是不是就是同步的
如果是当前处理程序处理的那就是同步的,如果仅仅是获得完成通知,那就只是协程上的同步 - 为什么需要这么多的日志级别
trace,debug,info,warn,error,critical
左到右,级别越来越高
级别 | 用途描述 |
---|---|
trace | 最细节的日志,追踪变量、执行路径,性能分析时有用(函数级别打印) |
debug | 调试信息,用于开发期观察程序逻辑,如状态变化、调用流程(模块级别状态) |
info | 一般运行信息,系统启动、任务完成、资源加载等(用户可以看到) |
warn | 非致命问题,程序还能运行,比如配置异常、网络抖动(可忽略但需注意) |
error | 程序运行失败了某个功能,比如读取文件失败、通信断开等(需重点关注) |
critical | 致命错误,系统可能崩溃或者需要立刻介入,如系统异常终止(报警级别) |
最低日志级别:高于它的才会显示,低的不显示
部署发布的时候,通常要求日志级别较高
- 分级控制打印:通过
logger->set_level(...)
控制输出级别,避免生产中输出太多调试信息 - 提高性能:只记录重要日志,减少 I/O 开销
- 定位问题快速准确:关键错误更容易定位,细节日志帮助开发回溯问题
2. 如何创建logger
获取实例是线程安全的
- 工厂方法创建
auto logger = spdlog::stdout_color_mt("console");
- 手动创建
auto sink = make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto logger = make_shared<spdlog::logger>("mylogger", sink);
spdlog::register_logger(logger);
- 注册logger,目的为了全局访问
spdlog::get("mylogger")->info("Hello from anywhere!");
3. 如何创建sink
可用的sink
// stdout_color_sink_mt:彩色终端输出(多线程)
// basic_file_sink_mt:写入基础文件
// rotating_file_sink_mt:日志轮转(按文件大小)
// daily_file_sink_mt:每天生成一个日志文件
// null_sink_mt:静默输出(调试用途)
自定义的sink
继承 spdlog::sinks::base_sink
或 sink
,实现 sink_it_()
和 flush_()
方法即可
4. 如何自定义格式化
- set_paattern
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
- 扩展falgs
%v
:日志内容%t
:线程 ID%s
:源文件名%#
:源代码行号%!
:函数名%^ %$
:高亮开头/结尾(用于终端)
- 源文件定位flags
SPDLOG_LOGGER_CALL(logger, spdlog::level::info, "msg");
5. 如何创建异步日志
- 使用async_factory
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
auto logger = spdlog::basic_logger_mt<spdlog::async_factory>("async_logger", "logs/async.txt");
- creat_async,自定义方式
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async.txt", true);
auto logger = spdlog::create_async("async_logger", sink);
- creat_async_nb,非阻塞写法,不阻塞主线程写入日志
auto logger = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>("async_nb_logger", "logs/nb.txt");
- async_logger
// 初始化线程池:队列大小8192,线程数1
spdlog::init_thread_pool(8192, 1);
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/custom.txt", true);
auto async_logger = std::make_shared<spdlog::async_logger>(
"custom_async",
sink,
spdlog::thread_pool(),
spdlog::async_overflow_policy::block);
spdlog::register_logger(async_logger);
- 默认线程池只有一个线程,队列大小8192,
- 优点:低资源占用,保证 FIFO 顺序。
- 缺点: 日志处理速度受限于一个线程
-
处理队列满:async_overflow_policy:block,overrun_oldest
-
多线程线程池,日志输出无法保证有序
6. 刷新策略
- 手动flush
logger->flush();
- 条件flush
spdlog::flush_on(spdlog::level::err);
- 间隔flush
spdlog::flush_every(std::chrono::seconds(3));