目录
1. 标准输出(stdout)和标准错误(stderr)的日志
解释
spdlog 中的 sinks
常用的 spdlog sinks
示例:创建一个 rotating file sink 的日志器
2. 基本的文件日志器(basic file logger)
步骤
3. 每日日志文件
4. Rotating files
工作原理:
5. Backtrace support 缓存异常日志
代码解释:
输出结果:
使用场景:
6. Periodic flush 定期刷新日志缓冲区
使用场景:
代码解释:
注意事项:
7. StopWatch 计时工具
特性:
使用场景:
代码解释:
输出示例:
注意事项:
8. Log binary data in hex 记录二进制
特性介绍
支持的格式标志:
代码解释
输出示例
9. Logger with multi sinks - each with a different format and log level 设置日志级别
实现步骤:
代码解释:
输出示例(续):
总结
10. User-defined callbacks about log events 用户定义回调
特性介绍
代码解释
输出示例
总结
11. Asynchronous logging 异步日志记录
异步日志记录的特性
代码解释
注意事项
12. Asynchronous logger with multi sinks 带sink异步日志记录器
代码解释
输出结果
注意事项
13. User-defined types用户定义类型
代码解释
输出结果
总结
14. User-defined flags in the log pattern 用户定义日志模式
代码解释
输出示例
总结
15. Custom error handler 自定义错误处理
代码解释
输出示例
其他注意事项
16 syslog 系统日志
代码解释
输出结果
17. Android example
代码解释
查看日志
18.Load log levels from the env variable or argv
代码解释
使用示例
从环境变量加载日志级别
从命令行参数加载日志级别
可能的输出
总结
19. Log file open/close event handlers 打开和关闭事件注册回调函数。
代码解释
输出结果
总结
20. Replace the Default Logger 替换默认的日志记录器
代码解释
21. Log to Qt with nice colors
代码解释
运行效果
22. Mapped Diagnostic Context 附加特定的上下文信息(如用户 ID、会话 ID 等)
代码解释
运行结果
注意事项
总结
spdlog 是一个非常流行且高效的 C++ 日志库,主要用于跨平台的日志记录。
Very fast, header-only/compiled, C++ logging library.
github地址:
https://github.com/gabime/spdlog
1. 标准输出(stdout)和标准错误(stderr)的日志
#include "spdlog/spdlog.h"#include "spdlog/sinks/stdout_color_sinks.h"#include <memory>int stdoutAndstderr_logger() { /* 标准输出(stdout):通常用于输出普通信息,如程序的正常运行状态等。 标准错误(stderr):通常用于输出错误信息或警告,以便于在大量日志信息中突出显示这些重要信息。 spdlog 提供了多种日志 sink(即日志输出目标)来实现上述功能。你可以创建一个日志器,将多个 sink 组合在一起, 使其同时记录到 stdout 和 stderr。 */ // 创建 stdout 和 stderr sinks auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); auto stderr_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>(); // 创建一个 logger,组合这两个 sinks spdlog::logger logger("multi_sink", { stdout_sink, stderr_sink }); // 设置日志级别(可选) logger.set_level(spdlog::level::debug); // 使用 logger 记录日志 logger.info("This is an info message"); logger.warn("This is a warning message"); logger.error("This is an error message"); return 0;}
解释
标准输出(stdout):通常用于输出普通信息,如程序的正常运行状态等。标准错误(stderr):通常用于输出错误信息或警告,以便于在大量日志信息中突出显示这些重要信息。spdlog 提供了多种日志 sink(即日志输出目标)来实现上述功能。你可以创建一个日志器,将多个 sink 组合在一起,使其同时记录到 stdout 和 stderr。
spdlog 中的 sinks
spdlog 提供了一系列预定义的 sinks,供用户选择和组合,以满足不同的日志记录需求。每个 sink 都有不同的功能和特性,用户可以根据需要将多个 sinks 组合在一起,创建复杂的日志记录系统。
常用的 spdlog sinks
以下是一些常用的 spdlog sinks:
stdout_sink:将日志输出到标准输出(即控制台)。stderr_sink:将日志输出到标准错误。basic_file_sink:将日志输出到一个基础文件中。rotating_file_sink:将日志输出到一个文件中,并根据文件大小进行轮转。daily_file_sink:将日志输出到一个文件中,并根据日期进行轮转。null_sink:将日志丢弃,相当于禁用输出。syslog_sink:将日志输出到系统日志(如 Linux 的 syslog)。tcp_sink:将日志输出到 TCP 网络连接。udp_sink:将日志输出到 UDP 网络连接。示例:创建一个 rotating file sink 的日志器
#include "spdlog/spdlog.h"#include "spdlog/sinks/rotating_file_sink.h"#include <memory>int main() { // 创建一个 rotating file sink auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("logs/rotating_log.txt", 1024*1024*5, 3); // 创建一个 logger,使用 rotating file sink spdlog::logger logger("file_logger", rotating_sink); // 设置日志级别(可选) logger.set_level(spdlog::level::info); // 使用 logger 记录日志 logger.info("This is an info message in a rotating log file"); return 0;}
在 spdlog 中,sinks 是日志输出的目标,可以将日志记录到控制台、文件或网络等不同的地方。通过组合多个 sinks,用户可以灵活地配置日志记录系统,以满足复杂的日志记录需求。
2. 基本的文件日志器(basic file logger)
步骤
包含头文件:需要包含 spdlog 的核心头文件以及用于文件日志器的头文件。创建日志器:使用basic_file_sink_mt
创建一个文件日志器。记录日志:使用日志器记录不同级别的日志信息。 #include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"void Basic_file_logger(bool isOpen){ /*包含头文件:需要包含 spdlog 的核心头文件以及用于文件日志器的头文件。 创建日志器:使用 basic_file_sink_mt 创建一个文件日志器。 记录日志:使用日志器记录不同级别的日志信息。*/ if (!isOpen) return; // 创建一个文件日志器 auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt"); // 设置日志级别(可选) file_logger->set_level(spdlog::level::debug); // 设置日志格式(可选) file_logger->set_pattern("[%Y-%m-%d %H:%M:%S] [%l] %v"); // 使用日志器记录日志 file_logger->debug("This is a debug message"); file_logger->info("This is an info message"); file_logger->warn("This is a warning message"); file_logger->error("This is an error message"); file_logger->critical("This is a critical message");}
3. 每日日志文件
spdlog 提供了 daily_file_sink
汇编器来创建每日日志文件。以下是使用方法的示例:
#include "spdlog/spdlog.h"#include "spdlog/sinks/daily_file_sink.h"void Daily_files(bool isOpen){ /* * 在这个例子中,daily_file_sink 每天都会创建一个名为 my_daily_log.txt 的新日志文件。 日志消息将被写入此文件。 */ if (!isOpen) return; // 创建名为“my_daily_log”并写入“my_daily_log.txt”的每日记录器 auto daily_logger = spdlog::daily_logger_st("my_daily_log", "daily_logger/my_daily_log.txt"); // 将日志级别设置为调试 daily_logger->set_level(spdlog::level::debug); // 记录一些消息 daily_logger->info("这是一条信息消息"); daily_logger->debug("这是一条调试消息"); /* 您还可以通过向 daily_file_sink 构造函数传递附加参数来自定义每日日志文件名和其他选项。 例如,以下代码将创建名为 my_daily_log_%Y-%m-%d.txt 的每日日志文件: */ auto daily_logger_ = spdlog::daily_logger_st("my_daily_log_", "daily_logger/my_daily_log_%Y-%m-%d.txt"); daily_logger_->info("这是一条信息消息"); daily_logger_->debug("这是一条调试消息");}
#include "spdlog/sinks/daily_file_sink.h"void daily_example(){ // Create a daily logger - a new file is created every day at 2:30 am auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);}
4. Rotating files
Rotating Files(轮转文件):指的是日志文件达到一定大小或者数量后,旧的日志文件会被重命名保存,新的日志内容会写入到一个新的文件中。这种机制有助于控制日志文件的大小和数量,从而避免磁盘被大量日志文件占满。
#include "spdlog/sinks/rotating_file_sink.h"void rotating_example(){ // Create a file rotating logger with 5 MB size max and 3 rotated files auto max_size = 1048576 * 5; auto max_files = 3; auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);}
#include "spdlog/spdlog.h"#include "spdlog/sinks/rotating_file_sink.h"int main(){ // 创建一个轮转日志记录器,文件大小为5MB,最多保留3个日志文件 auto logger = spdlog::rotating_logger_mt("rotating_logger", "logs/mylog.txt", 1048576 * 5, 3); // 写日志 logger->info("This is an info message"); // 刷新日志(将缓存内容写入文件) spdlog::flush_on(spdlog::level::info); return 0;}
工作原理:
在logs/
目录下,mylog.txt
将成为主要日志文件。当 mylog.txt
的大小达到 5MB 时,spdlog
会将它重命名为 mylog.1.txt
,新的日志内容将写入新的 mylog.txt
。这个过程会继续进行,直到有 3 个日志文件(mylog.txt
, mylog.1.txt
, mylog.2.txt
),最旧的日志文件会被删除或覆盖。 5. Backtrace support 缓存异常日志
spdlog
提供了一个 backtrace
特性,可以在日志记录过程中收集一定数量的日志消息(即使这些消息没有被输出到日志文件)。当出现某种关键事件时,例如错误或异常,开发者可以选择将收集到的 backtrace
输出,这样就能查看在事件发生之前发生了什么。
#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"int main(){ // 创建一个日志记录器并启用 backtrace 支持 auto logger = spdlog::basic_logger_mt("basic_logger", "logs/backtrace_log.txt"); // 启用 backtrace,并设置缓存 10 条日志 logger->enable_backtrace(10); // 日志记录示例(这些日志将缓存到 backtrace 中,而不是立即写入文件) for (int i = 0; i < 15; ++i) { logger->debug("This is a debug message {}", i); } // 模拟一个错误,并输出 backtrace 缓存的日志 try { throw std::runtime_error("An error occurred!"); } catch (const std::exception &e) { // 记录异常信息 logger->error("Exception caught: {}", e.what()); // 输出 backtrace 缓存的日志 logger->dump_backtrace(); } return 0;}
代码解释:
启用backtrace
:使用 logger->enable_backtrace(10);
来启用 backtrace 功能,并指定最多缓存 10 条日志。日志记录:循环记录 15 条 debug 级别的日志信息。这些日志不会立即写入文件,而是先被缓存起来。模拟错误:在 try-catch
代码块中抛出一个异常,并在 catch
块中捕获异常。输出 backtrace:在捕获异常后,使用 logger->dump_backtrace();
将缓存的日志输出到日志文件。这会帮助开发者查看在异常发生前,程序经历了哪些函数调用和日志记录。 输出结果:
在日志文件 backtrace_log.txt
中,你将看到异常发生之前的 10 条日志记录,这些信息将帮助你调试和追踪程序执行过程中的问题。
使用场景:
错误调试:当程序抛出异常或出现错误时,backtrace
能够帮助开发者查看程序执行的历史记录,从而快速定位问题。复杂系统:在复杂的应用程序中,程序可能会经过多个函数调用后才发生错误,backtrace
提供了关键的上下文信息,帮助开发者理解程序的执行路径。 6. Periodic flush 定期刷新日志缓冲区
Periodic flush
是 spdlog
提供的一种功能,用于定期刷新日志缓冲区。默认情况下,spdlog
会将日志消息保存在内存缓冲区中,并在需要时(如日志缓冲区满或手动调用 flush
函数)将这些消息写入目标日志文件或输出流。使用 Periodic flush
功能,开发者可以设置一个固定的时间间隔,让 spdlog
自动定期刷新日志缓冲区,将消息写入磁盘或输出流。这对于确保日志数据不会因应用程序异常终止而丢失非常有用。
使用场景:
可靠性:在长时间运行的应用程序中,定期刷新日志可以防止日志消息因为应用崩溃或异常终止而丢失。实时性:定期刷新可以确保日志更接近实时反映程序状态,特别是在调试和监控应用程序时。#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"int main(){ // 创建一个基本的文件日志记录器 auto logger = spdlog::basic_logger_mt("periodic_logger", "logs/periodic_flush_log.txt"); // 设置日志记录器的刷新间隔为3秒 spdlog::flush_every(std::chrono::seconds(3)); // 写入一些日志消息 for (int i = 0; i < 10; ++i) { logger->info("Logging message number {}", i); std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟一些处理延迟 } // 结束前手动刷新一次 spdlog::shutdown(); return 0;}
代码解释:
创建日志记录器:使用spdlog::basic_logger_mt
创建一个记录日志到文件的日志记录器,日志文件名为 periodic_flush_log.txt
。设置定期刷新间隔:使用 spdlog::flush_every
设置日志记录器每 3 秒刷新一次缓冲区,将日志消息写入文件。日志记录:在一个循环中写入 10 条日志消息,每条日志消息之间有 1 秒的延迟。手动刷新并关闭:在程序结束前,使用 spdlog::shutdown()
手动刷新一次所有日志记录器并关闭它们,以确保所有日志都被写入文件。 注意事项:
性能权衡:定期刷新虽然能提高日志的实时性和可靠性,但频繁的刷新操作可能会影响性能,特别是在高频率日志记录的情况下。开发者需要根据应用程序的实际需求权衡刷新频率与性能之间的关系。多日志记录器环境:在一个程序中可能会有多个日志记录器,flush_every
适用于所有日志记录器,而不仅仅是某一个特定的日志记录器。 7. StopWatch 计时工具
Stopwatch
是 spdlog
提供的一个简单的计时工具,用于测量代码块执行的时间。这对性能分析和优化非常有用,因为它可以帮助开发者了解某段代码的执行时间。
特性:
高精度计时:Stopwatch
使用高精度的时钟来测量时间,可以精确到微秒或更高。便捷性:Stopwatch
的接口简单易用,可以直接嵌入代码中用于快速的性能测试。 使用场景:
性能调优:用于测试代码块执行时间,找出性能瓶颈。调试:在调试时,可以查看某段代码的执行时间,以便理解代码的效率。// Stopwatch support for spdlog#include "spdlog/stopwatch.h"void stopwatch_example(){ spdlog::stopwatch sw; spdlog::debug("Elapsed {}", sw); spdlog::debug("Elapsed {:.3}", sw); }
#include "spdlog/spdlog.h"#include "spdlog/stopwatch.h"#include <thread>int main(){ // 创建一个 stopwatch 实例 spdlog::stopwatch sw; // 模拟一些工作负载(睡眠2秒) std::this_thread::sleep_for(std::chrono::seconds(2)); // 记录执行时间 spdlog::info("Elapsed time: {} seconds", sw.elapsed().count()); return 0;}
代码解释:
创建stopwatch
实例:spdlog::stopwatch sw;
创建了一个 stopwatch
对象,计时从对象创建时自动开始。模拟工作负载:std::this_thread::sleep_for
函数用于模拟一个耗时 2 秒的操作。这个操作可以替换为实际需要测量的代码。记录执行时间:使用 sw.elapsed().count()
获取经过的时间,并以秒为单位输出日志。 输出示例:
当你运行这段代码时,输出日志将类似于:
[info] Elapsed time: 2.000123 seconds
注意事项:
精度:stopwatch
的精度取决于系统的时钟,通常可以达到微秒级别,但在某些平台上可能受限于硬件或操作系统的计时精度。多个计时器:你可以在同一个程序中创建多个 stopwatch
实例,用于测量不同代码块的执行时间。 8. Log binary data in hex 记录二进制
spdlog
提供了一种方便的方法来记录二进制数据,并将其以十六进制格式输出。这个特性特别有用,当你需要调试或分析底层数据时,例如网络数据包、文件内容或硬件接口数据。在 spdlog
中,这个功能是通过 fmt/bin_to_hex.h
实现的,它允许开发者将各种类型的二进制数据转换为十六进制格式,并灵活地控制输出格式。
特性介绍
容器支持:spdlog
的 to_hex
函数支持多种标准容器,如 std::vector<char>
, std::array<char>
, std::string
等,甚至支持原生指针或迭代器范围。格式标志:通过格式标志,开发者可以控制十六进制输出的样式,例如是否大写、是否显示字节分隔符、是否显示 ASCII 字符等。 支持的格式标志:
{:X}
:以大写字母显示十六进制数据(例如 A-F
)。{:s}
:不使用空格分隔每个字节。{:p}
:不在每行开头显示位置信息(偏移量)。{:n}
:不将输出拆分为多行。{:a}
:如果未设置 :n
标志,显示 ASCII 表示形式。 // many types of std::container<char> types can be used.// ranges are supported too.// format flags:// {:X} - print in uppercase.// {:s} - don't separate each byte with space.// {:p} - don't print the position on each line start.// {:n} - don't split the output into lines.// {:a} - show ASCII if :n is not set.#include "spdlog/fmt/bin_to_hex.h"void binary_example(){ auto console = spdlog::get("console"); std::array<char, 80> buf; console->info("Binary example: {}", spdlog::to_hex(buf)); console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); // more examples: // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));}
#include "spdlog/spdlog.h"#include "spdlog/fmt/bin_to_hex.h"#include <array>void binary_example(){ // 获取一个控制台日志记录器 auto console = spdlog::get("console"); // 创建一个包含二进制数据的缓冲区 std::array<char, 80> buf = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'S', 'p', 'd', 'l', 'o', 'g', '!'}; // 以默认格式记录二进制数据 console->info("Binary example: {}", spdlog::to_hex(buf)); // 仅记录前10个字节,并且不换行 console->info("Another binary example: {:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); // 更多格式示例: // 大写输出 console->info("Uppercase: {:X}", spdlog::to_hex(buf)); // 大写输出,且不使用分隔符 console->info("Uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); // 大写输出,不使用分隔符,且不显示位置信息 console->info("Uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); // 默认格式,并附带 ASCII 显示 console->info("With ASCII: {}", spdlog::to_hex(buf));}int main(){ // 初始化控制台日志记录器 spdlog::set_level(spdlog::level::info); // 设置日志级别 spdlog::set_default_logger(spdlog::stdout_color_mt("console")); // 执行二进制日志示例 binary_example(); return 0;}
代码解释
包含必要的头文件:spdlog/spdlog.h
是日志功能的核心头文件,spdlog/fmt/bin_to_hex.h
提供了将二进制数据转换为十六进制的功能。创建并填充缓冲区:std::array<char, 80>
是一个包含二进制数据的缓冲区,示例中填入了一些字符数据。记录二进制数据: 默认记录二进制数据为十六进制格式。使用不同的格式标志控制输出样式,如是否大写、是否显示分隔符和偏移量等。初始化日志记录器:使用 spdlog::stdout_color_mt("console")
初始化控制台日志记录器,并设置日志级别。 输出示例
运行上述代码后,控制台将显示类似以下的输出:
[info] Binary example: 0000: 48 65 6c 6c 6f 2c 20 53 70 64 6c 6f 67 21 ...[info] Another binary example: 48656c6c6f2c205370[info] Uppercase: 0000: 48 65 6C 6C 6F 2C 20 53 70 64 6C 6F 67 21 ...[info] Uppercase, no delimiters: 48656C6C6F2C205370[info] Uppercase, no delimiters, no position info: 48656C6C6F2C205370[info] With ASCII: 0000: 48 65 6c 6c 6f 2c 20 53 70 64 6c 6f 67 21 Hello, Spdlog!
9. Logger with multi sinks - each with a different format and log level 设置日志级别
spdlog
支持创建具有多个 "sinks"(日志输出目标)的日志记录器,并且每个 sink 可以有不同的格式和日志级别。这种能力使得开发者可以灵活地将日志输出到不同的目标(如文件、控制台等),并为每个目标设置不同的日志级别和格式,以满足不同的日志记录需求。
实现步骤:
创建多个 sinks:每个 sink 可以指定不同的输出目标(如文件、控制台)以及不同的格式和日志级别。将这些 sinks 组合成一个 logger:这个 logger 将日志同时输出到多个目标。使用 logger:当日志消息被记录时,它会根据每个 sink 的设置输出到相应的目标。// create a logger with 2 targets, with different log levels and formats.// The console will show only warnings or errors, while the file will log all.void multi_sink_example(){ auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); console_sink->set_level(spdlog::level::warn); console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true); file_sink->set_level(spdlog::level::trace); spdlog::logger logger("multi_sink", {console_sink, file_sink}); logger.set_level(spdlog::level::debug); logger.warn("this should appear in both console and file"); logger.info("this message should not appear in the console, only in the file");}
#include "spdlog/spdlog.h"#include "spdlog/sinks/stdout_color_sinks.h"#include "spdlog/sinks/basic_file_sink.h"int main() { // 创建控制台 sink,并设置日志级别为 info 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] [%^%l%$] %v"); // 创建文件 sink,并设置日志级别为 debug auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink_log.txt", true); file_sink->set_level(spdlog::level::debug); file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [thread %t] %v"); // 创建一个带有多个 sinks 的 logger spdlog::logger logger("multi_sink", {console_sink, file_sink}); // 设置 logger 的日志级别为 debug(这样所有级别的日志都能被输出) logger.set_level(spdlog::level::debug); // 使用 logger 记录不同级别的日志 logger.debug("This is a debug message"); logger.info("This is an info message"); logger.warn("This is a warning message"); logger.error("This is an error message"); // 刷新日志(将缓冲区的内容写入目标) spdlog::shutdown(); return 0;}
代码解释:
创建控制台 sink:
使用stdout_color_sink_mt
创建了一个多线程安全的彩色控制台输出 sink。设置了日志级别为 info
,表示只有 info
级别及更高级别的日志会输出到控制台。使用 set_pattern
方法设置了日志的输出格式,例如时间、日志级别、消息内容等。 创建文件 sink:
使用basic_file_sink_mt
创建了一个文件输出 sink,日志会被记录到 logs/multisink_log.txt
文件中。设置了日志级别为 debug
,这样即使是调试信息也会被写入到文件中。同样使用 set_pattern
设置了文件日志的格式,包含日期、时间、线程 ID 等信息。 创建带有多个 sinks 的 logger:
使用spdlog::logger
创建了一个 logger,并将之前创建的控制台和文件 sinks 传递给它。设置 logger 的日志级别为 debug
,确保所有级别的日志消息都会被发送到各个 sink。 记录日志:
使用 logger 记录了不同级别的日志消息。由于控制台 sink 的日志级别为info
,因此 debug
级别的消息不会显示在控制台上,但会写入文件。 日志刷新与关闭:
在程序结束前,使用spdlog::shutdown()
确保所有日志数据被正确刷新到目标位置。 输出示例(续):
控制台输出(由于控制台的日志级别设置为 info
,因此 debug
消息不会显示):
[12:34:56] [INFO] This is an info message[12:34:56] [WARN] This is a warning message[12:34:56] [ERROR] This is an error message
文件输出(文件的日志级别设置为 debug
,所以所有日志消息都被记录到文件中):
[2024-08-11 12:34:56.123] [debug] [thread 1] This is a debug message[2024-08-11 12:34:56.124] [info] [thread 1] This is an info message[2024-08-11 12:34:56.125] [warn] [thread 1] This is a warning message[2024-08-11 12:34:56.126] [error] [thread 1] This is an error message
总结
通过这种方式,你可以在一个日志记录器中同时使用多个 sinks,并为每个 sink 定制不同的日志级别和格式。这样可以满足不同的日志记录需求,比如将详细的调试信息保存到文件中,同时在控制台上只显示更为重要的日志消息。这种多 sink 的配置在复杂应用程序中非常实用,尤其是在需要将日志输出到多个目标时,如同时记录到控制台、文件、远程服务器等。
10. User-defined callbacks about log events 用户定义回调
spdlog
还允许用户定义回调函数,用于在日志事件发生时执行自定义操作。这种功能在需要对特定的日志事件采取即时措施时非常有用,例如发送警报、通知、或者执行其他逻辑。
特性介绍
用户定义回调:你可以为日志记录器设置一个回调函数,当满足特定条件的日志消息被记录时,该回调函数会被调用。这允许开发者在日志记录时执行额外的操作,例如发送通知或执行自定义的业务逻辑。灵活性:回调函数可以根据日志级别、内容或其他条件来触发不同的操作,这使得日志处理更加灵活和强大。// create a logger with a lambda function callback, the callback will be called// each time something is logged to the loggervoid callback_example(){ auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg &msg) { // for example you can be notified by sending an email to yourself }); callback_sink->set_level(spdlog::level::err); auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink}); logger.info("some info log"); logger.error("critical issue"); // will notify you}
#include "spdlog/spdlog.h"#include "spdlog/sinks/stdout_color_sinks.h"#include "spdlog/sinks/callback_sink.h"#include <memory>void callback_example(){ // 创建一个回调 sink auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg &msg) { // 自定义回调逻辑,例如发送通知或邮件 std::string log_message(msg.payload.begin(), msg.payload.end()); // 在这里,你可以执行任意操作,例如发送邮件、记录到数据库、调用外部API等。 std::cout << "Callback triggered: " << log_message << std::endl; }); // 设置回调 sink 只处理 error 级别及以上的日志 callback_sink->set_level(spdlog::level::err); // 创建一个控制台 sink auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); // 创建一个包含多个 sinks 的 logger spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink}); // 记录不同级别的日志 logger.info("some info log"); // 只会输出到控制台,不触发回调 logger.error("critical issue"); // 会输出到控制台并触发回调}int main(){ callback_example(); return 0;}
代码解释
创建回调 sink:
使用spdlog::sinks::callback_sink_mt
创建一个多线程安全的回调 sink,并传入一个 lambda 函数作为回调。这段代码中的回调函数简单地将日志消息输出到控制台,但你可以在此处添加更复杂的逻辑,如发送邮件或执行其他操作。msg
是 spdlog::details::log_msg
类型的结构,包含了日志消息及其元数据。你可以使用它来获取消息内容或其他相关信息。 设置日志级别:
使用callback_sink->set_level(spdlog::level::err);
设置回调 sink 只处理 error
级别及以上的日志。这意味着只有在记录 error
或更严重的日志时,回调函数才会被触发。 创建控制台 sink:
stdout_color_sink_mt
用于将日志输出到控制台,并且支持彩色输出。 创建带有多个 sinks 的 logger:
spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink});
创建了一个包含两个 sinks 的日志记录器。这个记录器会将日志输出到控制台,同时在满足条件时触发回调。 记录日志:
logger.info("some info log");
记录一条 info
级别的日志,这条日志只会输出到控制台,不会触发回调。logger.error("critical issue");
记录一条 error
级别的日志,这条日志不仅会输出到控制台,还会触发回调函数。 输出示例
运行上述代码后,控制台的输出将类似于以下内容:
[12:34:56] [info] some info log[12:34:56] [error] critical issueCallback triggered: critical issue
info
级别的日志只会输出到控制台。error
级别的日志既会输出到控制台,又会触发回调,打印出 “Callback triggered: critical issue”。 总结
通过使用 spdlog
的回调功能,你可以在记录日志的同时,执行自定义的操作,如发送通知、执行额外的业务逻辑等。这使得 spdlog
不仅仅是一个简单的日志库,还可以用作事件驱动的工具,在特定的日志事件发生时,自动触发相应的响应措施。这种能力对于实时监控、报警系统或任何需要对特定日志事件采取行动的应用程序非常有用。
11. Asynchronous logging 异步日志记录
spdlog
支持异步日志记录,这意味着日志消息可以在一个独立的线程中被处理和写入日志目标,而不会阻塞主线程的执行。这对于高性能应用程序尤为重要,因为它可以显著减少因日志记录导致的性能开销。
异步日志记录的特性
非阻塞:日志记录操作在独立线程中异步处理,主线程无需等待日志写入完成。高效:适用于高吞吐量的日志记录需求,尤其在日志量大且对性能敏感的场景下。队列管理:spdlog
通过内部的队列管理日志消息,可以控制队列的大小及溢出策略。 #include "spdlog/async.h"#include "spdlog/sinks/basic_file_sink.h"void async_example(){ // default thread pool settings can be modified *before* creating the async logger: // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread. auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt"); // alternatively: // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt"); }
#include "spdlog/spdlog.h"#include "spdlog/async.h" // 支持异步日志#include "spdlog/sinks/basic_file_sink.h" // 支持文件输出int main(){ // 设置异步日志模式,并指定队列大小为 8192 条日志消息 spdlog::init_thread_pool(8192, 1); // 创建一个异步文件 sink auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async_log.txt", true); // 创建一个异步日志记录器 auto async_logger = std::make_shared<spdlog::async_logger>("async_logger", file_sink, spdlog::thread_pool(), spdlog::async_overflow_policy::block); // 设置异步日志记录器为全局默认日志记录器(可选) spdlog::set_default_logger(async_logger); // 使用异步日志记录器记录一些日志消息 for (int i = 0; i < 10000; ++i) { async_logger->info("This is async log message number {}", i); } // 刷新日志,确保所有消息都被写入文件 spdlog::shutdown(); return 0;}
代码解释
初始化线程池:
spdlog::init_thread_pool(8192, 1);
初始化一个线程池用于异步日志记录。第一个参数 8192
表示队列的最大消息数,第二个参数 1
表示线程池中线程的数量。 创建异步 sink:
std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async_log.txt", true);
创建了一个文件 sink,用于将日志记录到文件 async_log.txt
中。true
参数表示文件为追加模式,即新的日志将被追加到已有文件的末尾。 创建异步 logger:
spdlog::async_logger
创建一个异步日志记录器。spdlog::thread_pool()
获取之前初始化的线程池,用于处理异步日志。spdlog::async_overflow_policy::block
指定当队列满时,主线程将阻塞等待队列有空余位置。 设置默认日志记录器(可选):
使用spdlog::set_default_logger(async_logger);
将这个异步日志记录器设置为默认的日志记录器,这样在程序的其他地方可以直接使用 spdlog::info
等方法进行日志记录。 记录日志:
通过for
循环记录大量日志消息。因为是异步日志记录,主线程不会因日志写入而被阻塞,日志会被异步写入文件。 刷新和关闭:
在程序结束前,调用spdlog::shutdown();
以确保所有日志消息都被处理并写入到文件。 [info] This is async log message number 0[info] This is async log message number 1...[info] This is async log message number 9999
注意事项
队列大小:队列大小的设置需要根据应用的日志量和处理能力进行调整。如果队列过小,可能会导致日志消息溢出或主线程阻塞。异步日志的潜在延迟:由于日志是在后台线程中处理的,日志消息可能不会立即写入目标。对于需要即时记录的关键日志,可能需要考虑同步日志记录或手动刷新。12. Asynchronous logger with multi sinks 带sink异步日志记录器
在 spdlog
中,您可以创建一个异步日志记录器,并将多个 sink
(输出目标)组合在一起使用。这允许您同时将日志输出到多个目标(例如文件、控制台等),而且这些操作都是异步进行的,不会阻塞主线程。
#include "spdlog/spdlog.h"#include "spdlog/async.h" // 支持异步日志#include "spdlog/sinks/basic_file_sink.h" // 文件输出 sink#include "spdlog/sinks/stdout_color_sinks.h" // 控制台输出 sinkint main(){ // 初始化异步日志线程池,队列大小为 8192,1 个后台线程 spdlog::init_thread_pool(8192, 1); // 创建一个控制台 sink auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); console_sink->set_level(spdlog::level::info); console_sink->set_pattern("[console] [%^%l%$] %v"); // 创建一个文件 sink auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/async_multisink_log.txt", true); file_sink->set_level(spdlog::level::debug); file_sink->set_pattern("[file] [%Y-%m-%d %H:%M:%S.%e] [%l] [thread %t] %v"); // 创建一个异步日志记录器,包含多个 sinks auto async_logger = std::make_shared<spdlog::async_logger>( "async_logger", spdlog::sinks_init_list({console_sink, file_sink}), spdlog::thread_pool(), spdlog::async_overflow_policy::block); // 设置全局默认 logger(可选) spdlog::set_default_logger(async_logger); // 记录日志 async_logger->info("This is an info message"); async_logger->debug("This is a debug message"); // 大量日志记录 for (int i = 0; i < 10000; ++i) { async_logger->debug("Debug message number {}", i); async_logger->info("Info message number {}", i); } // 确保所有日志都被写入 spdlog::shutdown(); return 0;}
代码解释
初始化线程池:
spdlog::init_thread_pool(8192, 1);
初始化了一个大小为 8192 的队列和 1 个处理线程的线程池,专用于异步日志记录。 创建多个 sinks:
控制台 sink:stdout_color_sink_mt
用于将日志输出到控制台,并设置了日志级别为 info
,日志格式带有自定义的控制台前缀。文件 sink:basic_file_sink_mt
用于将日志记录到文件 async_multisink_log.txt
中,日志级别为 debug
,并且带有时间戳、线程 ID 等详细信息。 创建异步日志记录器:
使用spdlog::async_logger
创建一个异步日志记录器 async_logger
,将控制台 sink 和文件 sink 组合在一起。所有日志操作都在后台线程中处理,不会阻塞主线程。spdlog::async_overflow_policy::block
指定当日志队列满时,主线程将阻塞,等待队列腾出空间。 记录日志:
通过async_logger->info()
和 async_logger->debug()
记录了几条日志消息。循环记录大量日志消息以测试异步日志记录的性能。 日志刷新与关闭:
在程序结束前,调用spdlog::shutdown();
确保所有日志消息都被处理并写入到各个目标。 输出结果
控制台输出(只会输出 info
级别及以上的日志):
[console] [info] This is an info message[console] [info] Info message number 0[console] [info] Info message number 1...
文件输出(会输出 debug
级别及以上的所有日志):
[file] [2024-08-11 12:34:56.123] [debug] [thread 1] This is a debug message[file] [2024-08-11 12:34:56.124] [info] [thread 1] This is an info message[file] [2024-08-11 12:34:56.125] [debug] [thread 1] Debug message number 0[file] [2024-08-11 12:34:56.126] [info] [thread 1] Info message number 0...
注意事项
队列大小:异步日志记录器依赖队列来管理日志消息的处理,队列大小的设置需要根据日志量和性能要求进行权衡。性能优化:对于高性能需求的场景,合理配置线程池和队列可以最大化性能,同时避免队列溢出或主线程阻塞。13. User-defined types用户定义类型
在 spdlog
中,您可以为自定义类型定义格式化规则,使其能够直接在日志消息中使用。这是通过 fmt
库的模板特化来实现的,spdlog
基于 fmt
库构建,因此可以使用相同的机制来格式化自定义类型。
template<>struct fmt::formatter<my_type> : fmt::formatter<std::string>{ auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "[my_type i={}]", my.i); }};void user_defined_example(){ spdlog::info("user defined type: {}", my_type(14));}
#include "spdlog/spdlog.h"#include "spdlog/fmt/ostr.h" // 需要包含这个头文件来支持 ostream 操作符// 定义一个自定义类型struct my_type{ int i; my_type(int val) : i(val) {}};// 为自定义类型定义 fmt 格式化器template<>struct fmt::formatter<my_type> : fmt::formatter<std::string>{ // 定义如何格式化 my_type auto format(my_type my, fmt::format_context &ctx) const -> decltype(ctx.out()) { return format_to(ctx.out(), "[my_type i={}]", my.i); }};// 使用自定义类型的示例函数void user_defined_example(){ spdlog::info("user defined type: {}", my_type(14));}int main(){ // 初始化 spdlog spdlog::set_level(spdlog::level::info); // 设置日志级别 spdlog::set_default_logger(spdlog::stdout_color_mt("console")); // 使用彩色控制台输出 // 调用示例函数 user_defined_example(); return 0;}
代码解释
定义自定义类型 my_type
:
i
,并通过构造函数进行初始化。 定义 fmt::formatter
模板特化:
fmt::formatter<my_type>
模板特化来定义 my_type
的格式化规则。format
方法用于定义如何将 my_type
格式化为字符串。在 format
方法中,我们使用 format_to
将格式化后的字符串写入 ctx.out()
,这里的格式是 [my_type i={}]
,其中 {}
是 my_type
的成员 i
的值。 使用自定义类型的示例函数:
在user_defined_example
函数中,我们使用 spdlog::info
记录一个包含 my_type
的日志消息。由于我们已经为 my_type
定义了格式化器,spdlog
可以正确地格式化并输出 my_type
类型的值。 初始化 spdlog
并调用示例函数:
info
,并初始化一个彩色控制台日志记录器。调用 user_defined_example
函数来演示如何记录包含自定义类型的日志消息。 输出结果
运行上述代码后,控制台将显示类似以下的输出:
[info] user defined type: [my_type i=14]
这表明 my_type
类型的值 14
被正确格式化并输出在日志中。
总结
通过为自定义类型定义 fmt::formatter
,您可以让 spdlog
直接处理这些类型,并在日志中以自定义的格式输出。这种机制非常灵活,适用于各种复杂类型的日志记录需求,使您的日志记录系统更加通用和强大。
14. User-defined flags in the log pattern 用户定义日志模式
在 spdlog
中,您可以定义自定义的日志模式标志,通过继承 spdlog::custom_flag_formatter
类来实现。这允许您将特定的信息或格式直接嵌入到日志输出的模式中,从而增强日志的可读性或增加特定的上下文信息。
#include "spdlog/spdlog.h"#include "spdlog/pattern_formatter.h"// 定义自定义的格式化标志类class my_formatter_flag : public spdlog::custom_flag_formatter{public: // 格式化方法,向日志中添加自定义文本 void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override { std::string some_txt = "custom-flag"; dest.append(some_txt.data(), some_txt.data() + some_txt.size()); } // 克隆方法,用于复制自定义格式化器 std::unique_ptr<custom_flag_formatter> clone() const override { return spdlog::details::make_unique<my_formatter_flag>(); }};// 使用自定义标志的示例函数void custom_flags_example(){ // 创建一个自定义的日志格式化器 auto formatter = std::make_unique<spdlog::pattern_formatter>(); // 添加自定义标志 '%',并设置日志输出模式 formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v"); // 设置全局日志格式化器 spdlog::set_formatter(std::move(formatter)); // 记录一些日志消息 spdlog::info("This is an info message with a custom flag."); spdlog::warn("This is a warning message with a custom flag.");}int main(){ // 初始化 spdlog spdlog::set_level(spdlog::level::info); // 设置日志级别 spdlog::set_default_logger(spdlog::stdout_color_mt("console")); // 使用彩色控制台输出 // 调用示例函数 custom_flags_example(); return 0;}
代码解释
定义自定义格式化标志类:
my_formatter_flag
类继承自 spdlog::custom_flag_formatter
,并实现了两个方法: format
:该方法用于在日志输出中插入自定义的文本。此示例中插入了 "custom-flag"
。clone
:该方法返回一个指向新创建的 my_formatter_flag
实例的唯一指针,用于复制格式化器。 创建自定义日志格式化器:
spdlog::pattern_formatter
是 spdlog
中用于定义日志模式的类。formatter->add_flag<my_formatter_flag>('*')
将自定义标志 %*
绑定到 my_formatter_flag
实例。formatter->set_pattern("[%n] [%*] [%^%l%$] %v");
设置日志输出模式,包含自定义标志 %*
。 设置全局日志格式化器:
spdlog::set_formatter(std::move(formatter));
将自定义的格式化器设置为全局默认的日志格式化器。 记录日志消息:
使用spdlog::info
和 spdlog::warn
记录日志消息,输出的日志将包含自定义标志生成的文本。 输出示例
运行上述代码后,控制台将显示类似以下的输出:
[console] [custom-flag] [info] This is an info message with a custom flag.[console] [custom-flag] [warn] This is a warning message with a custom flag.
总结
通过扩展 spdlog::custom_flag_formatter
,您可以为 spdlog
添加自定义的日志模式标志。这使得您能够根据具体需求增强日志输出的格式和内容,从而为日志增加更多的上下文信息或自定义标识。这种方法特别适用于复杂应用程序的日志管理需求,可以根据不同的场景灵活调整日志的输出内容。
15. Custom error handler 自定义错误处理
在 spdlog
中,您可以设置自定义错误处理程序,当日志记录过程中发生错误时,该错误处理程序将被调用。错误处理程序可以是全局的,也可以是针对特定日志记录器的。这样可以使您在遇到日志记录问题时执行自定义操作,比如记录错误信息、发送警报等。
#include "spdlog/spdlog.h"#include "spdlog/sinks/stdout_color_sinks.h"void err_handler_example(){ // 设置一个默认的控制台日志记录器 auto console = spdlog::stdout_color_mt("console"); // 设置全局错误处理程序 spdlog::set_error_handler([](const std::string &msg) { // 当发生日志错误时,记录一条错误信息 spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); }); // 试图记录一条格式错误的日志消息以触发错误 spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);}int main(){ // 调用示例函数 err_handler_example(); return 0;}
代码解释
创建日志记录器:
使用spdlog::stdout_color_mt("console")
创建了一个彩色控制台日志记录器,并将其命名为 "console"
。 设置全局错误处理程序:
使用spdlog::set_error_handler
设置全局错误处理程序。错误处理程序是一个 lambda 函数,它接受一个 std::string
类型的参数 msg
,该参数包含了错误信息。当日志记录过程中发生错误时,该处理程序会被调用,并将错误信息输出到控制台。 触发日志记录错误:
试图记录一条格式错误的日志消息spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
。由于占位符 {}
的数量不匹配,这会触发一个日志记录错误。 错误处理:
当上述错误发生时,之前设置的错误处理程序会被调用,并将错误信息记录下来。输出示例
运行上述代码后,控制台将显示类似以下的输出:
[2024-08-11 12:34:56] [error] *** LOGGER ERROR ***: format error: argument index out of range
*** LOGGER ERROR ***: format error: argument index out of range
是由自定义错误处理程序生成的错误信息。
其他注意事项
局部错误处理程序:如果您只想为某个特定的日志记录器设置错误处理程序,而不是全局设置,可以使用 logger->set_error_handler(...)
来为特定的日志记录器设置错误处理程序。
错误处理的应用场景:自定义错误处理程序对于处理和记录日志错误信息非常有用,尤其是在需要监控日志系统健康状况或在日志系统出现问题时采取纠正措施的场景中。
16 syslog 系统日志
在 spdlog
中,您可以使用 syslog_sink
将日志消息发送到系统日志(syslog)。syslog
是 Unix 系统中一个重要的日志记录工具,用于记录系统事件和应用程序日志。通过将日志消息发送到 syslog
,可以将其集成到系统的日志管理基础设施中,方便统一管理和监控。
#include "spdlog/spdlog.h"#include "spdlog/sinks/syslog_sink.h"void syslog_example(){ // 定义 syslog 的标识符 std::string ident = "spdlog-example"; // 创建一个 syslog 日志记录器 auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); // 记录一个警告信息到 syslog syslog_logger->warn("This is a warning that will end up in syslog.");}int main(){ // 调用 syslog 示例函数 syslog_example(); return 0;}
代码解释
包含必要的头文件:
spdlog/spdlog.h
是 spdlog
的主头文件,包含了日志记录功能的核心内容。spdlog/sinks/syslog_sink.h
包含了 syslog_sink
,用于将日志消息发送到 syslog
。 定义 syslog 标识符:
std::string ident = "spdlog-example";
定义了发送到 syslog
的标识符(ident
)。这个标识符将在 syslog
中标识出哪个程序或模块生成了日志消息。 创建 syslog 日志记录器:
spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
创建了一个 syslog
日志记录器。"syslog"
是日志记录器的名称。ident
是标识符。LOG_PID
是 syslog
的选项,用于在日志消息中包含生成日志的进程 ID。 记录日志到 syslog:
syslog_logger->warn("This is a warning that will end up in syslog.");
记录一条警告信息,该信息将被发送到 syslog
,并出现在系统的日志文件中(通常是 /var/log/syslog
或类似的路径)。 输出结果
在运行上述代码后,如果在 Unix 系统上配置正确,您可以在 syslog
文件中(通常是 /var/log/syslog
或 /var/log/messages
)看到类似以下的日志消息:
Aug 11 12:34:56 hostname spdlog-example[12345]: This is a warning that will end up in syslog.
hostname
是系统的主机名。spdlog-example
是我们设置的 ident
标识符。[12345]
是生成此日志的进程 ID。This is a warning that will end up in syslog.
是日志消息的内容。 通过使用 spdlog
的 syslog_sink
,您可以将应用程序的日志消息发送到 Unix 系统的 syslog
,从而利用系统级的日志管理和监控工具。这样可以将应用程序日志与系统日志统一管理,方便运维人员进行监控和故障排查。syslog
是 Unix 系统中广泛使用的日志记录机制,将日志消息集成到 syslog
中能够提高日志管理的集中性和效率。
17. Android example
在 Android 平台上,spdlog
提供了一个 android_sink
,用于将日志消息输出到 Android 的日志系统(logcat
)。这使得开发者可以将 spdlog
与 Android 的日志系统集成,在开发和调试应用程序时更加方便地查看日志输出。
#include "spdlog/sinks/android_sink.h"void android_example(){ std::string tag = "spdlog-android"; auto android_logger = spdlog::android_logger_mt("android", tag); android_logger->critical("Use \"adb shell logcat\" to view this message.");}
#include "spdlog/spdlog.h"#include "spdlog/sinks/android_sink.h"void android_example(){ // 定义日志的标签 std::string tag = "spdlog-android"; // 创建一个 Android 日志记录器 auto android_logger = spdlog::android_logger_mt("android", tag); // 记录一条严重错误级别的日志消息 android_logger->critical("Use \"adb shell logcat\" to view this message.");}int main(){ // 调用 Android 日志示例函数 android_example(); return 0;}
代码解释
包含必要的头文件:
spdlog/spdlog.h
是 spdlog
的主头文件,包含了日志记录功能的核心内容。spdlog/sinks/android_sink.h
包含了 android_sink
,用于将日志消息发送到 Android 的日志系统。 定义日志的标签:
std::string tag = "spdlog-android";
定义了日志消息的标签(tag
)。在 Android 的 logcat
中,可以通过这个标签来过滤日志消息。 创建 Android 日志记录器:
spdlog::android_logger_mt("android", tag);
创建了一个 Android 日志记录器。"android"
是日志记录器的名称。tag
是日志消息的标签。 记录日志到 Android 日志系统:
android_logger->critical("Use \"adb shell logcat\" to view this message.");
记录一条严重错误级别的日志消息。此消息将被发送到 Android 的日志系统,可以通过 adb logcat
查看。 查看日志
在运行上述代码后,使用以下命令查看日志消息:
adb shell logcat -s spdlog-android
这将显示带有 spdlog-android
标签的所有日志消息,输出类似以下内容:
E/spdlog-android(12345): Use "adb shell logcat" to view this message.
E
表示日志级别为 Error
(与 critical
对应)。spdlog-android
是我们设置的日志标签。12345
是 Android 应用进程的 ID。Use "adb shell logcat" to view this message.
是日志消息的内容。 18.Load log levels from the env variable or argv
在 spdlog
中,您可以通过环境变量或命令行参数动态设置日志级别。这种方式非常灵活,尤其是在运行时环境不可控或需要根据不同的部署环境动态调整日志级别时。这种功能对于调试、测试和生产环境中的日志管理非常有用。
#include "spdlog/spdlog.h"#include "spdlog/cfg/env.h" // 支持从环境变量加载日志级别#include "spdlog/cfg/argv.h" // 支持从命令行参数加载日志级别int main(int argc, char *argv[]){ // 从环境变量加载日志级别 spdlog::cfg::load_env_levels(); // 或者,从命令行参数加载日志级别 // 例如: ./example SPDLOG_LEVEL=info,mylogger=trace spdlog::cfg::load_argv_levels(argc, argv); // 创建一个日志记录器并记录消息 auto logger = spdlog::stdout_color_mt("mylogger"); logger->debug("This is a debug message"); logger->info("This is an info message"); logger->warn("This is a warning message"); logger->error("This is an error message"); return 0;}
代码解释
加载日志级别:
从环境变量加载: 使用spdlog::cfg::load_env_levels();
从环境变量 SPDLOG_LEVEL
中加载日志级别。例如,设置环境变量 export SPDLOG_LEVEL=info,mylogger=trace
,可以控制所有日志记录器和名为 mylogger
的日志记录器的日志级别。从命令行参数加载: 使用 spdlog::cfg::load_argv_levels(argc, argv);
从命令行参数中加载日志级别。例如,通过命令 ./example SPDLOG_LEVEL=info,mylogger=trace
运行程序,可以设置日志级别。 创建日志记录器并记录日志:
使用spdlog::stdout_color_mt("mylogger");
创建一个带有彩色控制台输出的日志记录器。调用不同级别的日志记录方法(如 debug
, info
, warn
, error
)来记录日志消息。 使用示例
从环境变量加载日志级别
首先,设置环境变量:
export SPDLOG_LEVEL=info,mylogger=trace
然后运行程序:
./example
在这种情况下:
SPDLOG_LEVEL=info
设置所有日志记录器的默认级别为 info
。mylogger=trace
设置名为 mylogger
的日志记录器的级别为 trace
。 从命令行参数加载日志级别
运行程序时传递日志级别参数:
./example SPDLOG_LEVEL=warn,mylogger=debug
在这种情况下:
SPDLOG_LEVEL=warn
设置所有日志记录器的默认级别为 warn
。mylogger=debug
设置名为 mylogger
的日志记录器的级别为 debug
。 可能的输出
根据设置的日志级别,不同级别的日志消息可能会被显示或忽略。例如,如果日志级别设置为 info
,则 debug
级别的消息不会被输出:
[mylogger] [info] This is an info message
[mylogger] [warning] This is a warning message
[mylogger] [error] This is an error message
总结
通过使用 spdlog::cfg::load_env_levels()
或 spdlog::cfg::load_argv_levels()
,您可以在运行时灵活地控制日志级别。这为日志管理提供了极大的便利,使得在不同环境下调试和运行程序变得更加简单和高效。无论是通过环境变量还是命令行参数,您都可以动态调整日志级别,以适应当前的需求。
19. Log file open/close event handlers 打开和关闭事件注册回调函数。
在 spdlog
中,您可以为日志文件的打开和关闭事件注册回调函数。这些回调函数允许您在日志文件打开之前、打开之后、关闭之前以及关闭之后执行特定的操作。例如,您可以在日志文件打开后添加一些初始信息,或者在日志文件关闭前执行清理工作。
#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"void file_events_example(){ // 创建文件事件处理程序 spdlog::file_event_handlers handlers; // 在日志文件打开之前触发的回调 handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; // 在日志文件打开之后触发的回调 handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); // 在日志文件中添加一行 }; // 在日志文件关闭之前触发的回调 handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); // 在日志文件中添加一行 }; // 在日志文件关闭之后触发的回调 handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; // 创建一个带有事件处理程序的基本文件日志记录器 auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); // 记录一些日志消息 my_logger->info("This is an info message."); my_logger->warn("This is a warning message."); // 显式关闭日志记录器,以触发关闭事件 my_logger->flush(); spdlog::drop("some_logger"); // 删除日志记录器并关闭日志文件}int main(){ // 初始化 spdlog spdlog::set_level(spdlog::level::info); // 设置日志级别 spdlog::set_default_logger(spdlog::stdout_color_mt("console")); // 使用彩色控制台输出 // 调用示例函数 file_events_example(); return 0;}
代码解释
创建文件事件处理程序:
spdlog::file_event_handlers handlers;
定义了一个文件事件处理程序对象,用于处理文件的打开和关闭事件。handlers.before_open
在日志文件打开之前执行,记录文件打开前的操作。handlers.after_open
在日志文件打开之后执行,在文件中添加初始内容(如 "After opening\n"
)。handlers.before_close
在日志文件关闭之前执行,添加内容(如 "Before closing\n"
)。handlers.after_close
在日志文件关闭之后执行,记录文件关闭后的操作。 创建带有事件处理程序的文件日志记录器:
spdlog::basic_logger_st
创建了一个基本的文件日志记录器,并将事件处理程序传递给它。"logs/events-sample.txt"
是日志文件的路径,true
表示以追加模式打开文件。 记录日志消息并关闭日志文件:
通过my_logger->info()
和 my_logger->warn()
记录日志消息。使用 my_logger->flush()
和 spdlog::drop("some_logger")
来显式关闭日志记录器,这会触发 before_close
和 after_close
回调。 输出结果
运行上述代码后,logs/events-sample.txt
文件的内容将类似于以下内容:
After opening[info] This is an info message.[warning] This is a warning message.Before closing
此外,控制台上将输出与文件打开和关闭事件相关的日志:
[info] Before opening logs/events-sample.txt[info] After closing logs/events-sample.txt
总结
通过使用 spdlog::file_event_handlers
,您可以灵活地控制和监控日志文件的打开和关闭事件。这在需要在日志文件生命周期内执行特定操作(如记录文件打开/关闭事件、添加文件头或尾信息等)时非常有用。这种机制增强了日志管理的灵活性和可扩展性,使得 spdlog
能够适应更复杂的日志记录需求。
20. Replace the Default Logger 替换默认的日志记录器
在 spdlog
中,您可以通过替换默认的日志记录器来更改日志记录的行为。默认日志记录器通常用于那些不指定特定日志记录器的日志消息。通过替换默认日志记录器,您可以将日志消息定向到不同的输出目标或文件,并自定义日志记录的格式和级别。
#include "spdlog/spdlog.h"#include "spdlog/sinks/basic_file_sink.h"void replace_default_logger_example(){ // 创建一个新的日志记录器,并将其作为默认日志记录器 auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); // 设置新的日志记录器为默认日志记录器 spdlog::set_default_logger(new_logger); // 使用默认日志记录器记录消息 spdlog::info("new logger log message");}int main(){ // 调用示例函数,替换默认日志记录器 replace_default_logger_example(); return 0;}
代码解释
创建新的日志记录器:
spdlog::basic_logger_mt
创建了一个新的日志记录器,该记录器会将日志消息写入 logs/new-default-log.txt
文件中。"new_default_logger"
是日志记录器的名称。true
参数表示以追加模式打开日志文件(如果文件已存在,则在文件末尾追加新日志)。 设置新的默认日志记录器:
spdlog::set_default_logger(new_logger);
将刚刚创建的 new_logger
设置为默认的日志记录器。这意味着任何未显式指定记录器的日志消息都将使用这个新的记录器。 记录日志消息:
spdlog::info("new logger log message");
这条消息将使用新的默认日志记录器,并被记录到 logs/new-default-log.txt
文件中。
21. Log to Qt with nice colors
在使用 spdlog
与 Qt 框架集成时,可以将日志消息直接输出到 Qt 的 QTextEdit
控件中,并且可以使用颜色来区分不同级别的日志消息。这对于开发调试和实时监控非常有用,因为它能够提供直观的日志信息展示。
#include <QMainWindow>#include <QTextEdit>#include "spdlog/spdlog.h"#include "spdlog/sinks/qt_sinks.h"class MainWindow : public QMainWindow{ Q_OBJECTpublic: MainWindow(QWidget *parent = nullptr) : QMainWindow(parent) { // 设置主窗口的最小尺寸 setMinimumSize(640, 480); // 创建 QTextEdit 控件用于显示日志 auto log_widget = new QTextEdit(this); log_widget->setReadOnly(true); // 设置为只读 // 将 QTextEdit 设置为主窗口的中央控件 setCentralWidget(log_widget); // 设置最大行数限制,超过时删除旧行 int max_lines = 500; // 创建一个彩色日志记录器,输出到 QTextEdit 控件 auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines); // 记录一些日志消息 logger->info("Some info message"); logger->warn("This is a warning message"); logger->error("This is an error message"); }};int main(int argc, char *argv[]){ QApplication app(argc, argv); // 创建并显示主窗口 MainWindow window; window.show(); return app.exec();}
代码解释
创建 QTextEdit
控件:
QTextEdit
控件用于显示日志消息。将其设置为只读模式,以避免用户修改显示的日志内容。setCentralWidget(log_widget);
将 QTextEdit
设置为主窗口的中央控件。 设置日志记录器:
使用spdlog::qt_color_logger_mt
创建一个彩色日志记录器,将日志消息输出到 QTextEdit
控件中。max_lines
参数设置了 QTextEdit
中的最大行数。如果日志行数超过这个限制,旧的日志行将被删除,以保持内容的更新。 记录日志消息:
使用logger->info(...)
, logger->warn(...)
, 和 logger->error(...)
方法记录不同级别的日志消息。这些日志消息将在 QTextEdit
控件中显示,并且颜色会根据日志级别自动调整(例如,info
为绿色,warn
为黄色,error
为红色等)。 运行 Qt 应用程序:
QApplication app(argc, argv);
创建 Qt 应用程序对象。MainWindow window;
创建主窗口对象并显示它。app.exec();
进入 Qt 的事件循环。 运行效果
运行此代码后,将会出现一个包含 QTextEdit
控件的主窗口。这个控件会显示以下内容,并使用不同的颜色区分日志级别:
Some info message // 以绿色显示This is a warning message // 以黄色显示This is an error message // 以红色显示
22. Mapped Diagnostic Context 附加特定的上下文信息(如用户 ID、会话 ID 等)
Mapped Diagnostic Context
(MDC) 是 spdlog
提供的一种机制,用于在日志记录过程中自动附加特定的上下文信息(如用户 ID、会话 ID 等)到日志消息中。MDC 是基于线程局部存储的,即每个线程维护自己的 MDC 数据,因此在多线程环境中可以为每个线程附加不同的上下文信息。不过,由于 MDC 依赖于线程局部存储,因此它在异步模式下不可用。
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread local storage.// Each thread maintains its own MDC, which loggers use to append diagnostic information to log outputs.// Note: it is not supported in asynchronous mode due to its reliance on thread-local storage.#include "spdlog/mdc.h"void mdc_example(){ spdlog::mdc::put("key1", "value1"); spdlog::mdc::put("key2", "value2"); // if not using the default format, use the %& formatter to print mdc data // spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [%&] %v");}
#include "spdlog/spdlog.h"#include "spdlog/mdc.h"void mdc_example(){ // 向 MDC 添加键值对 spdlog::mdc::put("key1", "value1"); spdlog::mdc::put("key2", "value2"); // 设置日志格式,包括 MDC 数据(使用 %& 格式符) spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [%&] %v"); // 创建一个日志记录器 auto logger = spdlog::stdout_color_mt("mdc_logger"); // 记录一些日志消息 logger->info("This is an info message with MDC context."); logger->warn("This is a warning message with MDC context."); // 移除 MDC 中的一个键 spdlog::mdc::remove("key1"); // 记录另一条日志消息 logger->error("This is an error message with modified MDC context."); // 清除 MDC spdlog::mdc::clear();}int main(){ // 调用 MDC 示例函数 mdc_example(); return 0;}
代码解释
使用 MDC 添加上下文信息:
spdlog::mdc::put("key1", "value1");
向当前线程的 MDC 中添加一个键值对 "key1" : "value1"
。spdlog::mdc::put("key2", "value2");
再次添加另一个键值对 "key2" : "value2"
。 设置日志格式:
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [%&] %v");
设置日志消息的格式。%&
是用于输出 MDC 数据的格式符。日志消息中会附加当前线程 MDC 中所有的键值对。 记录日志消息:
使用logger->info()
和 logger->warn()
记录日志消息。由于 MDC 中存在键值对,日志消息中会包含这些信息。 移除和修改 MDC 信息:
spdlog::mdc::remove("key1");
从 MDC 中移除 "key1"
键及其对应的值。记录新的日志消息时,MDC 中只包含 "key2"
键值对。 清除 MDC:
spdlog::mdc::clear();
清除当前线程 MDC 中的所有键值对,恢复 MDC 到初始状态。 运行结果
根据上述代码,输出的日志可能类似于以下内容:
[12:34:56 +0000] [info] [key1=value1 key2=value2] This is an info message with MDC context.[12:34:56 +0000] [warning] [key1=value1 key2=value2] This is a warning message with MDC context.[12:34:56 +0000] [error] [key2=value2] This is an error message with modified MDC context.
在每条日志消息中,[key1=value1 key2=value2]
代表了 MDC 的内容。移除 key1
后,MDC 只包含 key2=value2
。
注意事项
线程局部存储:MDC 使用线程局部存储,因此每个线程拥有自己独立的 MDC 数据。它不能在异步模式下使用,因为异步模式可能涉及不同的线程处理日志消息。上下文信息管理:MDC 提供了一种方便的方式来在多线程应用程序中为每个线程附加和管理上下文信息,特别适合需要详细上下文信息的日志记录场景,如处理多个用户请求的服务器应用程序。总结
Mapped Diagnostic Context
(MDC) 是 spdlog
提供的一种机制,用于在日志记录中附加线程级别的上下文信息。这对于调试和监控复杂的多线程应用程序非常有用,因为它允许在日志中包含丰富的上下文信息,帮助更好地理解和分析日志记录的背景。通过 MDC,您可以轻松地管理和输出每个线程的特定上下文数据。