一个高性能、仅头文件、支持异步的 C++ 日志库

一、核心部件(架构骨架)

1.1 四大组件关系图

┌─────────────────────────────────────────────────────────────────┐
│                        你的代码                 
│              logger->info("用户 {} 登录", name)     
└───────────────────────────┬─────────────────────────────────────┘
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│  📌 Logger(日志记录器)—— 你直接打交道的对象    
│  • 职责:接收日志、检查级别、分发消息              
│  • 类型:logger(同步)/ async_logger(异步)         
│  • 关系:一个 Logger 可拥有多个 Sink       
└───────────────────────────┬─────────────────────────────────────┘
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│  📌 Formatter(格式化器)—— 按模板转换消息                   
│  • 职责:将消息 + 元数据 → 格式化字符串                       
│  • 示例:spdlog::set_pattern("[%Y-%m-%d] [%l] %v")         
└───────────────────────────┬─────────────────────────────────────┘
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│  📌 Sink(接收器/输出目标)—— 实际写入目的地                       
│  • 职责:将格式化后的字符串写入具体目标                             
│  • 常见:stdout_color_sink, rotating_file_sink, daily_file_sink  
│  • 扩展:可自定义(网络/数据库/消息队列)                           
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│  📌 Registry(注册中心)—— 全局单例,幕后管家
│  • 职责:管理所有 Logger(注册/查找/广播配置)
│  • 关键:spdlog::get("name") 查找,spdlog::set_level 广播
└─────────────────────────────────────────────────────────────────┘

1.2 核心理解要点

组件一句话理解比喻
Logger你调用的接口快递员(你交给它包裹)
Sink实际输出的目标快递目的地(送到哪)
Formatter消息的格式规则包装样式(怎么包)
Registry全局管理器快递公司总部(管所有快递员)

二、必须知道的概念(20个核心术语)

#术语含义关键点
1Logger日志记录器你直接调用的对象
2Sink接收器/输出目标实际写入的地方
3Formatter格式化器控制日志输出格式
4Registry注册中心全局单例,管理所有 Logger
5Level日志级别trace < debug < info < warn < error < critical
6_mtMulti-Threaded线程安全(带锁),默认推荐
7_stSingle-Threaded线程不安全(无锁),仅单线程用
8同步日志当前线程直接写 I/O简单可靠,适合低频
9异步日志入队后后台线程写 I/O高性能,适合高频,退出前必须 shutdown
10Pattern格式模板如 "%Y-%m-%d %H:%M:%S [%l] %v"
11SPDLOG_* 宏编译时可控的日志宏配合 SPDLOG_ACTIVE_LEVEL 可完全移除
12spdlog::* 函数运行时日志函数简洁,但无法编译时移除
13文件滚动日志文件自动切分按大小(rotating)或按时间(daily)
14多 Sink一个 Logger 多个输出同时输出到控制台 + 文件 + 网络
15Backtrace回溯日志缓存 trace 日志,发生错误时转储
16刷新策略何时写入磁盘flush_on(level) + flush_every(seconds)
17溢出策略异步队列满时行为block(阻塞)或 overrun_oldest(丢弃旧)
18零拷贝减少内存复制性能优化手段
19无锁队列无需锁的队列异步日志性能核心
20仅头文件Header-only直接 #include 即可,无需编译库

三、编译选项(构建配置)

