背景
spdlog 是一个快速、异步的、header-only 的 c 日志库。它提供了简单易用的 api 并具有高性能和可扩展性。
下载和使用
下载
spdlog 库下载地址:github 链接
hello world
在使用时只需要 include 整个 /include/spdlog 文件夹即可。
#include "spdlog/spdlog.h"
int main() {
spdlog::info("hello world");
return 0;
}
运行结果如下:
如上图所示,spdlog 库上手非常简单。
基本概念
核心组件
spdlog 有以下基本组成部分:
- registry(日志记录器注册表):registry 用于管理所有已创建的 logger 对象。
- logger(日志记录器):logger 是打印日志的核心对象,负责记录日志消息。可以根据需要创建多个logger 对象。
- sink(日志输出):sink 是 logger 的目标输出位置,它指定了日志消息的最终存储位置。每个 logger 内包含一个 sink 组成的 vector。
- formatter(日志格式化):formatter 用于格式化日志消息的输出格式。
通过以上组件,可以灵活地配置和使用 spdlog,以满足不同的日志需求。例如,可以创建多个 logger 对象,并将它们的日志消息输出到不同的文件中;也可以自定义日志消息的格式,添加时间戳和其他元数据。
日志格式
spdlog 自带了默认的 formatter,其格式为:[日期时间] [logger名] [log级别] log内容。
日志级别
spdlog 提供了一组日志级别,用于控制记录哪些级别的日志消息:
- trace:最详细的日志级别,提供追踪程序执行流程的信息。
- debug:调试级别的日志信息,用于调试程序逻辑和查找问题。
- info:通知级别的日志信息,提供程序运行时的一般信息。
- warn:警告级别的日志信息,表明可能发生错误或不符合预期的情况。
- error:错误级别的日志信息,表明发生了某些错误或异常情况。
- critical:严重错误级别的日志信息,表示一个致命的或不可恢复的错误。
通过设置日志记录器的级别,可以控制哪些级别的日志进行输出:
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
auto logger = spdlog::stdout_color_mt("console");
logger->set_level(spdlog::level::warn);
logger->trace("trace message");
logger->debug("debug message");
logger->info("info message");
logger->warn("warn message");
logger->error("error message");
logger->critical("critical message");
return 0;
}
运行结果如下:
日志参数
spdlog 绑定了 fmt 库,可以用于格式化输出日志内容:
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
auto logger = spdlog::stdout_color_mt("console");
logger->info("log message {},{}","hello",123);
return 0;
}
运行结果如下:
spdlog 快速上手
概述
spdlog 提供了一系列工厂函数用于创建 logger。其中以 _mt 后缀的表示创建多线程的日志记录器、以 -st 后缀的表示创建单线程的日志记录器。
创建控制台 logger
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
auto logger = spdlog::stdout_color_mt("console");
logger->info("hello world");
return 0;
}
运行结果如下:
创建基本文件 logger
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() {
auto logger = spdlog::basic_logger_mt("file","my_log.log");
logger->info("hello world");
return 0;
}
运行结果如下:
创建滚动文件 logger
可以设置日志文件的大小及数量限定:
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
int main() {
auto max_size = 1024*2;// 每个文件最大 2 k
auto max_files = 3;//最多滚动 3 次
auto logger = spdlog::rotating_logger_mt("file","my_log.log", max_size, max_files);
for( int i = 0 ;i < 100; i)
{
logger->info("rotating file log test");
}
return 0;
}
运行结果如下:
创建每日 logger
可以在每天固定时间点创建一个新的日志文件:
#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
int main() {
auto logger = spdlog::daily_logger_mt("daily_logger", "my_log.log", 0, 0);
return 0;
}
创建异步日志
可以与 spdlog::async_factory 搭配使用,以实现异步日志。异步记录可以提高程序的性能,因为日志写入操作不会阻塞主线程。
#include "spdlog/async.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
auto logger = spdlog::stdout_color_mt("async_logger");
logger->info("123");
return 0;
}
运行结果如下:
创建 logger
spdlog 提供的工厂方法封装了 sink 的创建过程,也可以根据需要先创建 sink ,再创建 logger。
创建 sink
sink 是将日志实际写入其目标(文件、控制台、数据库等)的对象,且应仅负责单个目标。
创建控制台 sink
auto sink = std::make_shared();
创建文件 sink
auto sink = std::make_shared("my_log.log");
创建每日文件 sink
auto sink = std::make_shared("my_log.log",23,59);
创建滚动文件 sink
auto sink = std::make_shared("my_log.log",1048576 * 5, 3,false);
创建流输出 sink
std::ostringstream oss;
auto ostream_sink = std::make_shared (oss);
根据 sink 创建 logger
创建单 sink 记录器
可以直接在构造函数中传入 sink 来创建 logger:
int main() {
auto sink = std::make_shared();
auto logger = std::make_shared("my_logger", sink);
logger->info("hello world ");
return 0;
}
创建多 sink 记录器
也可以传入一个 sink 列表来创建 logger,每个 sink 可单独设置日志级别和样式:
int main() {
auto sink1 = std::make_shared();
auto sink2 = std::make_shared("my_log.log");
spdlog::sinks_init_list sinks = {
sink1,sink2};
auto logger = std::make_shared("my_logger", sinks.begin(),sinks.end());
logger->info("hello world ");
return 0;
}
创建共用 sink 记录器
多个 logger 也可以共用相同的 sink :
int main() {
auto sink = std::make_shared();
auto logger1 = std::make_shared("logger1", sink);
auto logger2 = std::make_shared("logger2", sink);
auto logger3 = std::make_shared("logger3", sink);
logger1->info("hello world ");
logger2->info("hello world ");
logger3->info("hello world ");
return 0;
}
logger 注册与获取
spdlog 提供了一个全局注册和获取 logger 的方法。
logger 注册
使用 spdlog 工厂方法创建的 logger 无需手动注册即可根据名称获取,手动创建的 logger 需要注册。
#include "spdlog/sinks/stdout_color_sinks.h"
#include
void register_logger()
{
auto sink = std::make_shared();
auto logger = std::make_shared("my_logger", sink);
spdlog::register_logger(logger);
}
int main() {
register_logger();
auto logger = spdlog::get("my_logger");
logger->info("hello world");
return 0;
}
logger 删除
手动注册的全局 logger 也可以删除:
spdlog::drop("my_logger");//全局注册中删除指定 logger
spdlog::drop_all();// 删除所有注册的 logger
logger 的使用与设置
设置默认 logger
spdlog 提供了最为便捷的默认 logger,注意,该logger在全局公用:
spdlog::info("hello world");
可以设置自定义的 logger 为全局默认:
auto logger = spdlog::stdout_color_mt("my_log");
spdlog::set_default_logger(logger);
设置日志级别
logger 和 sink 都可以单独指定日志级别。
设置指定 logger 级别
auto logger = spdlog::stdout_color_mt("my_log");
logger->set_level(spdlog::level::debug);
设置指定 sink 级别
auto sink = std::make_shared();
sink->set_level(spdlog::level::debug);
设置默认 logger 级别
spdlog::set_level(spdlog::level::debug);
设置缓存刷新策略
创建好 logger 后建议设置 flush 方式,否则可能无法立刻在文件中看到 logger 的内容:
定时刷新
spdlog::flush_every(std::chrono::seconds(5));// 定期为所有注册的logger隔5秒刷新
基于级别刷新
auto logger = spdlog::stdout_color_mt("my_log");
logger->flush_on(spdlog::level::warn);//遇到 warn 就立即刷新
手动刷新
auto logger = spdlog::stdout_color_mt("my_log");
logger->flush()// logger 将依次在每个 sink 上调用 flush
spdlog 使用进阶
记录自定义类型
用户自定义类型对象作为日志参数进行记录,需要重置输出运算符:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ostr.h"
class demo{
public:
demo(int id_,const std::string &name_):id(id_),name(name_){
}
private:
int id ;
std::string name;
public:
friend std::ostream& operator<<(std::ostream& os, const demo& d);
};
std::ostream &operator<<(std::ostream &os, const demo &d) {
os << "id:"<info("log message {}",d);
return 0;
}
运行结果如下:
记录 vector 中数据
vector 对象可以直接作为参数进行输出:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ranges.h"
int main() {
auto logger = spdlog::stdout_color_mt("console");
std::vector vec{
1,2,3,4,5};
logger->info("vector data :{}",vec);
return 0;
}
运行结果如下:
记录运行时间
使用 spdlog::stopwatch 对象可以记录代码运行时间:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/stopwatch.h"
#include
void test()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
int main() {
auto logger = spdlog::stdout_color_mt("console");
spdlog::stopwatch sw;
test();
logger->info("test run {} seconds",sw);
return 0;
}
运行结果如下:
记录十六进制数据
使用 to_hex 可以吧二进制数据转十六进制进行记录:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/bin_to_hex.h"
int main() {
auto logger = spdlog::stdout_color_mt("console");
unsigned char data[] = {
0xab, 0xcd, 0xef, 0x12, 0x34, 0x56};
logger->info("hex string: {} ", spdlog::to_hex(std::begin(data), std::begin(data) sizeof(data)));
return 0;
}
运行结果如下:
还可以显示对应 ascii 值:
int main() {
auto logger = spdlog::stdout_color_mt("console");
unsigned char data[] = {
0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
logger->info("hex string: {:a} ", spdlog::to_hex(std::begin(data), std::begin(data) sizeof(data),4));
return 0;
}
运行结果如下:
记录文件名及行号
在使用 spdlog 记录日志时,可以通过格式化字符串来包含方法名、行号和文件名的信息:
#define spdlog_active_level spdlog_level_trace
#include
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
spdlog::set_pattern("[%h:%m:%s] [%n] [%^---%l---%$] [%s:%#] [%!] %v");
auto logger = spdlog::stdout_color_mt("my_log");
spdlog_logger_info(logger, "this is a log message");
return 0;
}
运行结果如下:
一定确保 spdlog_active_level 定义的日志级别低于或等于你期望的日志级别,并且在包含 spdlog.h 之前定义了它。
其他特殊 logger
qt sink
qt_sink 可以向 qtextbrowser、qtextedit 等控件输出日志消息:
#include "spdlog/sinks/qt_sinks.h"
auto logger = spdlog::qt_logger_mt("qlogger",ui->textbrowser);
logger->info("hello qtextbrowser");
logger->warn("this msg from spdlog");
运行结果如下:
msvc sink
msvc_sink 使用 outputdebugstringa 向 windows调试接收器发生日志记录:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/msvc_sink.h"
int main() {
auto sink = std::make_shared();
auto logger = std::make_shared("msvc_logger", sink);
logger->info("debug log test...");
return 0;
}
运行结果如下:
消息过滤 sink
dup_filter_sink 可以实现重复消息删除:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/dup_filter_sink.h"
int main() {
auto sink = std::make_shared();
auto dup_filter_sink= std::make_shared(std::chrono::seconds(5));
dup_filter_sink->add_sink(sink);
auto logger = std::make_shared("msvc_logger", dup_filter_sink);
logger->info("hello world");
logger->info("hello world");
logger->info("hello world");
logger->info("hello world");
logger->info("log msg");
return 0;
}
运行结果如下:
ringbuffer sink
ringbuffer_sink 将最新的日志消息保存在内存中:
#include
#include "spdlog/sinks/ringbuffer_sink.h"
#include "spdlog/fmt/ranges.h"
int main() {
auto ringbuffer_sink = std::make_shared(5);
auto logger = std::make_shared("ringbuffer_logger", ringbuffer_sink);
for (int i = 0; i < 20; i) {
logger->info("log message {}", i);
}
std::vector log_messages = ringbuffer_sink->last_formatted(1);
spdlog::info("{}",log_messages);
return 0;
}
运行结果如下:
udp sink
spdlog 提供的一个封装了 udp 传输的 logger。它可以将日志记录通过 udp 协议发送到指定的目标地址和端口:
#include
#include "spdlog/sinks/udp_sink.h"
int main() {
spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091);
auto my_logger = spdlog::udp_logger_mt("udplog", cfg);
my_logger->info("hello world");
return 0;
}
支持回调的 logger
callback_logger_mt 是一个支持设置调用回调函数的日志记录器:
#include
#include
#include "spdlog/sinks/callback_sink.h"
int main() {
auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg & msg) {
std::cout << msg.payload.data() << std::endl;
});
logger->info("123");
return 0;
}
运行结果如下: