当前位置: 首页 > news >正文

spdlog高性能日志库

一、spdlog 概述

1. 定位与作用

spdlog 是一款高性能 C++ 日志库,旨在为开发者提供高效、灵活的日志记录解决方案。其核心作用包括:

  • 追踪程序状态:记录程序运行时的关键节点、变量状态等信息。
  • 故障排查:在问题发生时提供详细的现场数据(如调用栈、错误参数),辅助定位 bug。
  • 性能分析:通过日志分析程序的性能瓶颈(如耗时操作、资源竞争)。
  • 系统监控:记录潜在故障信号(如内存泄漏、异常流量),支持预警机制。
2. 核心优势
  • 极致性能
    • 零成本抽象:通过模板和内联函数实现,仅在日志级别启用时执行实际记录操作。
    • 异步日志:将日志消息异步提交到线程池处理,避免阻塞主线程。
    • 高效格式化:集成 fmt 库,支持快速字符串格式化,减少 CPU 开销。
  • 低资源占用:内存管理优化,高负载下仍保持稳定。
  • 灵活配置:支持多日志级别(trace/debug/info/warn/error/critical)、多输出目标(控制台、文件、远程服务器)及自定义格式。

二、spdlog 核心组件与架构

1. 关键组件
组件功能描述
Logger日志记录的入口,负责生成日志消息,关联 Sink 和 Formatter。
Sink定义日志输出目标(如文件、控制台、网络),支持自定义输出逻辑。
Formatter负责将日志消息格式化为指定结构(如 JSON、纯文本),支持自定义格式字符串。
Async Logger通过线程池异步处理日志消息,减少主线程延迟。
Registry全局管理所有 Logger,支持通过名称注册和获取 Logger,方便全局访问。
2. 处理流程
  1. 同步日志:Logger 直接调用 Sink 的接口,将格式化后的消息写入目标(如文件 I/O)。
  2. 异步日志
    • Logger 将消息放入异步队列,由线程池中的工作线程负责处理。
    • 线程池默认配置:1 个线程,队列大小 8192,可通过 queue_size 和 n_threads 调整。
    • 队列满时策略:block(阻塞等待)或 overrun_oldest(丢弃最旧消息)。

三、快速上手:从安装到基本使用

1. 安装步骤
# 克隆仓库
git clone https://github.com/gabime/spdlog.git
cd spdlog# 编译与安装(需提前安装 CMake)
mkdir build && cd build
cmake ..
make -j  # 并行编译加速
sudo make install  # 安装到系统路径(默认 /usr/local)
2. 基本使用示例
步骤 1:包含头文件与链接库
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>  // 带颜色的控制台输出
// 链接时需指定 spdlog 库(如 -lspdlog -lspdlog_stdout)
步骤 2:创建 Logger 并记录日志
// 创建同步 Logger(输出到控制台,带颜色)
auto console_logger = spdlog::stdout_color_mt("console_logger");
console_logger->set_level(spdlog::level::info);  // 设置日志级别
console_logger->info("Hello, spdlog!");          // 记录 info 级日志
console_logger->warn("This is a warning!");       // 记录 warn 级日志// 创建异步 Logger(输出到文件,异步处理)
auto file_logger = spdlog::create_async<spdlog::sinks::simple_file_sink_mt>("file_logger", "app.log");
file_logger->error("Error occurred at line {}!", __LINE__);  // 带格式化参数
步骤 3:注册 Logger(全局访问)
spdlog::register_logger(console_logger);  // 注册到 Registry
auto global_logger = spdlog::get("console_logger");  // 全局获取
global_logger->debug("This is a debug message.");

四、进阶用法:异步日志与自定义配置

1. 异步日志深度配置
// 手动创建线程池(自定义线程数和队列大小)
auto thread_pool = std::make_shared<spdlog::async_factory::thread_pool>(4, 8192);  // 4 个工作线程,队列大小 8192// 创建带自定义线程池的异步 Logger
auto async_logger = spdlog::async_factory::create_async<spdlog::sinks::rotating_file_sink_mt>("async_file", "async.log", 1024*1024, 3, thread_pool);  // 分割文件,最大 1MB,保留 3 个备份// 设置队列满策略(默认 block,可选 overrun_oldest)
async_logger->set_async_mode(8192, spdlog::async_overflow_policy::overrun_oldest);
2. 自定义输出格式
// 定义格式模板(包含时间、级别、文件名、行号)
console_logger->set_pattern("[%Y-%m-%d %H:%M:%S] [%^%l%$] [%f:%l] %v");
// 输出示例:[2025-05-20 14:30:00] [info] [main.cpp:42] Hello, spdlog!// 扩展格式标识(如源文件路径)
console_logger->set_pattern("[%P] %v");  // %P 表示源文件完整路径
3. 自定义 Sink
// 实现自定义 Sink(继承 spdlog::sink)
class my_network_sink : public spdlog::sink {
public:void log(const spdlog::details::log_msg& msg) override {// 解析日志消息(msg.payload() 为格式化后的字符串)// 发送到网络服务器的逻辑}void flush() override { /* 可选:刷新缓冲区 */ }
};// 注册自定义 Sink
auto custom_logger = spdlog::create("custom_logger", std::make_shared<my_network_sink>());

五、高级特性:刷新策略与线程安全

1. 刷新策略
  • 手动刷新logger->flush(); 立即触发异步线程刷新缓冲区。
  • 条件刷新:当记录指定级别日志时自动刷新(如错误日志):
    logger->flush_on(spdlog::level::err);  // 记录 error/critical 时自动刷新
    