3.1 编译时优化宏(在 #include <spdlog/spdlog.h> 之前定义)

// ========== 性能优化 ==========

// 1. 使用精度稍低但更快的时钟(Linux)
#define SPDLOG_CLOCK_COARSE

// 2. 禁用线程 ID 获取(格式中无 %t 时使用)
#define SPDLOG_NO_THREAD_ID

// 3. 禁用源文件位置(格式中无 %@ 时使用)
#define SPDLOG_NO_SOURCE_LOC

// ========== 编译时日志级别控制 ==========

// 4. 编译时移除低于指定级别的日志(仅对 SPDLOG_* 宏有效)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO  // 移除 trace 和 debug
// 可选值: SPDLOG_LEVEL_TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL, OFF

3.2 CMake 集成示例

# CMakeLists.txt
find_package(spdlog REQUIRED)

# Debug 模式保留所有日志,Release 模式移除 trace/debug
target_compile_definitions(${PROJECT_NAME} PRIVATE
    $<$<CONFIG:Release>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO>
    $<$<CONFIG:Release>:SPDLOG_CLOCK_COARSE>
)
target_link_libraries(${PROJECT_NAME} spdlog::spdlog)

3.3 命令行编译

# Debug 构建
g++ -std=c++17 -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG main.cpp -o app_debug

# Release 构建(移除 trace 和 debug)
g++ -std=c++17 -O2 -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO main.cpp -o app_release

四、必须注意的事项(关键决策点)

4.1 选型决策表

场景推荐选择原因
多线程环境_mt 后缀线程安全,默认选择
单线程 + 极致性能_st 后缀无锁开销,但需明确单线程
高频日志(> 1000条/秒)异步日志不阻塞业务线程
低频日志(< 100条/秒)同步日志简单可靠
自定义 Logger(多模块)logger->info() 函数宏无法指定自定义 Logger
调试日志(trace/debug)SPDLOG_* 宏编译时可完全移除
业务日志(info/warn/error)spdlog::info() 或 logger->info()简洁
热路径(循环内)SPDLOG_* 宏 + 异步极致性能

4.2 三原则

原则内容
1. 线程安全不确定环境时一律用 _mt
2. 异步优先生产环境高频场景用异步
3. 按需编译trace/debug 用宏 + SPDLOG_ACTIVE_LEVEL 控制

五、易错内容(避坑清单)

🔴 易错 1:日志不刷新 → 文件为空或丢日志


// ❌ 错误:程序崩溃时日志丢失
spdlog::info("关键操作完成");
// ✅ 正确:设置刷新策略
spdlog::flush\_on(spdlog::level::err);  // error 及以上立即刷新
spdlog::flush\_every(std::chrono::seconds(3));  // 定时刷新
// 退出前
spdlog::shutdown();

🔴 易错 2:异步日志退出时挂起

// ❌ 错误:主线程直接退出,后台线程还在工作
int main() {
    setup_async();
    spdlog::info("Hello");
    return 0;  // 可能卡住或崩溃
}

// ✅ 正确:优雅关闭
int main() {
    setup_async();
    spdlog::info("Hello");
    spdlog::shutdown();  // 等待后台线程完成
    return 0;
}

🔴 易错 3:热路径中使用同步日志

// ❌ 错误:高频循环中用同步日志 → 严重拖慢性能
for (int i = 0; i < 1000000; ++i) {
    spdlog::info("{}", i);
}

// ✅ 正确:使用异步日志
auto async_logger = get_async_logger();
for (int i = 0; i < 1000000; ++i) {
    async_logger->info("{}", i);
}

🔴 易错 4:SPDLOG_ACTIVE_LEVEL 对 spdlog:: 函数无效

// ❌ 错误:以为能移除 spdlog::debug()
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#include <spdlog/spdlog.h>
spdlog::debug("这条还会输出");  // 无法移除!

// ✅ 正确:使用 SPDLOG_* 宏
SPDLOG_DEBUG("这条在编译时被移除");  // 完全不生成代码

🔴 易错 5:跨 DLL 传递 Logger 对象

// ❌ 错误:在 DLL 内部创建 Logger
// plugin.dll
auto logger = spdlog::basic_logger_mt("plugin", "plugin.log");
// 卸载 DLL 时可能崩溃

// ✅ 正确:由主程序创建,通过接口传递
// main.exe
auto main_logger = spdlog::basic_logger_mt("app", "app.log");
plugin->set_logger(main_logger);

🔴 易错 6:宏中的昂贵参数仍会被求值

// 对于 SPDLOG_* 宏,如果级别不满足,参数不会被求值
SPDLOG_DEBUG("复杂数据: {}", generate_large_json());  // ✅ 安全

// 但如果直接用 spdlog:: 函数,参数会立即求值
spdlog::debug("复杂数据: {}", generate_large_json());  // ❌ 即使关闭也会调用

// 对于宏,如果参数是纯函数调用,安全;但如果有副作用需注意
int counter = 0;
SPDLOG_DEBUG("counter: {}", ++counter);  // 如果 debug 被关闭,++counter 不会执行

🔴 易错 7:_st 在多线程中使用

// ❌ 错误:多个线程共享同一个 _st Logger
auto logger = spdlog::stdout_color_st("console");  // 线程不安全
std::thread t1([&](){ logger->info("from t1"); });
std::thread t2([&](){ logger->info("from t2"); });
// 数据竞争 → 崩溃或乱码

// ✅ 正确:多线程用 _mt
auto logger = spdlog::stdout_color_mt("console");

六、工作原理(快速理解)

6.1 同步 vs 异步

同步:

【同步模式】
调用 logger->info()
       │
       ▼
检查日志级别 ──不通过→ 返回
       │通过
       ▼
格式化消息(Formatter)
       │
       ▼
Sink 写入(I/O 操作,耗时)
       │
       ▼
返回 ← 业务线程被阻塞

异步:

【异步模式】
调用 logger->info()
       │
       ▼
检查日志级别 ──不通过→ 返回
       │通过
       ▼
格式化消息(Formatter)
       │
       ▼
放入无锁队列 ──→ 立即返回 ← 业务线程几乎无阻塞
       │
       │ (后台线程)
       ▼
从队列取出
       │
       ▼
Sink 写入(I/O 操作)

6.2 为什么快?

  1. 异步非阻塞:I/O 移出业务线程
  2. 无锁队列moodycamel::ConcurrentQueue,减少竞争
  3. 高效格式化fmt 库比 iostream 快数倍
  4. 零拷贝:减少内存分配和数据复制

七、速查表(快速参考)

7.1 常用 API

// ===== 创建 Logger =====
auto logger = spdlog::stdout_color_mt("name");           // 彩色控制台
auto logger = spdlog::basic_logger_mt("name", "log");    // 文件
auto logger = spdlog::rotating_logger_mt("name", "log", 5*1024*1024, 3);  // 滚动
auto logger = spdlog::daily_logger_mt("name", "log", 0, 0);  // 每日

// ===== 获取 Logger =====
auto logger = spdlog::get("name");

// ===== 全局配置 =====
spdlog::set_level(spdlog::level::debug);
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v");
spdlog::flush_on(spdlog::level::err);
spdlog::flush_every(std::chrono::seconds(3));

// ===== 记录日志 =====
logger->info("用户 {} 登录", name);
spdlog::info("全局日志");
SPDLOG_INFO("宏日志");

// ===== 异步日志 =====
spdlog::init_thread_pool(8192, 1);
auto logger = spdlog::basic_logger_mt<spdlog::async_factory>("async", "log");
spdlog::shutdown();  // 退出前必须调用

7.2 常用格式化占位符

占位符含义示例
%v消息正文用户登录
%t线程 ID12345
%Y-%m-%d日期2026-08-22
%H:%M:%S.%e时间+毫秒14:30:25.123
%l级别全称info
%L级别简称I
%nLogger 名称app
%@文件:行号main.cpp:42
%^ %$颜色包裹带颜色输出

八、总结(一句话记忆)

维度一句话总结
核心部件Logger 接口,Sink 目的地,Formatter 格式,Registry 管家
线程安全多线程用 _mt,单线程用 _st,不确定就 _mt
同步/异步高频用异步,低频用同步,退出前 shutdown
宏 vs 函数调试用宏(可移除),业务用函数(更简洁)
编译控制SPDLOG_ACTIVE_LEVEL 移除 trace/debug
刷新策略error 即刷新,退出前 shutdown
避坑口诀异步要 shutdown,热路径用异步,多线程用 _mt,调试日志用宏

九、进阶API接口


// ===== 1.多 Sink(同时输出到多个目标) =====

#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>

// 创建多个 Sink
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
    "logs/app.log", 1024*1024*5, 3
);

// 组合成一个 Logger(同时输出到控制台和文件)
std::vector<spdlog::sink_ptr> sinks = {console_sink, file_sink};
auto logger = std::make_shared<spdlog::logger>("multi", sinks.begin(), sinks.end());
// 需要手动注册才能用 spdlog::get() 获取
spdlog::register_logger(logger);


// ===== 2.为不同 Sink 设置不同级别和格式 =====

// 控制台只显示 info 及以上,文件记录全部
console_sink->set_level(spdlog::level::info);
file_sink->set_level(spdlog::level::trace);

// 控制台用简洁格式,文件用详细格式
console_sink->set_pattern("[%l] %v");
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%t] [%l] %v");


// ===== 3.回溯日志(Backtrace) =====

// 启用回溯:缓存最近 100 条 trace 日志
logger->enable_backtrace(100);

// 在代码中埋点(不会输出,只缓存)
logger->trace("步骤1: 初始化");
logger->trace("步骤2: 处理数据");

// 发生错误时转储所有缓存的 trace
logger->error("处理失败!");
logger->dump_backtrace();  // 输出缓存的 100 条 trace

// ===== 4.自定义日志级别(level::from_str) =====

// 从字符串解析级别(方便从配置文件读取)
std::string level_str = config.get("log_level", "info");
spdlog::level::level_enum level = spdlog::level::from_str(level_str);
spdlog::set_level(level);

