spdlog入门

        spdlog 是一个快速、头文件式的 C++ 日志库,非常易于使用且功能强大。以下是 spdlog 的基本使用指南。

编译安装

从源码编译安装

bash

git clone https://github.com/gabime/spdlog.git
cd spdlog
mkdir build && cd build
cmake .. -DSPDLOG_BUILD_EXAMPLE=OFF
cmake --build . --config Release
sudo cmake --install .

编译选项

常用 CMake 选项:

  • -DSPDLOG_BUILD_EXAMPLE=ON/OFF - 是否编译示例

  • -DSPDLOG_BUILD_TESTS=ON/OFF - 是否编译测试

  • -DSPDLOG_BUILD_SHARED=ON/OFF - 是否构建共享库

  • -DSPDLOG_FMT_EXTERNAL=ON/OFF - 使用外部 fmt 库

基本使用

1. 快速开始

cpp

#include <spdlog/spdlog.h>

int main() {
    spdlog::info("Welcome to spdlog!");
    spdlog::error("Some error message with arg: {}", 1);
    
    spdlog::warn("Easy padding in numbers like {:08d}", 12);
    spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
    spdlog::info("Support for floats {:03.2f}", 1.23456);
    spdlog::info("Positional args are {1} {0}..", "too", "supported");
    spdlog::info("{:<30}", "left aligned");
}

2. 创建日志器

cpp

// 创建控制台日志器
auto console = spdlog::stdout_color_mt("console");

// 创建文件日志器
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");

3. 使用日志器

cpp

console->info("This is an info message");
file_logger->warn("This warning will go to the file");

4. 自动刷新与手工刷新日志写入文件

1)默认自动刷新机制

spdlog 的 基本文件日志器 (basic_file_sink) 默认会在以下情况下自动刷新(写入文件):

触发条件说明
缓冲区满默认缓冲区大小通常为 8192 字节(8KB),当缓冲区填满时自动写入文件
每条日志后 (可选)如果设置 flush_on 为特定日志级别(如 spdlog::level::err),达到该级别的日志会立即触发刷新
析构时当日志器被销毁(如程序退出时 spdlog::shutdown()),会强制刷新缓冲区
auto logger = spdlog::basic_logger_mt("file_logger", "logs.txt");
logger->set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v");

// 设置缓冲区大小为 4KB(更频繁刷新)
logger->sinks()[0]->set_buffer_size(4096); 
// 遇到 ERROR 或更严重的日志时立即刷新
logger->flush_on(spdlog::level::err);
2)手工刷新

flush() 方法

功能

  • 强制将缓冲区中的所有日志消息立即写入目标(文件、控制台等)

  • 不会丢弃任何日志消息

  • 保证调用时刻之前的所有日志都被写入

spdlog::info("这是一条重要消息");
spdlog::default_logger()->flush(); // 确保消息立即写入

// 或者在程序关键点强制刷新
void criticalOperation() {
    spdlog::info("开始关键操作");
    // ...操作代码...
    spdlog::default_logger()->flush(); // 确保操作前的日志已写入
}

5. spdlog 中 drop_all() 

功能

  • 立即丢弃所有挂起的日志消息(在缓冲区中但尚未写入的消息)

  • 不会执行任何实际的写入操作

  • 只影响调用它的特定日志器

// 紧急情况下丢弃非关键日志
void handleMemoryEmergency() {
    auto logger = spdlog::get("non_critical");
    if (logger) {
        logger->drop_all(); // 快速释放内存
    }
}

// 快速退出时避免I/O延迟
void fastExit() {
    spdlog::info("程序即将快速退出");
    spdlog::default_logger()->drop_all(); // 不保证写入最后一条日志
    std::_Exit(1);
}

6.spdlog 中 shutdown()

功能

  • 全局关闭整个 spdlog 系统

  • 会先 flush 所有日志器的消息

  • 释放所有资源(文件句柄、线程池等)

  • 注销所有已注册的日志器

  • 调用后不能再使用 spdlog

// 正常程序退出
int main() {
    spdlog::info("程序启动");
    
    // ...程序逻辑...
    
    spdlog::info("程序正常退出");
    spdlog::shutdown(); // 保证所有日志写入
    return 0;
}

// 重新配置日志系统
void reconfigureLogging() {
    spdlog::shutdown(); // 先完全关闭
    
    // 重新初始化
    auto logger = spdlog::basic_logger_mt("new_logger", "new.log");
    spdlog::set_default_logger(logger);
}

