C++高性能日志库spdlog使用指南

news/2024/7/24 6:32:56 标签: c++

spdlog库简介

  • spdlog 是一个高性能的 C++ 日志库,它设计时充分考虑了速度和易用性,具有以下特点
    • 高效与快速:Spdlog 专注于提供极致的性能,在大量日志记录场景下也能保持较低的延迟和较高的吞吐量。
    • 轻量化设计:Spdlog 是头文件(header-only)库,这意味着用户只需要包含相应的头文件即可开始使用,无需编译链接额外的库文件。
    • 跨平台支持:它支持多种操作系统,包括但不限于 Windows、Linux 和 macOS,并且在这些平台上都能够良好运行。
    • 丰富的日志级别:Spdlog 支持常见的日志级别,如 TRACE、DEBUG、INFO、WARN、ERROR、CRITICAL 等,用户可以根据需要选择不同级别的日志输出。
    • 格式化与定位信息:通过集成 fmt 库,Spdlog 允许用户自定义日志消息的格式,可以轻松地包含时间戳、线程ID、文件名、行号以及函数名等上下文信息。
    • 多目标输出:可以将日志输出到控制台、普通文本文件、循环写入文件(rotating log files)、每日生成新文件(daily logs)、系统日志等目标,同时也支持异步写入以提高性能。
    • 线程安全:对于多线程环境,Spdlog 提供了线程安全的日志接口,确保在并发环境下日志记录的正确性和完整性。
    • 异步模式:提供可选的异步日志记录机制,能够将日志操作放入后台线程执行,从而避免阻塞主线程。
    • 条件日志:根据预定义的条件开关,可以动态启用或禁用特定级别的日志输出。

使用步骤

  • spdlog库的使用也非常简单,只需要下载源代码,然后把根目录下的include目录下的文件拷贝到我们的工程下,在工程中包含相应的头文件即可。

实例演示

控制台打印

  • 首先看下最简单的使用,如何在控制台打印以及如何进行参数化打印。
  • main.cpp
  •   #include <spdlog/spdlog.h>
      #include <string.h>
      #include <iostream>
      
      int main()
      {
      	// 普通打印
      	spdlog::info("Welcome to info spdlog!");
      
      	// 格式化打印
      	// 打印字符串
      	spdlog::info("Hello World {}", "spdlog!");
      	// 打印数字
      	spdlog::error("spdlog errCode : {}", -10020);
      	// 指定打印数字的占位符
      	spdlog::warn("spdlog format char {:08d}", 12);
      	// 格式化打印不同进制的数据
      	spdlog::critical("Support for int:{0:d} hex:{0:x} oct:{0:o} bin:{0:b}", 42);
      	// 打印浮点型数据
      	spdlog::info("float args are {:03.2f}", 1.23456);
      	// 打印多个参数
      	spdlog::info("string args are {0} {1}..", "too", "supported");
      	spdlog::info("number args are {0} {1} {2}..", 10020, 10040, -100);
      
      	system("pause");
      }
    
  • 执行结果
    在这里插入图片描述
  • 这里参数化打印的实现是通过 {} 实现的,跟常见函数的参数化打印有点不一样。

在文件中打印日志

  • 接下来就看下如何在文件中打印日志
  • main.cpp
  •   #include <spdlog/spdlog.h>
      #include <spdlog/sinks/basic_file_sink.h>
      #include <string.h>
      #include <iostream>
      
      int main()
      {
      	try
      	{
      		// 参数1 日志标识符, 参数2 日志文件名
      		std::shared_ptr<spdlog::logger> mylogger = spdlog::basic_logger_mt("spdlog", "spdlog.log");
      		// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]
      		mylogger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t]  %v");
      		mylogger->set_level(spdlog::level::debug);
      		spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区
      
      		mylogger->trace("Welcome to info spdlog!");
      		mylogger->debug("Welcome to info spdlog!");
      		mylogger->info("Welcome to info spdlog!");
      		mylogger->warn("Welcome to info spdlog!");
      		mylogger->error("Welcome to info spdlog!");
      		mylogger->critical("Welcome to info spdlog!");
      
      		// 刷新
      		mylogger->flush_on(spdlog::level::debug);
      	}
      	catch (const spdlog::spdlog_ex& ex)
      	{
      		std::cout << "Log initialization failed: " << ex.what() << std::endl;
      	}
      
      	system("pause");
      }
    
  • 执行结果。执行程序后就会在当前目录下生成一个spdlog.log文件,可以看下打印内容
    在这里插入图片描述
    在这里插入图片描述

