spdlog ╰+攻爆jí腚メ 2023-09-27 08:48 93阅读 0赞 spdlog是一个常见的第三方日志库,因为诸多优点得到青睐。这篇文章就对spdlog进行一个简单的介绍。内容主要围绕以下几个方面: 一、spdlog的基本信息 二、spdlog的基本概念 三、spdlog的使用相关 ### 一、spdlog的基本信息 ### 简单点儿说spdlog就是一个开源日志库,支持跨平台(Windows、Linux、Mac、Android),主要开发语言是C++,其整体语言占比如下图: ![在这里插入图片描述][c55ed657162a474e94a7fd6e18c87da8.png] 目前为止最新版本是1.10.0 GitHub的网址是:[gabime/spdlog: Fast C++ logging library. (github.com)][gabime_spdlog_ Fast C_ logging library. _github.com] ### 二、spdlog的基本概念 ### ps:网上很多文章对于spdlog的介绍都是先表明其特点,然后介绍各自特点的实现原理。本文采用自下而上的方法,希望在第二部分之后读者能对spdlog内部有一个清晰的认知。 ##### 2.1 spdlog的结构 ##### spdlog可以分成三级结构,从上而下是logger registry、logger、sink,其各自功能如下: **logger registry(日志管理器)**:负责管理所有的logger,用户建立的所有logger都会在registry处进行登记然后统一管理 **logger(日志记录器)**:是用户直接操作的对象,通过操作logger进行日志逻辑的生成 **sink(日志记录器槽)**:受logger控制,执行具体的动作(动作包括写入日志文件/输出到控制台) 三级结构的关系如下图所示: ![在这里插入图片描述][a148f44853b64925a0be254246f7e148.png] 简单来说,就是一个logger registry管多个logger,一个logger管多个sink。logger registry中的logger是通过name进行对应的。后面使用的时候可以直接通过名称获取对应的日志对象。 有了这种层级结构,在代码调用的时候,logger的每个操作都会下顺到sink层面,调用sink的对象。比如像一些set\_pattern()和set\_level()。 说到底,日志库的目的就是把日志信息写到指定地方。从上面对于结构的功能描述,sink才是真正操作日志进行写操作的结构,那sink可以把日志信息写到哪里呢?主要有三个去向: 1)控制台输出(stdout)——默认输出方式 2)日志文件 3)数据库或其他外部实体 再看这三种去向,控制台输出是为了用户便于即时查看代码状态。通常情况下,日志的数量是比较大的,特别是一些debug的模式。把日志信息存储起来供以后复盘才是更好的方法。从使用的角度上讲,用文件存储日志比直接存储在数据库中更加常用一点。所以本文主要介绍的就是文件的存储方法了。 当然,存储文件也有多种方式了。比如是全写到一个文件中能还是按照某种方式进行分开存放。spdlog中提供了以下几种存放方式,基本能够覆盖对于日志库的常规需求了。 1)当天日志(spdlog::daliy\_logger):记录当天的所有日志,但在指定时间点会把日志清空 2)循环日志(spdlog::rotating\_logger):日志创建成功后,如果写入的日志大小超过限制就会写入到新日志文件中去。不过同时存在的日志总数是有上限的,达到上限后按指定策略淘汰。需要特别注意的是日志更迭的规则是:当日志A存满时,将日志A名称更改为B,再新建一个日志命名为A,依次类推,直到达到上限数字 3)单个日志(spdlog::basic\_logger):只有一个日志文件,所有日志都会在该文件中累加 除了3种文件日志外,输出终端(控制台)也比较常用啦 4)输出终端(spdlog::stdout\_color):日志打印至终端,不同等级日志颜色不同 明确了spdlog的结构,再看看spdlog提供的几个特性吧: ##### 2.2 特性——多种日志等级 ##### 这个不是spdlog特有的,是很常见的功能,对于日志信息分级可以对日志信息进行筛选,对指定用户显示指定信息了。spdlog以枚举的方式提供了七个等级: enum level_enum { trace = SPDLOG_LEVEL_TRACE 0 debug = SPDLOG_LEVEL_DEBUG 1 info = SPDLOG_LEVEL_INFO 2(默认输出等级) warn = SPDLOG_LEVEL_WARN 3 err = SPDLOG_LEVEL_ERROR 4 critical = SPDLOG_LEVEL_CRITICAL 5 off = SPDLOG_LEVEL_OFF 6 } 等级按照大小进行排序,设定等级A后,小于A的信息将不再输出. ##### 2.3 特性——同步、异步 ##### 这里的同步/异步指日志信息是否直接输出/写入文件,直接写就是同步,稍后写就是异步。spdlog默认的状态就是同步了,同步也没什么好说的。这里介绍下异步的逻辑实现: 异步状态下,日志会先存入队列,然后由线程从队列中取数据,当队列满的时候会有淘汰策略。如果工作线程中抛出了异常,向队列写入下一条日志时异常会再次抛出,可以在写入队列时捕捉工作者线程的异常,淘汰策略一般两种: 1)阻塞新来的的日志,直到队列有剩余空间(默认处理方式) 2)把新的日志丢掉(需要设定:spdlog::set\_async\_mode(队列大小,spdlog::async\_overflow\_policy::discard\_log\_msg)) ##### 2.4 特性——单、多线程处理模式 ##### spdlog中提供了单线程和多线程模式,由使用者在对象创建中自己指定。 st:单线程版本,不用加锁,效率高,但不保证线程安全 mt:多线程版本,保证多线程并发情况线程安全,但效率稍低 ##### 2.5 特性——个性化输出格式(pattern) ##### 一般情况下,希望输出的日志信息中带有各类基本信息。但具体情况要具体分析,spdlog中提供了个性化的输出方式,可以自己指定模式进行输出。基本上就是各类“%参数”的组合。具体内容可以参考:[Custom formatting · gabime/spdlog Wiki (github.com)][Custom formatting _ gabime_spdlog Wiki _github.com] ##### 2.6 特性——刷新方式 ##### 刷新方式指日志何时写入文件中,spdlog提供了两种刷新方式: 1)程序正常退出时写入(默认) 2)程序运行中,在指定位置进行写入(实时刷新日志,便于锁定错误所在位置) 要想使用实时刷新日志,spdlog提供了两种方法: 方法一:logger对象->flush\_on(设定等级),flush\_on是一次性刷新,执行到此时按照设定等级进行日志刷新。 方法二:logger对象->flush\_every(周期时间),flush\_every是设置刷新周期,定时进行刷新。刷新的级别采取默认了。 ##### 2.7 特性——异常处理 ##### 对于日志库来说,当异常发生时,应该要输出异常出现的位置。spdlog会向std::err打印一条语句(终端可显示)。为了防止异常语句刷屏,打印的频率固定在每分钟一条。 ### 三、spdlog的使用相关 ### 上一部分介绍了spdlog一些特性,下面结合具体代码明确下各特性的体现。 使用spdlog一定要明确它的各种头文件,常用的头文件如下所示,使用时选择相关的就可以: ![在这里插入图片描述][6ebcbaafc9df4fe79bf45e5461542ec1.png] 最简单的使用: #include "spdlog/spdlog.h" int main() { //Use the default logger (stdout, multi-threaded, colored) spdlog::info("Hello, {}!", "World"); } 上面这个代码里面info函数中的格式看上去有点儿奇怪,具体的教程在[spdlog输出格式详解\_1 - 哔哩哔哩 (bilibili.com)][spdlog_1 - _ _bilibili.com] 日志等级显示: #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #include "spdlog/spdlog.h" int main() { spdlog::info("{:<30}", "left aligned"); spdlog::warn("Easy padding in numbers like {:08d}", 12); spdlog::error("Some error message with arg: {}", 1); spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); spdlog::set_level(spdlog::level::debug); // Set global log level to debug spdlog::debug("This message should be displayed.."); // change log pattern spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); // Compile time log levels // define SPDLOG_ACTIVE_LEVEL to desired level,if not define, don't show SPDLOG_TRACE("Some trace message with param {}", 42); SPDLOG_DEBUG("Some debug message"); return 0; } 三种文件写入方式: #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/daily_file_sink.h" #include <iostream> void basic_logfile_example() { try { auto 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; } } void rotating_example() { // Create a file rotating logger with 5mb size max and 3 rotated files //auto max_size = 1024*1024 * 5; auto max_size = 256; auto max_files = 3; auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files); for (int i=0; i<10000; i++) { logger->info("{} * {} equals {:>10}",i, i, i*i); } } void daily_example() { // Create a daily logger - a new file is created every day on 2:30am auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); } int main() { basic_logfile_example(); rotating_example(); daily_example(); return 0; } 再从代码使用上看同时操作所有logger 1)apply\_all操作所有logger进行输出: spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info(“hello world!”); }) 2)drop\_all释放所有logger spdlog::drop_all(); 以上说了spdlog这么多,来总结下它的优点吧(其实也就是特性了): 1)丰富的日志输出格式,自定义 2)多种输出文件日志类型,支持控制台 3)支持单多线程、支持同步异步 4)清晰的代码结构,便于读者阅读 5)写日志非常的快,效果很好 留一些学习过程中的参考资料: 参考:[spdlog使用\_蜗牛单行道的博客-CSDN博客\_spdlog][spdlog_-CSDN_spdlog] 参考:[spdlog学习笔记\_haojie\_superstar的博客-CSDN博客\_spdlog][spdlog_haojie_superstar_-CSDN_spdlog] 参考(内部有多个demo测试):[\[C++\]-日志记录库SPDLog简介\[通俗易懂\] - 全栈程序员必看 (javaforall.cn)][C_-_SPDLog_ - _ _javaforall.cn] 参考:[spdlog简介\_JontyZh的博客-CSDN博客\_spdlog 输出格式][spdlog_JontyZh_-CSDN_spdlog] 源码阅读:[spdlog 基本结构分析 - 小胖西瓜 - 博客园 (cnblogs.com)][spdlog _ - _ - _ _cnblogs.com] 多线程使用:c++ 日志输出库 spdlog 简介(4)- 多线程txt输出日志 (shuzhiduo.com) **因作者水平有限,如有错误之处,请在下方评论区指正,谢谢!** [c55ed657162a474e94a7fd6e18c87da8.png]: https://img-blog.csdnimg.cn/c55ed657162a474e94a7fd6e18c87da8.png [gabime_spdlog_ Fast C_ logging library. _github.com]: https://github.com/gabime/spdlog [a148f44853b64925a0be254246f7e148.png]: https://img-blog.csdnimg.cn/a148f44853b64925a0be254246f7e148.png [Custom formatting _ gabime_spdlog Wiki _github.com]: https://github.com/gabime/spdlog/wiki/3.-Custom-formatting [6ebcbaafc9df4fe79bf45e5461542ec1.png]: https://img-blog.csdnimg.cn/6ebcbaafc9df4fe79bf45e5461542ec1.png [spdlog_1 - _ _bilibili.com]: https://www.bilibili.com/read/cv15130912/ [spdlog_-CSDN_spdlog]: https://blog.csdn.net/qq_39568245/article/details/115714105 [spdlog_haojie_superstar_-CSDN_spdlog]: https://blog.csdn.net/haojie_superstar/article/details/89383433 [C_-_SPDLog_ - _ _javaforall.cn]: https://javaforall.cn/152339.html [spdlog_JontyZh_-CSDN_spdlog]: https://blog.csdn.net/mec_zhang/article/details/126039105 [spdlog _ - _ - _ _cnblogs.com]: https://www.cnblogs.com/shuqin/p/12214439.html
相关 spdlog spdlog是一个常见的第三方日志库,因为诸多优点得到青睐。这篇文章就对spdlog进行一个简单的介绍。内容主要围绕以下几个方面: 一、spdlog的基本信息 ╰+攻爆jí腚メ/ 2023年09月27日 08:48/ 0 赞/ 94 阅读
相关 spdlog 日志库学习,简易封装 spdlog wiki:[https://github.com/gabime/spdlog/wiki][https_github.com_gabime_spdlog_wiki] 旧城等待,/ 2023年01月12日 04:23/ 0 赞/ 465 阅读
相关 spdlog 日志库学习,自定义 sink spdlog wiki:[https://github.com/gabime/spdlog/wiki][https_github.com_gabime_spdlog_wiki] 一时失言乱红尘/ 2023年01月08日 04:27/ 0 赞/ 183 阅读
相关 【日志库】spdlog的格式设置 SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info 小鱼儿/ 2022年12月15日 03:55/ 0 赞/ 289 阅读
相关 spdlog Features Very fast (see [benchmarks][] below). Headers only or compiled Feature rich for 偏执的太偏执、/ 2022年12月11日 08:00/ 0 赞/ 18 阅读
相关 快速的 C++ 日志库 spdlog spdlog 网站 : [https://isocpp.org/blog/2014/11/spdlog][https_isocpp.org_blog_2014_11_spdlo ╰半橙微兮°/ 2022年06月18日 00:38/ 0 赞/ 149 阅读
还没有评论,来说两句吧...