高级特性

1. 日志级别

cpp

spdlog::set_level(spdlog::level::debug); // 设置全局日志级别
console->set_level(spdlog::level::trace); // 设置特定日志器级别

console->trace("This is a trace message");
console->debug("This is a debug message");

2. 格式化

cpp

// 自定义格式
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");

// 格式说明:
// %v - 实际日志文本
// %t - 线程ID
// %n - 日志器名称
// %l - 日志级别
// %L - 短日志级别
// %^ - 开始颜色范围
// %$ - 结束颜色范围

3. 异步日志

cpp

// 创建异步日志器
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");

// 或转换现有日志器为异步
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_logger", "logs/async_log.txt");

4. 多接收器日志器

cpp

// 创建同时输出到控制台和文件的日志器
auto sinks = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("multisink.txt");
auto logger = std::make_shared<spdlog::logger>("multi_sink", {sinks, file_sink});
spdlog::register_logger(logger);

logger->info("This message goes to both console and file");

5. 每日日志文件

cpp

// 创建每天生成新文件的日志器
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 0, 0);

6. 日志文件大小限制

spdlog 提供了日志文件大小限制的功能,可以通过 rotating_logger_mt 或 rotating_logger_st 创建支持文件旋转的日志器,当日志文件达到指定大小时会自动创建新文件。

std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(
    const std::string& logger_name,
    const std::string& filename,
    size_t max_file_size,
    size_t max_files,
    bool rotate_on_open = false,
    const file_event_handlers& event_handlers = {}
);

参数说明

参数说明
logger_name日志记录器的名称(唯一标识)
filename日志文件路径
max_file_size单个日志文件的最大大小(字节)
max_files保留的最大日志文件数量(旧的会被删除)
rotate_on_open(可选) 是否在打开时立即轮转(默认 false
event_handlers(可选) 文件操作事件回调(如打开、关闭等)

返回值

  • 类型std::shared_ptr<spdlog::logger>

  • 用途: 用于后续的日志记录操作(如 logger->info()logger->error() 等)

示例代码:

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

int main() {
    // 创建旋转日志器,限制单个文件大小为30MB,最多保留5个文件
    auto max_size = 30 * 1024 * 1024; // 30MB
    auto max_files = 5;
    auto logger = spdlog::rotating_logger_mt("rotating_logger", "logs/rotating.txt", max_size, max_files);
    
    logger->info("This will be written to rotating.txt");
    // 当日志文件达到30MB时,会重命名为rotating.1.txt,创建新的rotating.txt
    // 当有5个备份文件后,最旧的会被删除
}

 日志轮转机制
1)当日志文件大小超过 max_file_size 时,会自动创建新文件(如 mylog.log, mylog.1.log, mylog.2.log 等)。
2)旧文件会按序号递增,超过 max_files 的文件会被删除。

异步示例代码:

#include <spdlog/async.h>

int main() {
    spdlog::init_thread_pool(8192, 1); // 队列大小,线程数
    
    auto max_size = 30 * 1024 * 1024; // 30MB
    auto max_files = 5;
    auto async_logger = spdlog::create_async<spdlog::sinks::rotating_file_sink_mt>(
        "async_rotating", "logs/async_rotating.txt", max_size, max_files);
    
    async_logger->info("This is an async message with rotation");
}

7. 日志打印函数名称行号

使用 SPDLOG_LOGGER_<LEVEL> 宏

spdlog 提供了内置的宏,可以自动捕获 文件名、函数名、行号

