背景
之前做开发基本是运用Qt, Qt中供给了qDebug作为输出调试东西,可是其明显功用有限,需求自行封装一套东西类,最近考虑换成干流的日志东西,在参阅了网上很多开源库后,发现spdlog和glog似乎还挺不错的。
如果是关于其他言语来说,我更愿意自行封装一套东西类,在其中供给接口,接口的实践调用运用开源库来完成。这样的优点是:
- 统一log风格
- 在日后换用其他开源库的时候只需求替换东西类中接口,不会造成整个项目的改动
可是关于C
这门言语来说,我觉得没有必要:C
项目一旦选型,很难有机会再换用其他技能框架。每个库之间的风格差异和功用都很大, 自行封装一层接口的本钱也挺高的。
spdlog
spdlog作为C 干流开源日志库,有如下特性:
- 性能好
- 只包括头文件
- 无需依赖第三方库
- 支撑跨渠道 Linux / Windows / Android / MacOS
- 支撑多线程
- 可对日志文件进行循环输出
- 可每日生成日志文件
- 支撑控制台日志输出
- 可选的异步日志
- 支撑日志输出等级
- 可自定义日志格局
- 兼容性好,支撑C 11
编译
spdlog的示例cmakelist中展现了两种方法,静态库链接和包括头文件路径,详细运用那种看项目需求。
方法一
最简略的方法便是直接将spdlog源码放在项目目录下,cmake装备如下:
# 往工程中增加spdlog库,这个库是一个interface类型的库
add_library(spdlog INTERFACE)
# 包括这个interface库的工程路径
target_include_directories(
spdlog
INTERFACE
"spdlog/include"
)
# 在cmake最终链接上去就能够了
target_link_libraries(xxx spdlog)
这种便是header only方法,编译简略,可是这样有个缺点,每次改动都会造成从头编译,艹
方法二
在Windows和Linux下编译的方法是不同的
- Windows MSVC SisualStudio
- Linux中编译 && 安装
我这儿运用的是Windows环境,编译东西是Cmake VS, 这一套东西应该很成熟了,就不过多介绍了,总归最终生成.lib,便是windows渠道的静态库。
开发用的Clion MSVC,在项目中新增libs文件夹,将生成的.lib文件复制过来即可,cmake装备如下:
include_directories(${PROJECT_SOURCE_DIR}/spdlog/include)
link_directories(${PROJECT_SOURCE_DIR}/libs)
如果想换用MinGW, 能够自行去折腾…. Linux装备应该也是一样的,仅仅换成.a文件
简略示例
#include <iostream>
#include "spdlog/spdlog.h"
int main() {
std::cout << "hello world" << std::endl;
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");
return 0;
}
输出:
hello world
[2023-12-07 16:58:41.121] [info] Welcome to spdlog!
[2023-12-07 16:58:41.122] [error] Some error message with arg: 1
[2023-12-07 16:58:41.122] [warning] Easy padding in numbers like 00000012
[2023-12-07 16:58:41.122] [critical] Support for int: 42; hex: 2a; oct: 52; bin: 101010
[2023-12-07 16:58:41.122] [info] Support for floats 1.23
[2023-12-07 16:58:41.122] [info] Positional args are supported too..
[2023-12-07 16:58:41.122] [info] left aligned
从上述的输出信息我们能够和std::cout规范库不一样的地方:
- 时刻tag信息
- 日志等级:warn / debug / info 等等
- 不同数据类型和格局的输出方法
Sink Logger
spdlog 是由Sink Logger两部分组成,是整个组件的中心部分。
Logger
- 一个logger中存储了多个sink,也便是logger和sink是一对多的联系,也能够是多对多的联系
- 当调用logger的日志输出函数时,logger会调用本身存储的一切sink目标的log(log_msg) 函数进行输出。
Sink
- 能够理解为一个日志文件,一个日志输出窗口,sink是实践写入日志的目标,每个 sink 应该只负责一个日志输出装备(例如文件、控制台、数据库),并且每个 Sinks 都有自己的 formatter 目标的私有实例。
- sink 有 _mt 多线程 和 _st 单线程之分;单线程不能在多线程中运用。
理解了sink和logger才干更好的运用spdlog
日志等级
enum level_enum
{
trace = SPDLOG_LEVEL_TRACE, // 描述事情的日志等级,显示代码的逐步执行,在规范操作期间能够疏忽。『最初级』
debug = SPDLOG_LEVEL_DEBUG, // 当需求更详细的信息时,运用此调试信息
info = SPDLOG_LEVEL_INFO, // 突出强调应用程序的运转过程中,输出一些提示信息
warn = SPDLOG_LEVEL_WARN, // 应用程序内部发生了意外行为,但它仍在持续工作,要害事务功用按预期运转。
err = SPDLOG_LEVEL_ERROR, // 一项或多项功用无法正常工作,导致某些功用无法正常工作。
critical = SPDLOG_LEVEL_CRITICAL, // 一项或多项要害事务功用不起作用,整个系统无法完成事务功用。
off = SPDLOG_LEVEL_OFF, // 用于关闭一切日志记载。『第一流』
n_levels
};
日志等级 Log Level 相关,一般只用到 debug、info、err 这三个等级比较多。
logger默许创立是Info等级,也便是info之上的等级才干输出,我们经过set_level函数设置其输出等级:
void level_test()
{
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/level-log.txt");
logger->trace("spdlog level trace output");
logger->debug("spdlog level debug output");
logger->info("spdlog level info output");
logger->warn("spdlog level warn output");
logger->error("spdlog level error output");
//在设置warn等级之后,warn以上的等级都不再输出。
logger->set_level(spdlog::level::warn);
logger->trace("spdlog level trace output");
logger->debug("spdlog level debug output -2");
logger->info("spdlog level info output -2 ");
logger->warn("spdlog level warn output -2");
logger->error("spdlog level error output -2");
}
输出为:
[2023-12-07 18:47:13.822] [basic_logger] [info] spdlog level info output
[2023-12-07 18:47:13.824] [basic_logger] [warning] spdlog level warn output
[2023-12-07 18:47:13.824] [basic_logger] [error] spdlog level error output
[2023-12-07 18:47:13.824] [basic_logger] [warning] spdlog level warn output -2
[2023-12-07 18:47:13.824] [basic_logger] [error] spdlog level error output -2
输出格局pattern
经过set_pattern可设定日志格局,如set_pattern(“[%Y-%m-%d %H:%M:%S.%e]%l: %v”);
比如默许的便是[2023-12-07 18:47:13.824]
带有这样的时刻信息
void log_pattern_test()
{
spdlog::info("Welcome to spdlog!");
spdlog::set_pattern("[%c] [pid %P][thread id: %t] [%l] %v");
spdlog::info("Welcome to spdlog!");
}
输出:
[2023-12-07 18:55:51.512] [info] Welcome to spdlog!
[Thu Dec 7 18:55:51 2023] [pid 32564][thread id: 24252] [info] Welcome to spdlog!
能够看到前面的tag发生了一些改变
详细的输出格局能够参阅:blog.csdn.net/alwaysrun/a…
写入文件
/**
* 写入文件的logger
*/
void basic_logfile_example()
{
std::shared_ptr<spdlog::logger> logger;
try
{
logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
}
//运用logger输出的信息会被写入到文件中
for (int i = 0; i < 100; i)
{
logger->info("write info: {} to logger file",i);
}
}
上述创立了一个std::shared_ptr<spdlog::logger>
目标,运用此目标输出的log信息都会被写入到设定的文件中。
除此之外还有daily files,便是依据事情每天都会写入一个新的文件
#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);
}
循环文件:设置文件巨细,超出之后会掩盖
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5mb size max and 3 rotated files
auto max_size = 1024*1024 * 5;
auto max_files = 3;
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
}
Backtrace support
回溯支撑?我没太理解,依据其给出的示例,便是将log信息缓存起来了并不是马上显示,而是在需求的时候才显示出来。
比如:
void backtrace_test()
{
//调试消息能够存储在环缓冲区中,而不是立即记载。
//这仅在需求时显示调试日志很有用(例如发生错误时)。
//在需求时,调用dump_backtrace将它们倒入日志中。
spdlog::enable_backtrace(10); // Store the latest 32 messages in a buffer.
// or my_logger->enable_backtrace(10)..
for(int i = 0; i < 100; i )
{
spdlog::debug("Backtrace message {}", i);
}
spdlog::dump_backtrace(); // log them now! show the last 32 messages
// or my_logger->dump_backtrace(32)..
}
输出成果:
[2023-12-07 18:24:40.392] [info] ****************** Backtrace Start ******************
[2023-12-07 18:24:40.392] [debug] Backtrace message 90
[2023-12-07 18:24:40.392] [debug] Backtrace message 91
[2023-12-07 18:24:40.392] [debug] Backtrace message 92
[2023-12-07 18:24:40.392] [debug] Backtrace message 93
[2023-12-07 18:24:40.392] [debug] Backtrace message 94
[2023-12-07 18:24:40.392] [debug] Backtrace message 95
[2023-12-07 18:24:40.392] [debug] Backtrace message 96
[2023-12-07 18:24:40.392] [debug] Backtrace message 97
[2023-12-07 18:24:40.392] [debug] Backtrace message 98
[2023-12-07 18:24:40.392] [debug] Backtrace message 99
[2023-12-07 18:24:40.397] [info] ****************** Backtrace End **************
创立自定义logger
void get_logger()
{
//经过spdlog::get()获取现已创立的logger
auto test_logger = spdlog::get("spd_logger_info");
test_logger->info("getlogger::helloworld");
}
void create_logger()
{
// auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("logs/mylog.txt", 1024*1024*10, 3);
auto my_logger = std::make_shared<spdlog::logger>("spd_logger_info", sink);
my_logger->set_level(spdlog::level::debug);
my_logger->warn("this should appear in both console and file");
my_logger->info("this message should not appear in the console, only in the file");
//需求注册了之后,其他地方才干获取
spdlog::register_logger(my_logger);
my_logger->info("just create a logger");
}
一个logger相关多个sink
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");
}
一个sink相关多个sink
void multi_logger_test()
{
auto sharedFileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multi_logger.txt");
auto firstLogger = std::make_shared<spdlog::logger>("loggerOne", sharedFileSink);
auto secondLogger = std::make_shared<spdlog::logger>("loggerTwo", sharedFileSink);
for(int i = 0; i < 10; i )
{
firstLogger->info("[loggerOne]: Hello {}.", i);
}
for(int j = 0; j < 10; j )
{
secondLogger->info("[loggerTwo]: Hello {}. ", j);
}
}
最终
更多示例参阅官网example
以上代码请参阅:LogDemo