在文件中打印日志 - 支持打印文件名和行号

通过spdlog提供的单独接口打印文件名和行号

  • main.cpp
  •   // 要打印文件名,必须定义这个宏
      #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
      
      #include <spdlog/spdlog.h>
      #include <spdlog/sinks/basic_file_sink.h>
      #include <string.h>
      #include <iostream>
      
      int main()
      {
      	try
      	{
      		std::shared_ptr<spdlog::logger> mylogger = spdlog::basic_logger_mt("spdlog1", "spdlog1.log");
      		// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [文件名 函数名:行号] [数据]
      		mylogger->set_pattern("[%n] [%Y-%m-%d %H:%M:%S.%e] [%l] [%t] [%s %!:%#]  %v");
      
      		mylogger->set_level(spdlog::level::trace);
      		spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区
      
      		SPDLOG_LOGGER_TRACE(mylogger, "Welcome to info spdlog. {}", "hello!");
      		SPDLOG_LOGGER_DEBUG(mylogger, "Welcome to info spdlog. {}", "hello!");
      		SPDLOG_LOGGER_INFO(mylogger, "Welcome to info spdlog. {}", "hello!");
      		SPDLOG_LOGGER_ERROR(mylogger, "Welcome to info spdlog. {}", "hello!");
      
      		// 结束程序前刷新日志
      		mylogger->flush();
      	}
      	catch (const spdlog::spdlog_ex& ex)
      	{
      		std::cout << "Log initialization failed: " << ex.what() << std::endl;
      	}
      
      	system("pause");
      }
    
  • 打印结果
    在这里插入图片描述
  • 打印文件名和行号,要使用 SPDLOG_LOGGER_TRACE 这个类型的函数打印。并且必须在头文件前面定义这个宏 SPDLOG_ACTIVE_LEVEL , 否则只打印默认级别。还要在函数中通过这个接口再设置打印级别 set_level , 经过测试,这个接口设置的级别是无效的,但不设置的话,就又会打印默认级别,实际打印的日志级别以 SPDLOG_ACTIVE_LEVEL 这个宏设置的为准。
  • 如果在多个文件中打印时,每个文件开头都必须定义这个宏SPDLOG_ACTIVE_LEVEL
  • 上面提到的这些机制就很奇怪,我不确定是它的机制本身就是这样还是我的使用或者理解有问题。而且我们知道,日志级别一般要设置为可配置,它通过宏定义设置,相当于写死了,后期无法再动态修改日志级别,这在实际使用中肯定是无法满足需求的。
  • 有熟悉这块机制的可以评论区留言,如果我写的有问题我会及时修改。

自己封装打印文件名和行号的接口

  • 我又重新实现了一种打印文件名和行号的方式。
  • main.cpp
  •   #include <spdlog/spdlog.h>
      #include <spdlog/sinks/basic_file_sink.h>
      #include <string.h>
      #include <iostream>
      
      #ifdef WIN32
      #define __FILENAME__ (strrchr(__FILE__, '\\') ? (strrchr(__FILE__, '\\') + 1):__FILE__)
      #else
      #define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1):__FILE__)
      #endif
      
      int main()
      {
      	try
      	{
      		// 基本文件
      		std::shared_ptr<spdlog::logger> my_logger = spdlog::basic_logger_mt("spdlog", "spdlog.txt");
      		// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]
      		my_logger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");
      		my_logger->set_level(spdlog::level::debug);
      		spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区
      
      		my_logger->trace("Welcome to info spdlog!");
      		my_logger->debug("[{0}:{1}] Welcome to info spdlog!", __FILENAME__, __LINE__);
      		my_logger->info("[{0}:{1}] Welcome to info spdlog!", __FILENAME__, __LINE__);
      		my_logger->error("Welcome to info spdlog!");
      
      		my_logger->flush();
      
      		system("pause");
      	}
      	catch (const spdlog::spdlog_ex& ex)
      	{
      		std::cout << "Log initialization failed: " << ex.what() << std::endl;
      	}
      }
    
  • 打印结果
    在这里插入图片描述

