简述
spdlog是一个常见的第三方日志库,速度非常快,支持仅标头/编译两种使用方式,基于C++的日志记录库,支持跨平台(Windows、Linux、Mac、Android)。
特点
- 非常快速(请参见下面的基准测试)。
- 仅头文件或编译。
- 使用优秀的fmt库进行功能丰富的格式化。
- 异步模式(可选)。
- 自定义格式化。
- 多线程/单线程记录器。
- 各种日志目标:
轮换日志文件。
每日日志文件。
控制台日志记录(支持颜色)。
syslog。
Windows事件日志。
Windows调试器()。OutputDebugString(..)
可轻松扩展自定义日志目标。
- 日志过滤-日志级别可以在运行时和编译时进行修改。
- 支持从argv或环境变量加载日志级别。
- 回溯支持-将调试消息存储在环形缓冲区中,并在需要时显示它们。
安装
仅标头版本
将包含文件夹复制到构建树并使用 C++11 编译器。
编译版本(推荐 - 编译时间更快)
$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && make -j
请参阅示例 CMakeLists.txt了解如何使用。
快速入门
#include "spdlog/spdlog.h"
int main() {
//Use the default logger (stdout, multi-threaded, colored)
spdlog::info("Hello, {}!", "World");
}
SPDLOG 是一个仅标头库。只需将 include 下的文件复制到构建树并使用 C++11 编译器即可。
它使用捆绑的 fmt 库提供了一个类似 python 的格式化 API(请参阅参考):
logger->info("Hello {} {} !!", "param1", 123.4);
SPDLOG 采用“包含您需要的内容”方法 - 您的代码应包含实际需要的功能。
例如,如果只需要旋转记录器,则需要包含“spdlog/sinks/rotating_file_sink.h”。
另一个例子是包含“spdlog/async.h”来获取异步日志记录功能。
基本示例
#include <iostream>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h" // support for basic file logging
#include "spdlog/sinks/rotating_file_sink.h" // support for rotating file logging
int main(int, char* [])
{
try
{
// Create basic file logger (not rotated)
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
// create a file rotating logger with 5mb size max and 3 rotated files
auto file_logger = spdlog::rotating_logger_mt("file_logger", "myfilename", 1024 * 1024 * 5, 3);
}
catch (const spdlog::spdlog_ex& ex)
{
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
如何在 DLL 中使用 spdlog
问题
因为 spdlog 是仅标头的,所以构建共享库并在主程序中使用它不会在它们之间共享注册表。 这意味着对 like 函数的调用不会更改 DLL 中的记录器。spdlog::set_level(spdlog::level::level_enum::info)
解决方法
可以做的是在两个注册表中注册logger。
/*
* Disclaimer:
* This was not compiled but extracted from documentation and some code.
*/
// mylibrary.h
// In library, we skip the symbol exporting part
#include <memory>
#include <vector>
#include <spdlog/spdlog.h>
#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_color_sinks.h>
namespace library
{
static const std::string logger_name = "example";
std::shared_ptr<spdlog::logger> setup_logger(std::vector<spdlog::sink_ptr> sinks)
{
auto logger = spdlog::get(logger_name);
if(not logger)
{
if(sinks.size() > 0)
{
logger = std::make_shared<spdlog::logger>(logger_name,
std::begin(sinks),
std::end(sinks));
spdlog::register_logger(logger);
}
else
{
logger = spdlog::stdout_color_mt(logger_name);
}
}
return logger;
}
void test(std::string message)
{
auto logger = spdlog::get(logger_name);
if(logger)
{
logger->debug("{}::{}", __FUNCTION__, message);
}
}
}
// In the main program
#include <mylibrary.h>
#include <spdlog/logger.h>
#include <spdlog/sinks/daily_file_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
int main()
{
// We assume that we load the library here
...
// Let's use the library
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_st>());
sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_st>("logfile", 23, 59));
auto logger = library::setup_logger(sinks);
spdlog::set_level(spdlog::level::level_enum::debug); // No effect for the library.
library::test("Hello World!"); // No logging
spdlog::register_logger(logger);
// Now this will also affect the library logger
spdlog::set_level(spdlog::level::level_enum::debug);
library::test("Hello World!"); // Hurray !
return 0;
}
接下来会尝试qt重定向日志和spdlog对接,关键词qInstallMessageHandler