Skip to content

spdlog — 高性能日志库

spdlog 是 C++ 最流行的日志库,基于 fmtlib,支持异步日志、多 sink、结构化日志,零配置即可使用。

安装

bash
# vcpkg
vcpkg install spdlog

# Conan
conan install spdlog/1.13.0

# CMake FetchContent
FetchContent_Declare(spdlog
    GIT_REPOSITORY https://github.com/gabime/spdlog.git
    GIT_TAG v1.13.0)
FetchContent_MakeAvailable(spdlog)
target_link_libraries(myapp PRIVATE spdlog::spdlog)

快速开始

cpp
#include <spdlog/spdlog.h>

int main() {
    // 直接使用默认 logger(输出到 stdout)
    spdlog::info("Hello, {}!", "world");
    spdlog::warn("警告: {}", 42);
    spdlog::error("错误码: {}", -1);
    spdlog::critical("严重错误!");

    // 设置全局日志级别
    spdlog::set_level(spdlog::level::debug);
    spdlog::debug("调试信息: x={}, y={}", 1, 2);

    // 格式化输出
    spdlog::info("整数: {:05d}", 42);       // 00042
    spdlog::info("浮点: {:.3f}", 3.14159);  // 3.142
    spdlog::info("十六进制: {:#x}", 255);   // 0xff
}

创建 Logger

cpp
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/daily_file_sink.h>

// 控制台 logger(带颜色)
auto console = spdlog::stdout_color_mt("console");
console->info("彩色输出");

// 文件 logger
auto file_logger = spdlog::basic_logger_mt("file", "logs/app.log");
file_logger->info("写入文件");

// 滚动文件(按大小)
auto rotating = spdlog::rotating_logger_mt(
    "rotating",
    "logs/app.log",
    1024 * 1024 * 10,  // 10MB 滚动
    3                   // 保留 3 个文件
);

// 按天滚动
auto daily = spdlog::daily_logger_mt("daily", "logs/app.log", 0, 0);  // 每天 00:00 滚动

// 获取已注册的 logger
auto logger = spdlog::get("console");

多 Sink(同时输出到多个目标)

cpp
#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>();
console_sink->set_level(spdlog::level::info);  // 控制台只显示 info 及以上

auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
    "logs/app.log", 1024 * 1024 * 5, 3);
file_sink->set_level(spdlog::level::debug);  // 文件记录所有级别

// 组合为一个 logger
spdlog::logger logger("multi", {console_sink, file_sink});
logger.set_level(spdlog::level::debug);  // logger 级别要 <= sink 级别

logger.debug("只写文件");
logger.info("写文件 + 控制台");
logger.error("写文件 + 控制台(红色)");

// 注册为全局可访问
spdlog::register_logger(std::make_shared<spdlog::logger>(logger));

异步日志

cpp
#include <spdlog/async.h>
#include <spdlog/sinks/basic_file_sink.h>

// 初始化异步线程池(队列大小, 线程数)
spdlog::init_thread_pool(8192, 1);

// 创建异步 logger
auto async_logger = spdlog::basic_logger_async<spdlog::async_overflow_policy::block>(
    "async", "logs/async.log");

// 日志调用立即返回,实际写入在后台线程
async_logger->info("异步写入,不阻塞主线程");

// 确保所有日志写完再退出
spdlog::shutdown();

自定义格式

cpp
// 格式说明符
// %Y-%m-%d %H:%M:%S  时间戳
// %l                  日志级别
// %n                  logger 名称
// %v                  日志内容
// %t                  线程 ID
// %s                  源文件名
// %#                  行号
// %!                  函数名

spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [%t] %v");
// 输出:[2026-04-23 10:30:00.123] [console] [info] [12345] Hello!

// 带源码位置(需要用宏)
#include <spdlog/spdlog.h>
SPDLOG_INFO("带位置信息: {}", 42);  // 自动记录文件名和行号

// 自定义格式器
spdlog::set_pattern("%+");  // 默认格式

结构化日志

cpp
// spdlog 支持 fmtlib 的所有格式化特性
struct Request {
    std::string method;
    std::string path;
    int status;
};

// 自定义类型格式化
template<>
struct fmt::formatter<Request> {
    constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
    auto format(const Request& r, format_context& ctx) const {
        return fmt::format_to(ctx.out(), "{} {} -> {}",
                              r.method, r.path, r.status);
    }
};

Request req{"GET", "/api/users", 200};
spdlog::info("请求: {}", req);
// 输出:请求: GET /api/users -> 200

生产配置示例

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

void init_logging(const std::string& log_dir, spdlog::level::level_enum level) {
    // 异步线程池
    spdlog::init_thread_pool(65536, 2);

    std::vector<spdlog::sink_ptr> sinks;

    // 控制台(仅 warn 及以上)
    auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console->set_level(spdlog::level::warn);
    sinks.push_back(console);

    // 滚动文件(所有级别)
    auto file = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
        log_dir + "/app.log",
        1024 * 1024 * 100,  // 100MB
        10                   // 保留 10 个
    );
    file->set_level(spdlog::level::trace);
    sinks.push_back(file);

    auto logger = std::make_shared<spdlog::async_logger>(
        "app", sinks.begin(), sinks.end(),
        spdlog::thread_pool(),
        spdlog::async_overflow_policy::block
    );
    logger->set_level(level);
    logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v");

    spdlog::set_default_logger(logger);
    spdlog::flush_every(std::chrono::seconds(3));
}

关键认知

spdlog 的性能来自两点:基于 fmtlib 的零开销格式化,以及异步模式下的无锁队列。生产环境用异步 logger + 滚动文件,开发环境用彩色控制台。SPDLOG_* 宏在 Release 模式下可以完全消除日志调用。

系统学习 C++ 生态,深入底层架构