>_ DevTrendszh

语言

首页

语言

板块

前端 后端 移动端 DevOps AI / ML 游戏开发 区块链 嵌入式 安全
C-plus-plus

快速 C++ 日志库:无需繁琐配置和重量级依赖

任何写过比教程脚本更复杂的 C++ 代码的人,都面临过日志库的选择。标准库 std::cout 虽然方便,适合简单的控制台工具,但很快就会因为 I/O 速度慢和格式化功能不完善而成为瓶颈。而 Boost.Log 或 log4cxx 等替代方案则带来了大量依赖,编译时间长,配置复杂,令人望而却步。

2014 年,开发者 Gabriel Melman 发布了 spdlog 库。该项目迅速在 C++ 社区获得关注,目前在 GitHub 上已获得近 30,000 颗星。

它为何如此受欢迎

该库成功的首要原因在于性能与易用性的平衡。spdlog 以仅头文件的形式分发:只需将 include 目录复制到项目中,包含头文件即可开始使用。如果需要加快项目构建速度,也可以通过 CMake 预编译,或从主流包管理器(apt、brew、vcpkg、conan)安装。

字符串格式化基于 fmt 库,该库后来成为 C++20 中 std::format 的基础。这避免了使用 << 操作符进行繁琐的链式调用。对齐、十六进制数字输出和位置参数都能轻松清晰地实现。

日常开发中的实用功能

组合 Log Sink

在实际应用中,你经常需要将错误信息以彩色高亮方式输出到控制台,同时将详细的调试信息写入带轮转功能的文件。在 spdlog 中,可以通过将多个 sink 组合成一个 logger 对象来解决这个任务:

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"

void setup_logging() {
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::warn);

    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/app.txt", true);
    file_sink->set_level(spdlog::level::trace);

    spdlog::logger logger("multi_sink", {console_sink, file_sink});
    logger.info("Сообщение зафиксировано только в файле");
    logger.warn("Сообщение попадет и в консоль, и в файл");
}

高负载任务的异步模式

在网络服务器或游戏引擎的工作线程中,直接写入磁盘会延迟代码执行。spdlog 可以异步工作:后台线程从环形缓冲区中取出消息并刷新到磁盘,而不会减慢主计算。

#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"

void async_example() {
    // Очередь на 8192 элемента и 1 фоновый поток
    spdlog::init_thread_pool(8192, 1); 
    
    auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(
        "async_logger", "logs/async_log.txt"
    );
    
    async_file->info("Запись лога не блокирует текущий поток");
}

用于错误追踪的环形缓冲区

该库的一个有趣功能是支持 backtrace。假设生产环境的日志级别设置为 info,详细调试已禁用。当 backtrace 被激活时,库会将最后 N 条调试消息保存在内存中。当发生故障时,可以立即将它们转储到日志文件。

spdlog::enable_backtrace(32); // Сохраняем последние 32 сообщения в буфер

for (int i = 0; i < 100; ++i) {
    spdlog::debug("Отладочный шаг №{}", i); // Сообщения пока не пишутся в диск
}

// При возникновении ошибки выгружаем накопленный буфер
spdlog::dump_backtrace();

无需重新编译即可更改日志级别

日志级别可以在应用程序启动时直接从环境变量或命令行参数读取:

#include "spdlog/cfg/env.h"

int main(int argc, char* argv[]) {
    spdlog::cfg::load_env_levels();
    spdlog::info("Приложение успешно запущено");
}

使用 SPDLOG_LEVEL=debug ./my_app 标志运行二进制文件会立即激活调试消息输出。

性能基准测试

同步单线程日志记录器在处理短字符串时每秒可处理约 570 万次调用(在 Intel i7-4770 上的作者测试)。在 10 个竞争线程的情况下,速度保持在每秒约 160 万条消息。

在异步模式下使用溢出策略(overrun),该库每秒处理超过 260 万次操作。这些数字是通过最小化内存分配和快速字符串构建实现的。

现成的集成方案

该库支持 Linux、Windows、macOS、Android 和 FreeBSD。包中包含用于与 syslog、Windows 事件日志、Android logcat 和 Qt 文本小部件(QTextEdit)集成的现成 sink。此外,代码库中还包含一个 spdlog::stopwatch 秒表用于测量代码段的执行时间,以及一个用于以十六进制格式输出二进制数据的实用工具。

spdlog 以简洁的方式解决了日志记录问题。对于小型工具,只需复制头文件即可;对于大型服务,异步模式、文件轮转和实时消息过滤功能非常有用。如果你需要一个快速且可预测的 C++ 工具,将 spdlog 添加到你的技术栈应该是首要任务。

相关项目