// 所有级别枚举值
spdlog::level::trace     // 0
spdlog::level::debug     // 1
spdlog::level::info      // 2
spdlog::level::warn      // 3
spdlog::level::err       // 4
spdlog::level::critical  // 5
spdlog::level::off       // 6


// ===== 5.环境变量动态控制 =====

#include <spdlog/cfg/env.h>

// 从环境变量加载级别
// 启动前设置: export SPDLOG_LEVEL=debug
// 或针对特定 logger: export SPDLOG_LEVEL=logger1=debug,logger2=warn
spdlog::cfg::load_env_levels();

// 从 argv 加载(支持 --spdlog-level=debug)
spdlog::cfg::load_argv_levels(argc, argv);



// ===== 6.自定义 Sink(输出到任意目标) =====

#include <spdlog/sinks/base_sink.h>

template<typename Mutex>
class my_redis_sink : public spdlog::sinks::base_sink<Mutex> {
protected:
    void sink_it_(const spdlog::details::log_msg& msg) override {
        spdlog::memory_buf_t formatted;
        this->formatter_->format(msg, formatted);
        // 将 formatted.data() 发送到 Redis
        redis_client_.rpush("logs", std::string(formatted.data(), formatted.size()));
    }
    void flush_() override { /* 刷新连接 */ }
    
private:
    RedisClient redis_client_;
};

// 使用自定义 Sink
auto redis_sink = std::make_shared<my_redis_sink<std::mutex>>();
auto logger = std::make_shared<spdlog::logger>("redis_logger", redis_sink);



// ===== 7.自定义 Sink(输出到任意目标) =====

// 如果你的程序中只有一个 Sink,可以直接设为默认
auto logger = spdlog::stdout_color_mt("console");
spdlog::set_default_logger(logger);

// 之后 spdlog::info() 就会输出到这个 logger
spdlog::info("这会输出到默认 logger");


// ===== 8.线程局部 Logger(thread_local) =====

// 每个线程拥有自己的 Logger,避免锁竞争
thread_local auto logger = spdlog::stdout_color_st("thread_local");

void thread_func() {
    logger->info("这条日志只属于当前线程,线程安全且无锁");
}





// ===== 9.条件日志(should_log) =====

// 在调用昂贵函数前检查级别
if (logger->should_log(spdlog::level::debug)) {
    logger->debug("复杂数据: {}", generate_large_json());
}



// ===== 10. 异常处理 =====
try {
    auto logger = spdlog::rotating_logger_mt("app", "logs/app.log", 1024*1024*5, 3);
} catch (const spdlog::spdlog_ex& ex) {
    std::cerr << "日志初始化失败: " << ex.what() << std::endl;
}