  • 间隔刷新:定期自动刷新(需确保 Logger 线程安全):
    spdlog::flush_every(std::chrono::seconds(5));  // 每 5 秒全局刷新一次
    
2. 多线程注意事项
  • 异步日志线程安全:异步 Logger 内部通过线程池处理,多线程并发调用 log() 是安全的。
  • 输出顺序:多线程异步日志无法保证严格顺序(因线程池调度不确定),如需有序日志需使用同步模式或自定义序列化机制。

六、具体使用案例

1.dlog.h

#ifndef LOG_H
#define LOG_H
#include <vector>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/async.h>
#include "spdlog/sinks/stdout_color_sinks.h"
#ifndef SPDLOG_TRACE_ON
#define SPDLOG_TRACE_ON
#endif#ifndef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG_ON
#endifclass DLog{
public:static DLog* GetInstance(){static DLog dlogger;return &dlogger;}std::shared_ptr<spdlog::logger> getLogger(){return log_;}static void SetLevel(char *log_level);private:DLog(){//创建一个包含多个日志的sink列表std::vector<spdlog::sink_ptr> sinkList;#if 1   //输出日志到控制台auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();  //输出到控制台带颜色支持consoleSink->set_level(level_);consoleSink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][thread %t][%@,%!][%l] : %v");   //这个是日志的输出格式sinkList.push_back(consoleSink);#endif//输出日志到文件auto dailySink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/daily.log", 23, 59);dailySink->set_level(level_);dailySink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][thread %t][%@,%!][%l] : %v");sinkList.push_back(dailySink);log_ = std::make_shared<spdlog::logger>("both", begin(sinkList), end(sinkList));   //创建一个新的 spdlog::logger 对象,名称为 "both",并将 sinkList 中的所有 sink 传递给它。这将使得日志同时输出到控制台和文件中。//注册到全局管理器中spdlog::register_logger(log_);//每隔一秒刷新一次spdlog::flush_every(std::chrono::seconds(1));  //`std::chrono::seconds(1)` 表示一个持续时间为1秒的时间间隔}~DLog(){}
private:std::shared_ptr<spdlog::logger> log_;static spdlog::level::level_enum level_;
};//用定义的方式调用C++标准库spdlog里面的接口,记录不同级别的日志信息
#define LogTrace(...) SPDLOG_LOGGER_CALL(DLog::GetInstance()->getLogger().get(), spdlog::level::trace, __VA_ARGS__)
#define LogDebug(...) SPDLOG_LOGGER_CALL(DLog::GetInstance()->getLogger().get(), spdlog::level::debug, __VA_ARGS__)
#define LogInfo(...) SPDLOG_LOGGER_CALL(DLog::GetInstance()->getLogger().get(), spdlog::level::info, __VA_ARGS__)
#define LogWarn(...) SPDLOG_LOGGER_CALL(DLog::GetInstance()->getLogger().get(), spdlog::level::warn, __VA_ARGS__)
#define LogError(...) SPDLOG_LOGGER_CALL(DLog::GetInstance()->getLogger().get(), spdlog::level::err, __VA_ARGS__)
#define LogCritical(...) SPDLOG_LOGGER_CALL(DLog::GetInstance()->getLogger().get(), spdlog::level::critical, __VA_ARGS__)
#endif

2.dlog.c

#include "dlog.h"spdlog::level::level_enum DLog::level_ = spdlog::level::info; // 默认使用info级别void DLog::SetLevel(char *log_level) {printf("SetLevel log_level:%s\n", log_level);fflush(stdout);if(strcmp(log_level, "trace") == 0) {level_ =  spdlog::level::trace;}else if(strcmp(log_level, "debug") == 0) {level_ =  spdlog::level::debug;}else if(strcmp(log_level, "info") == 0) {level_ =  spdlog::level::info;}else if(strcmp(log_level, "warn") == 0) {level_ =  spdlog::level::warn;}else if(strcmp(log_level, "err") == 0) {level_ =  spdlog::level::err;}else if(strcmp(log_level, "critical") == 0) {level_ =  spdlog::level::critical;}else if(strcmp(log_level, "off") == 0) {level_ =  spdlog::level::off;} else {printf("level: %s is invalid\n", log_level);}
}

相关文章:

  • c语言刷题之实际问题
  • HTML页面渲染过程
  • VMD查看蛋白质-配体的分子动力学模拟轨迹
  • Oracle如何解决LATCH:CACHE BUFFERS CHAINS
  • 阿里云服务器Ubuntu的git clone失败问题解决方案
  • 什么是防抖和节流?有什么区别?如何实现?
  • 新闻媒体发稿:社会实践返家乡主题如何选择
  • jvm对象压缩
  • 【工具变量】地级市健康城市试点政策数据集(2007-2024年)
  • web基础
  • 系统架构设计(十五):质量效用树
  • unipp === 状态管理 Pinia 使用
  • Mermaid 使用快速入门
  • MinerU可视化界面程序部署(Windows环境)
  • RSA加解密实战指南:Java与JavaScript实现详解 + 在线工具推荐
  • 探索付费社群的成功之道:生财与群响的深度解析
  • Pandas:Series和DataFrame的概念、常用属性和方法
  • 【漫话机器学习系列】270.KNN算法(K-Nearest Neighbors)
  • 【python进阶知识】Day 31 文件的规范拆分和写法
  • 工业数据治理标准规范深度解析
  • 翻越高山,成为高山!浙江广厦成CBA历史第八支夺冠球队
  • 国家发改委:进一步完善促进民营经济发展的制度机制
  • 中疾控专家:新冠感染的临床严重性未发生显著变化
  • 中国旅游日|上天当个“显眼包”!体验低空经济的“飞”凡魅力
  • “复旦源”一源六馆焕新启幕,设立文化发展基金首期1亿元
  • 网警打谣:传播涉刘国梁不实信息,2人被处罚