多个文件中打印

  • 一般都需要在多个文件中打印,下面都通过两个文件演示下
  • main.cpp
  •   #include <spdlog/spdlog.h>
      #include <spdlog/sinks/basic_file_sink.h>
      #include <string.h>
      #include <iostream>
      #include "myfile.h"
      
      #ifdef WIN32
      #define __FILENAME__ (strrchr(__FILE__, '\\') ? (strrchr(__FILE__, '\\') + 1):__FILE__)
      #else
      #define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1):__FILE__)
      #endif
      
      int main()
      {
      	try
      	{
      		// 基本文件
      		std::shared_ptr<spdlog::logger> my_logger = spdlog::basic_logger_mt("spdlog", "spdlog.txt");
      		// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]
      		my_logger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");
      		my_logger->set_level(spdlog::level::debug);
      		spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区
      
      		my_logger->trace("Welcome to info spdlog!");
      		my_logger->debug("[{0}:{1}] Welcome to info spdlog!", __FILENAME__, __LINE__);
      		my_logger->info("[{0}:{1}] Welcome to info spdlog!", __FILENAME__, __LINE__);
      		my_logger->error("Welcome to info spdlog!");
      
      		myFuncPrint();
      
      		my_logger->flush();
      
      		system("pause");
      	}
      	catch (const spdlog::spdlog_ex& ex)
      	{
      		std::cout << "Log initialization failed: " << ex.what() << std::endl;
      	}
      }
    
  • myfile.cpp
  •   #include "myfile.h"
      #include <spdlog/spdlog.h>
      #include <iostream>
      
      #ifdef WIN32
      #define __FILENAME__ (strrchr(__FILE__, '\\') ? (strrchr(__FILE__, '\\') + 1):__FILE__)
      #else
      #define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1):__FILE__)
      #endif
      
      
      void myFuncPrint() {
      	std::shared_ptr<spdlog::logger> mylogger = spdlog::get("spdlog");
      
      	mylogger->trace("Welcome to myFuncPrint!");
      	mylogger->debug("[{0}:{1}] Welcome to myFuncPrint!", __FILENAME__, __LINE__);
      	mylogger->info("[{0}:{1}] Welcome to myFuncPrint!", __FILENAME__, __LINE__);
      	mylogger->error("Welcome to myFuncPrint!");
      }
    
  • 在main函数中定义了 mylogger 对象后,在其他文件中通过 spdlog::get 获取到这个对象,就可以打印了。
  • 打印结果
    在这里插入图片描述

在线程中打印

  • main.cpp
  •   #include <spdlog/spdlog.h>
      #include <spdlog/sinks/basic_file_sink.h>
      #include <string.h>
      #include <iostream>
      #include <windows.h>
      #include <process.h>
      
      unsigned int __stdcall ThreadFun(PVOID lpParam) {
      	std::shared_ptr<spdlog::logger> mylogger = spdlog::get("spdlog");
      
      	mylogger->trace("Welcome to ThreadFun!");
      	mylogger->debug("Welcome to ThreadFun!");
      	mylogger->info("Welcome to ThreadFun!");
      	mylogger->error("Welcome to ThreadFun!");
      
      	return 0;
      }
      
      
      int main()
      {
      	try
      	{
      		// 基本文件
      		std::shared_ptr<spdlog::logger> my_logger = spdlog::basic_logger_mt("spdlog", "spdlog.txt");
      		// 设置日志格式. 参数含义: [日志标识符] [日期] [日志级别] [线程号] [数据]
      		my_logger->set_pattern("[%n][%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");
      		my_logger->set_level(spdlog::level::debug);
      		spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新日志缓冲区
      
      		my_logger->trace("Welcome to trace spdlog!");
      		my_logger->debug("Welcome to debug spdlog!");
      		my_logger->info("Welcome to info spdlog!");
      		my_logger->error("Welcome to error spdlog!");
      
      		HANDLE handle1 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
      		HANDLE handle2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
      		HANDLE handle3 = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
      
      		//永久等待线程运行结束
      		WaitForSingleObject(handle1, INFINITE);
      		WaitForSingleObject(handle2, INFINITE);
      		WaitForSingleObject(handle3, INFINITE);
      
      		// 刷新缓冲区
      		my_logger->flush();
      
      		system("pause");
      	}
      	catch (const spdlog::spdlog_ex& ex)
      	{
      		std::cout << "Log initialization failed: " << ex.what() << std::endl;
      	}
      }
    
  • 打印结果
    在这里插入图片描述