// ===== 12. 异常处理 =====

// 固定宽度 7 个字符,左对齐
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%-7l] %v");
// 输出: [2026-08-22 14:30:25.123] [info   ] 消息
//                          [error  ] 消息
//                          [debug  ] 消息
  1. 更高级的格式化占位符

    占位符含义
    %s源文件名
    %#行号
    %!函数名
    %^ %$颜色范围(配合 %l 使用)
    %+等同于 %Y-%m-%d %H:%M:%S.%e

十、综合实践:生产级日志系统配置

10.1 完整示例代码

// logger_config.hpp
#pragma once
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/async.h>
#include <memory>
#include <vector>

class LoggerConfig {
public:
    static void init(const std::string& app_name = "app",
                     const std::string& log_dir = "logs",
                     bool enable_console = true,
                     bool enable_async = true) {
        try {
            std::vector<spdlog::sink_ptr> sinks;
            
            // 1. 控制台 Sink(彩色输出,仅 info 及以上)
            if (enable_console) {
                auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                console_sink->set_level(spdlog::level::info);
                console_sink->set_pattern("%^[%H:%M:%S.%e] [%l]%$ %v");
                sinks.push_back(console_sink);
            }
            
            // 2. 滚动文件 Sink(记录全部级别,5MB × 3 个文件)
            auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
                log_dir + "/" + app_name + ".log", 
                5 * 1024 * 1024,  // 5MB
                3                   // 保留 3 个文件
            );
            file_sink->set_level(spdlog::level::trace);
            file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%t] [%-7l] [%s:%#] %v");
            sinks.push_back(file_sink);
            
            // 3. 创建 Logger(异步或同步)
            spdlog::logger_ptr logger;
            if (enable_async) {
                // 异步:队列 8192,1 个后台线程
                spdlog::init_thread_pool(8192, 1);
                logger = std::make_shared<spdlog::async_logger>(
                    app_name, sinks.begin(), sinks.end(),
                    spdlog::thread_pool(),
                    spdlog::async_overflow_policy::block
                );
            } else {
                logger = std::make_shared<spdlog::logger>(
                    app_name, sinks.begin(), sinks.end()
                );
            }
            
            // 4. 全局配置
            logger->set_level(spdlog::level::debug);
            logger->flush_on(spdlog::level::err);  // error 及以上立即刷新
            spdlog::flush_every(std::chrono::seconds(3));  // 定时刷新
            
            // 5. 注册为默认 Logger
            spdlog::set_default_logger(logger);
            
            // 6. 启动日志
            logger->info("日志系统初始化完成 (async: {})", enable_async);
            
        } catch (const spdlog::spdlog_ex& ex) {
            std::cerr << "日志初始化失败: " << ex.what() << std::endl;
            throw;
        }
    }
    
    static void shutdown() {
        spdlog::info("应用关闭,日志系统即将退出");
        spdlog::shutdown();
    }
};
// main.cpp
#include "logger_config.hpp"
#include <spdlog/spdlog.h>
#include <thread>
#include <vector>

// 编译时控制:Release 模式移除 trace/debug
// 在 CMake 或编译命令中定义