日志级别说明
SPDLOG_LOGGER_TRACE(logger, fmt, ...)trace最详细的调试信息(需启用 SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE
SPDLOG_LOGGER_DEBUG(logger, fmt, ...)debug调试信息(需启用 SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG
SPDLOG_LOGGER_INFO(logger, fmt, ...)info常规信息
SPDLOG_LOGGER_WARN(logger, fmt, ...)warn警告信息
SPDLOG_LOGGER_ERROR(logger, fmt, ...)error错误信息
SPDLOG_LOGGER_CRITICAL(logger, fmt, ...)critical严重错误(通常会导致程序终止)

示例代码:

#include <spdlog/spdlog.h>

int main() {
    // 初始化 logger(以 rotating_logger_mt 为例)
    auto logger = spdlog::rotating_logger_mt("my_logger", "logs/app.log", 1024 * 1024, 3);
    
    // 使用宏记录日志(自动包含文件名、函数名、行号)
    SPDLOG_LOGGER_INFO(logger, "This is an info message");
    SPDLOG_LOGGER_WARN(logger, "Warning: something might be wrong");
    
    return 0;
}

输出:

[2025-04-20 12:34:56] [my_logger] [info] [main.cpp:5 (main)] This is an info message
[2025-04-20 12:34:56] [my_logger] [warning] [main.cpp:6 (main)] Warning: something might be wrong

 8. spdlog中支持QT字符串(QString)写入

方法1:转换为std::string或QByteArray

最简单的方法是先将QString转换为标准字符串:

#include <spdlog/spdlog.h>
#include <QString>

QString qtStr = "Hello from QT";
spdlog::info("Message: {}", qtStr.toStdString());
// 或者
spdlog::info("Message: {}", qtStr.toUtf8().constData());
方法2:自定义格式化器

你可以为QString添加自定义格式化支持:

#include <spdlog/fmt/fmt.h>
#include <QString>

template <>
struct fmt::formatter<QString> : fmt::formatter<std::string> {
    auto format(const QString& qstr, format_context& ctx) {
        return fmt::formatter<std::string>::format(qstr.toStdString(), ctx);
    }
};

// 使用示例
QString qtStr = "Formatted QT string";
spdlog::info("Message: {}", qtStr);

9. 在spdlog中支持QT字符串(QString)并确保中文不乱码

方法1:使用toUtf8()确保UTF-8编码(推荐)
#include <spdlog/spdlog.h>
#include <QString>

QString chineseStr = "你好,世界!";
spdlog::info("中文消息: {}", chineseStr.toUtf8().constData());

这是最可靠的方法,因为:

  1. UTF-8是spdlog内部使用的编码

  2. QT的toUtf8()方法会正确转换所有Unicode字符

  3. 避免了本地编码转换可能带来的问题

方法2:设置全局编码(适用于QT5)
#include <QTextCodec>

int main(int argc, char *argv[]) {
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
    
    QString chineseStr = "中文内容";
    spdlog::info("消息: {}", chineseStr.toLocal8Bit().constData());
    
    return 0;
}

10. logger->set_level(spdlog::level::debug) 设置了日志级别为 debug,但 SPDLOG_LOGGER_DEBUG(logger, "这是一条 DEBUG 日志") 仍然没有输出.

方法1

在 包含 spdlog 头文件之前,定义宏:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG  // 启用 DEBUG 级别
#include <spdlog/spdlog.h>

方法2
日志级别被全局覆盖,如果调用了 spdlog::set_level(),它会覆盖所有 logger 的级别:

spdlog::set_level(spdlog::level::info);  // 全局设置为 info,DEBUG 不会输出
auto logger = spdlog::default_logger();
logger->set_level(spdlog::level::debug);  // 会被全局设置覆盖
SPDLOG_LOGGER_DEBUG(logger, "仍然不会输出");

11. 禁用日志器

// 完全禁用某个日志器
logger->set_level(spdlog::level::off);

异常处理

try {
    // spdlog 代码
} catch (const spdlog::spdlog_ex& ex) {
    std::cout << "Log initialization failed: " << ex.what() << std::endl;
}

完整示例

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

int main() {
    try {
        // 控制台日志器
        auto console = spdlog::stdout_color_mt("console");
        console->info("Welcome to spdlog!");
        
        // 文件日志器
        auto file_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
        file_logger->info("Logging to file");
        
        // 异步日志器
        spdlog::init_thread_pool(8192, 1); // 队列大小,线程数
        auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async-log.txt");
        async_file->info("This is async log message");
        
        // 修改日志格式
        spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%t] %v");
        
        // 多接收器日志器
        auto sinks = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
        auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("multisink.txt");
        auto multi_logger = std::make_shared<spdlog::logger>("multi_sink", {sinks, file_sink});
        spdlog::register_logger(multi_logger);
        multi_logger->info("This goes to both console and file");
        
        // 每日日志
        auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt");
        daily_logger->info("Daily log message");
        
        // 刷新所有日志
        spdlog::drop_all();
    } catch (const spdlog::spdlog_ex& ex) {
        std::cout << "Log initialization failed: " << ex.what() << std::endl;
        return 1;
    }
    return 0;
}

性能建议

  1. 对于高性能场景,使用异步日志

  2. 在发布版本中考虑降低日志级别

  3. 避免在日志语句中进行复杂计算

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byxdaz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值