实现日志回滚

  • 可以通过以下这个函数实现日志回滚,参数3和参数4分别代表单个文件大小和回滚数
  •   std::shared_ptr<spdlog::logger> file_logger = spdlog::rotating_logger_mt("spdlog1", "spdlog.log", 10 * 1024, 5);
    

每日日志

  • 可以通过下面接口实现每日日志的打印,即每天都会生成一个新日志。
  •   std::shared_ptr<spdlog::logger> dailylogger = spdlog::daily_logger_mt("daily", "daily.log");
    

参考

  • C++日志记录库SPDLog简介

http://www.niftyadmin.cn/n/5396782.html

相关文章

Qt中tableView控件的使用

tableView使用注意事项 tableView在使用时&#xff0c;从工具栏拖动到底层页面后&#xff0c;右键进行选择如下图所示&#xff1a; 此处需要注意的是&#xff0c;需要去修改属性&#xff0c;从UI上修改属性如下所示&#xff1a; 也可以通过代码修改属性&#xff1a; //将其设…

【算法历练】动态规划副本—路径问题

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;宙でおやすみ 1:02━━━━━━️&#x1f49f;──────── 2:45 &#x1f504; ◀️ ⏸ ▶️ ☰ &#…

php基础学习之错误处理(其一)

一&#xff0c;错误处理的概念 错误处理指的是系统(或者用户)在执行某些代码的时候&#xff0c;发现有错误&#xff0c;就会通过错误处理的形式告知程序员&#xff0c;俗称报错 二&#xff0c;错误分类 语法错误&#xff1a;书写的代码不符合 PHP 的语法规范&#xff0c;语法错…

如何在aws服务器上部署mysql

在AWS服务器上部署 MySQL 数据库可以通过以下步骤完成&#xff1a; 启动 EC2 实例&#xff1a; 在 AWS 控制台中启动一个 EC2 实例&#xff0c;选择适合你需求的实例类型和配置。 安全组配置&#xff1a; 确保你的 EC2 实例的安全组配置允许来自你的 IP 地址的 MySQL 连接。默…

Java集合基础梳理(集合体系+ArrayList)

目录 Java集合体系 为什么要使用集合类 ? 如何选用集合? 哪些集合类是线程安全的&#xff1f;哪些不安全&#xff1f; 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么&#xff1f; 遍历的同时修改一个List有几种方式 ArrayList 如何进行元素的遍历操作&#x…

正则表达式常见的应用场景

数据格式验证&#xff1a;正则表达式可以用来验证输入数据的格式是否符合要求。例如&#xff0c;可以使用正则表达式验证一个邮箱地址是否符合邮箱的格式规范&#xff0c;或者验证一个手机号码是否符合手机号码的格式规范。这在开发网站、手机应用程序等需要用户输入数据的场景…

SpringBoot自动配置中bean的加载方式

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

计算机网络:IP

引言&#xff1a; IP协议是互联网协议族中的核心协议之一&#xff0c;负责为数据包在网络中传输提供路由寻址。它定义了数据包如何在互联网上从源地址传输到目的地址的规则和流程。IP协议使得各种不同类型的网络设备能够相互通信&#xff0c;实现了全球范围内的信息交换。 目录…