int main(int argc, char* argv[]) {
    // 初始化日志系统
    LoggerConfig::init("myapp", "logs", true, true);
    
    // 记录启动信息
    spdlog::info("=== 应用启动 ===");
    spdlog::info("版本: 1.0.0");
    spdlog::info("参数数量: {}", argc);
    
    // 模拟业务逻辑
    spdlog::debug("开始处理任务");
    
    // 多线程日志测试
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([i]() {
            // 使用宏(可编译时移除)
            SPDLOG_DEBUG("线程 {} 开始执行", i);
            
            for (int j = 0; j < 100; ++j) {
                spdlog::info("线程 {} 处理第 {} 个任务", i, j);
            }
            
            SPDLOG_DEBUG("线程 {} 执行完成", i);
        });
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    // 错误日志测试
    try {
        throw std::runtime_error("模拟异常");
    } catch (const std::exception& e) {
        spdlog::error("捕获异常: {}", e.what());
        spdlog::critical("严重错误,需要立即处理");
    }
    
    // 回溯日志示例
    spdlog::default_logger()->enable_backtrace(50);
    for (int i = 0; i < 100; ++i) {
        spdlog::trace("回溯日志 {}", i);
    }
    spdlog::error("发生错误,转储回溯日志");
    spdlog::dump_backtrace();
    
    // 性能测试
    auto start = std::chrono::steady_clock::now();
    for (int i = 0; i < 10000; ++i) {
        spdlog::info("性能测试消息 {}", i);
    }
    auto end = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    spdlog::info("10000 条异步日志耗时: {} ms", duration.count());
    
    // 优雅关闭
    LoggerConfig::shutdown();
    return 0;
}

10.2 CMake 配置文件

# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(LoggerDemo)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找 spdlog
find_package(spdlog REQUIRED)

# 创建可执行文件
add_executable(${PROJECT_NAME} main.cpp)

# Debug 配置:保留所有日志
target_compile_definitions(${PROJECT_NAME} PRIVATE
    $<$<CONFIG:Debug>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE>
)

# Release 配置:移除 trace/debug,优化性能
target_compile_definitions(${PROJECT_NAME} PRIVATE
    $<$<CONFIG:Release>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO>
    $<$<CONFIG:Release>:SPDLOG_CLOCK_COARSE>
    $<$<CONFIG:Release>:SPDLOG_NO_THREAD_ID>
)

# 链接 spdlog
target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog)

# 优化选项
if(MSVC)
    target_compile_options(${PROJECT_NAME} PRIVATE /O2 /W4)
else()
    target_compile_options(${PROJECT_NAME} PRIVATE -O2 -Wall -Wextra)
endif()

10.3 配置文件驱动(完整实现)

config.json 文件

{
    "log": {
        "level": "debug",
        "async": true,
        "queue_size": 8192,
        "console": {
            "enabled": true,
            "level": "info",
            "pattern": "%^[%H:%M:%S.%e] [%l]%$ %v"
        },
        "file": {
            "enabled": true,
            "path": "logs/app.log",
            "max_size_mb": 5,
            "max_files": 3,
            "level": "trace",
            "pattern": "[%Y-%m-%d %H:%M:%S.%e] [%t] [%-7l] %v"
        },
        "flush_on": "error",
        "flush_interval_sec": 3
    }
}

完整的配置文件驱动实现

// config_loader.hpp
#pragma once
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/async.h>
#include <fstream>
#include <nlohmann/json.hpp>  // 需要 json 库,或者用其他 JSON 解析库

using json = nlohmann::json;

class ConfigDrivenLogger {
public:
    static void init_from_file(const std::string& config_path) {
        try {
            // 1. 读取配置文件
            std::ifstream config_file(config_path);
            if (!config_file.is_open()) {
                throw std::runtime_error("无法打开配置文件: " + config_path);
            }
            
            json config;
            config_file >> config;
            
            // 2. 解析日志配置
            auto& log_config = config["log"];
            
            // 3. 创建 Sinks
            std::vector<spdlog::sink_ptr> sinks;
            
            // 控制台 Sink
            if (log_config["console"]["enabled"].get<bool>()) {
                auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                console_sink->set_level(
                    parse_level(log_config["console"]["level"].get<std::string>())
                );
                console_sink->set_pattern(
                    log_config["console"]["pattern"].get<std::string>()
                );
                sinks.push_back(console_sink);
                spdlog::info("控制台 Sink 已启用");
            }
            
            // 文件 Sink
            if (log_config["file"]["enabled"].get<bool>()) {
                auto& file_config = log_config["file"];
                auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
                    file_config["path"].get<std::string>(),
                    file_config["max_size_mb"].get<int>() * 1024 * 1024,
                    file_config["max_files"].get<int>()
                );
                file_sink->set_level(
                    parse_level(file_config["level"].get<std::string>())
                );
                file_sink->set_pattern(
                    file_config["pattern"].get<std::string>()
                );
                sinks.push_back(file_sink);
                spdlog::info("文件 Sink 已启用: {}", file_config["path"].get<std::string>());
            }
            
            // 4. 创建 Logger
            spdlog::logger_ptr logger;
            std::string logger_name = "config_logger";
            
            if (log_config["async"].get<bool>()) {
                // 异步 Logger
                size_t queue_size = log_config["queue_size"].get<size_t>();
                spdlog::init_thread_pool(queue_size, 1);
                
                logger = std::make_shared<spdlog::async_logger>(
                    logger_name,
                    sinks.begin(),
                    sinks.end(),
                    spdlog::thread_pool(),
                    spdlog::async_overflow_policy::block
                );
            } else {
                // 同步 Logger
                logger = std::make_shared<spdlog::logger>(
                    logger_name,
                    sinks.begin(),
                    sinks.end()
                );
            }
            
            // 5. 设置全局级别
            logger->set_level(
                parse_level(log_config["level"].get<std::string>())
            );
            
            // 6. 设置刷新策略
            std::string flush_level = log_config["flush_on"].get<std::string>();
            logger->flush_on(parse_level(flush_level));
            
            int flush_interval = log_config["flush_interval_sec"].get<int>();
            spdlog::flush_every(std::chrono::seconds(flush_interval));
            
            // 7. 注册为默认 Logger
            spdlog::set_default_logger(logger);
            
            spdlog::info("=== 日志系统初始化完成 ===");
            spdlog::info("配置来源: {}", config_path);
            spdlog::info("日志级别: {}", log_config["level"].get<std::string>());
            spdlog::info("异步模式: {}", log_config["async"].get<bool>() ? "开启" : "关闭");
            
        } catch (const std::exception& ex) {
            std::cerr << "日志初始化失败: " << ex.what() << std::endl;
            // 降级到默认配置
            fallback_to_default();
        }
    }
    
private:
    static spdlog::level::level_enum parse_level(const std::string& level_str) {
        if (level_str == "trace") return spdlog::level::trace;
        if (level_str == "debug") return spdlog::level::debug;
        if (level_str == "info") return spdlog::level::info;
        if (level_str == "warn") return spdlog::level::warn;
        if (level_str == "error") return spdlog::level::err;
        if (level_str == "critical") return spdlog::level::critical;
        if (level_str == "off") return spdlog::level::off;
        return spdlog::level::info;  // 默认
    }
    
    static void fallback_to_default() {
        std::cerr << "使用默认日志配置" << std::endl;
        auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
        auto logger = std::make_shared<spdlog::logger>("fallback", console_sink);
        logger->set_level(spdlog::level::info);
        spdlog::set_default_logger(logger);
        spdlog::warn("使用降级日志配置(仅控制台输出)");
    }
};

使用示例

// main_with_config.cpp
#include "config_loader.hpp"
#include <spdlog/spdlog.h>

int main() {
    // 从配置文件初始化
    ConfigDrivenLogger::init_from_file("config.json");
    
    // 测试各级别日志
    spdlog::trace("这是 trace 日志");
    spdlog::debug("这是 debug 日志");
    spdlog::info("这是 info 日志");
    spdlog::warn("这是 warn 日志");
    spdlog::error("这是 error 日志");
    spdlog::critical("这是 critical 日志");
    
    // 模拟业务逻辑
    for (int i = 0; i < 100; ++i) {
        spdlog::info("处理任务 {}", i);
    }
    
    // 优雅关闭
    spdlog::shutdown();
    return 0;
}

编译说明

# 需要安装 nlohmann-json 库
# Ubuntu/Debian:
sudo apt-get install nlohmann-json3-dev

# macOS:
brew install nlohmann-json

# 编译
g++ -std=c++17 main_with_config.cpp -o app -lspdlog -lfmt

CMake 配置

# CMakeLists.txt 添加
find_package(nlohmann_json REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE 
    spdlog::spdlog 
    nlohmann_json::nlohmann_json
)