C++spdlog学习总结

2023-10-29

spdlog简介

(1)按照官方介绍,是一个高性能的C++日志组件,支持跨平台,兼容 C++11;一款开源的、快速的日志库;
(2)spdlog是个只有头文件的库,只需要将头文件拷贝到你的工程就可以使用了,编译器需要支持C++11
它使用一个类似python的格式API库fmt:

spdlog优点

  1. 配置特别简单,仅包含头文件即可(我暂时没测试成功 在我测试时需要引入静态库)
  2. 写日志方式简单明了;
  3. 可实现自动按日期创建日志文件/定时创建日志文件;
  4. 可自定义日志格式;
  5. 可以输出当前输出日志所在的文件及函数;
  6. 可自定义文档大小;
  7. 可将不同级别的信息输出到不同日志文件;
  8. 多平台等。

一般日志功能设计

一般在实际工作中,日志最好要有以下3个功能:
①:自动按日期创建日志文件;
②:自动定期清理过期日志(spdlog好像没有这个功能…);
③:实时刷新日志。
具体分析:
①:一天只创建一个日志文件(或者将各级别的日志单独创建为一个文件)是为了更加清晰简洁,若程序在每次开启时都创建一个日志文件,可能就会显得很混乱。
②:一般时间较久的日志不再具有参考价值,如果真是现场出现问题,用户也不会等很久才反馈。所以一般保存7天内的日志就足够了。
③:当我们需要查看日志时,但又不能关闭现场程序,那么就需要能实时刷新日志信息。不然我们可能会遇到日志文件打不开或者打开以后日志信息未更新的问题。

spdlog安装

[1] 安装配置测试链接
[2] Vcpkg全自动方式
[3] C++参考手册
[4] 总体概括

spdlog琐碎知识点总结

(1)线程安全:spdlog:: 命名空间下的是线程安全的;类似于:spdlog::set_error_handler(log_err_handler); // or logger->set_error_handler(log_err_handler);
(2)对于sinks,以 _mt 后缀结尾的是线程安全的,比如:daily_file_sink_mt,以_st 后缀结尾的是非线程安全的,比如:daily_file_sink_st
(3)有关异步设置logger – 多线程 – (4)中相关细节也可查看
(4)使用spdlog::get("...")访问loggers缺点:loggers可以在任何地方使用线程安全的spdlog::get("logger_name")来进行访问,返回智能指针;注意:spdlog::get可能会拖慢你的程序,因为它内部维护了一把锁,所以要谨慎使用。比较推荐的用法是保存返回的shared_ptr<spdlog::logger>,直接使用它,至少在频繁访问的代码中。详情参考(3)链接

spdlog程序测试

(一)日志输出控制台

链接中包含cmake文件配置

(1)数据全部输出到控制台

spdlog灵魂所在:自动识别类型,避免%d,%s类型错误,输出不了内容或者崩溃

LogDebug(“cmd_id={},bodyLen={}”, 1, 2);
LogInfo(“user_id={},app_id={},domainId={},ip={},port={}”, 222, 222, 222, “127.0.0.1”, 8888);

#include <iostream>
#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main01(int argc, char* argv[]) 
{
	/*创建一个控制台对象*/
	auto console = spdlog::stdout_color_mt("ybhy");
	/*在控制台输出以下数据 -- 不同的等级有不同的颜色对应 :info、error、warn、critical*/
	console->info("信息");
	console->warn("警告");
	console->error("错误");
	console->critical("危险");
	/*上面的另一种写法*/
	spdlog::info("信息");
	spdlog::warn("警告");
	spdlog::error("错误");
	spdlog::critical("危险");

	/*根据对象输出到控制台 -- 类似于指定用户那种*/
	console->info("Welcome to spdlog!");
	console->info("Support for floats {:03.2f}", 1.23456);
	console->info("Positional args are {1} {0}..", "too", "supported");
	console->info("{:<30}", "left aligned");
	console->warn("Easy padding in numbers like {:08d}", 12);
	console->error("Some error message with arg{}..", 1);
	console->critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
	/*通过 get() 方法获取 相关对象指针 -- 输入相关数据*/
	spdlog::get("ybhy")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
	/*显示格式*/
	spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v");
	/*关闭所有logger对象*/
	spdlog::drop_all();
	return 0}

运行结果如下
运行结果

(2)指定某个等级以上的数据到控制台

spdlog::set_level(spdlog::level::warn);

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
};

#include <iostream>
#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main01(int argc, char* argv[]) 
{
	/*创建一个控制台对象*/
	auto console = spdlog::stdout_color_mt("ybhy");
	/*设计运行等级 -- 等级以下 不显示 相当于 warn以下的信息不打印*/
	spdlog::set_level(spdlog::level::warn); 
	/*在控制台输出以下数据 -- 不同的等级有不同的颜色对应 :info、error、warn、critical*/
	console->info("信息");
	console->warn("警告");
	console->error("错误");
	console->critical("危险");
	/*上面的另一种写法*/
	spdlog::info("信息");
	spdlog::warn("警告");
	spdlog::error("错误");
	spdlog::critical("危险");
	/*根据对象输出到控制台 -- 类似于指定用户那种*/
	console->info("Welcome to spdlog!");
	console->info("Support for floats {:03.2f}", 1.23456);
	console->info("Positional args are {1} {0}..", "too", "supported");
	console->info("{:<30}", "left aligned");
	console->warn("Easy padding in numbers like {:08d}", 12);
	console->error("Some error message with arg{}..", 1);
	console->critical("Support for int: {0:d};  hex: {0:x};  oct: {0:o}; bin: {0:b}", 42);
	/*通过 get() 方法获取 相关对象指针 -- 输入相关数据*/
	spdlog::get("ybhy")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
	/*显示格式*/
	spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v");
	return 0}

运行结果如下
运行结果

(二)输出格式的自定义方式

熟悉函数:spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v");

  1. 模式标记
flag meaning example
%v The actual text to log(要记录的实际文本) “some user text”
%t Thread id “1232”
%P Process id “3456”
%n Logger’s name “some logger name”
%l The log level of the message “debug”, “info”, etc
%L The log level of the message “D”, “I”, etc
%a Abbreviated weekday name(工作日的缩写) “Thu”
%@ Source file and line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) my_file.cpp:123
%s Source file (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) my_file.cpp
%# Source line (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc.) 123
%! Source function (use SPDLOG_TRACE(…),SPDLOG_INFO(…) etc. see tweakme for pretty-print) my_func
  1. 对齐
    对齐
  2. 相关程序测试

细节把握
(1)spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n],%v"); 可以放在最后(显示的时候会根据不同的level显示不同的颜色),可以放在最前(无颜色)
(2)在使用<%@><%s><%#><%!> 不是直接写上就可以,需要调用相对应的函数宏才可实现;目前调研中掌握了两种方法:①通过调用宏函数 ②自己编写一个函数宏来调用本身程序;
(3)具体实现:①通过调用宏函数:引入#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE,在程序中调用对应宏,比如:SPDLOG_LOGGER_INFO(spdlog::get("ybhy"),"faf"); ②自己编写一个函数宏来调用本身程序:如下代码所示:

#include <iostream>
#include <memory>
#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
/*以下宏函数将信心打印到控制台和文件*/
#define DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_DEBUG(spdlog::get("daily_logger"), __VA_ARGS__)
#define LOG(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_INFO(spdlog::get("daily_logger"), __VA_ARGS__)
#define WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_WARN(spdlog::get("daily_logger"), __VA_ARGS__)
#define ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__);SPDLOG_LOGGER_ERROR(spdlog::get("daily_logger"), __VA_ARGS__)

int main(int argc, char* argv[])
{
	// 每天2:30 am 新建一个日志文件
	auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
	// 遇到warn flush日志,防止丢失
	logger->flush_on(spdlog::level::warn);
	//每三秒刷新一次
	spdlog::flush_every(std::chrono::seconds(10));
	
	//将默认记录器设置为文件记录器
	auto console = spdlog::stdout_color_mt("ybhy");
	spdlog::set_default_logger(console);
	spdlog::set_level(spdlog::level::debug); // Set global log level to debug
	//打印相关信息
	LOG("test info");//将信息打印到控制台和文件
	ERROR("test error");
	WARN("dadasda");
	// %s:文件名
	// %#:行号
	// %!:函数名
	spdlog::set_pattern("[%Y-%m-%d %H:%M:%S] [%l] [%n] - <%@>,%v");
	spdlog::drop_all();
	return 0;
}

运行结果如下

答疑:为什么文件中写进的是“daily_logger”。而控制台是“ybhy”,是因为两者的logger对象不一致’
细节:在写入文件时注意:spdlog::set_level(spdlog::level::err);,如果你设计成“err”,那么文件中也只会保存等级为“error”以上的数据

输出信息

(三)数据输出到文件

(1)三种创建文件的方式

  1. basic_logger_mt
    a:说明:日志文件一直会被写入,不断变大
    b:案例:
    auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
    my_logger->info("Some log message");
    
  2. rotating_logger_mt
    a:说明:滚动日志,当日志文件超出规定大小时,会删除当前日志文件中所有内容,重新开始写入
    b:函数原型:rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
    c:参数介绍:参数max_files 规定了滚动文件的个数。当logger_name存满时,将其名称更改为logger_name.1,再新建一个logger_name文件来存储新的日志。再次存满时,把logger_name.1改名为logger_name.2,logger_name改名为logger_name.1,新建一个logger_name来存放新的日志。max_files 数量为几,就可以有几个logger_name文件用来滚动。
    d:案例:
    auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 256, 2);
    for (int i = 0; i < 10; ++i)
         rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
    
    e:测试结果
    每个文件内容如下,后缀数字越大,日志内容越早:测试结果
  3. daily_logger_mt
    a:说明:每天会新建一个日志文件,新建日志文件的时间可自己设定
    b:案例:
    	// Create a daily logger - a new file is created every day on 2:30am
        auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
        // trigger flush if the log severity is error or higher
        daily_logger->flush_on(spd::level::err);
        daily_logger->info(123.44);
    
    c:测试结果:
    测试
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++spdlog学习总结 的相关文章

  • 在C语言中使用“void”

    我很困惑为什么我们需要通过void转换为 C 函数 int f void return 0 versus int f return 0 什么是正确的做法以及为什么 In C int f 是一种老式的声明 它说f需要固定但未指定数量和类型的参
  • ASP.NET MVC 中的经典 ASP (C#)

    我有一个应用程序想要 最终 转换为 ASP NET MVC 我想要进行全面的服务升级 到 ASP NET 但想要使用当前的 ASP 内容来运行当前的功能 这样我就可以在对新框架进行增量升级的同时升级小部分 该站点严重依赖于不太成熟的 VB6
  • 迭代变量并查找特定类型实例的技术

    我想迭代进程中内存中的变量 通过插件动态加载 并查找特定类型的实例 以前我可以找到特定类型 或内存中的所有类型 我可以创建类型的实例 我可以获取作为不同类型的字段包含的实例 但我无论如何都不知道只是 搜索 特定类型的实例 一种方法是使用 W
  • Clang 编译器 (x86):80 位长双精度

    我正在尝试在 x86 Windows 平台上使用本机 80 位长双精度 海湾合作委员会选项 mlong double 80 https gcc gnu org onlinedocs gcc x86 Options html似乎不适用于 cl
  • JSON 数组到 C# 列表

    如何将这个简单的 JSON 字符串反序列化为 C 中的列表 on4ThnU7 n71YZYVKD CVfSpM2W 10kQotV 这样 List
  • C++ 异步线程同时运行

    我是 C 11 中线程的新手 我有两个线程 我想让它们同时启动 我可以想到两种方法 如下 然而 似乎它们都没有按照我的预期工作 他们在启动另一个线程之前启动一个线程 任何提示将不胜感激 另一个问题是我正在研究线程队列 所以我会有两个消费者和
  • 如何配置 WebService 返回 ArrayList 而不是 Array?

    我有一个在 jax ws 上实现的 java Web 服务 此 Web 服务返回用户的通用列表 它运行得很好 Stateless name AdminToolSessionEJB RemoteBinding jndiBinding Admi
  • ASP MVC:服务应该返回 IQueryable 的吗?

    你怎么认为 你的 DAO 应该返回一个 IQueryable 以便在你的控制器中使用它吗 不 您的控制器根本不应该处理任何复杂的逻辑 保持苗条身材 模型 而不是 DAO 应该将控制器返回给视图所需的所有内容 我认为在控制器类中看到查询 甚至
  • 在 2D 中将一个点旋转另一个点

    我想知道当一个点相对于另一个点旋转一定角度时如何计算出新的坐标 我有一个块箭头 想要将其相对于箭头底部中间的点旋转角度 theta 这是允许我在两个屏幕控件之间绘制多边形所必需的 我无法使用和旋转图像 从我到目前为止所考虑的情况来看 使问题
  • Qt 创建布局并动态添加小部件到布局

    我正在尝试在 MainWindow 类中动态创建布局 我有四个框架 它们是用网格布局对象放置的 每个框架都包含一个自定义的 ClockWidget 我希望 ClockWidget 对象在调整主窗口大小时相应地调整大小 因此我需要将它们添加到
  • 在非活动联合成员上使用“std::addressof”是否定义明确[重复]

    这个问题在这里已经有答案了 下面的代码是尝试实现constexpr的版本offsetof在 C 11 中 它可以在 gcc 7 2 0 和 clang 5 0 0 中编译 这取决于申请std addressof工会非活跃成员的成员 这是明确
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • 通过 NHibernate 进行查询,无需 N+1 - 包含示例

    我有一个 N 1 问题 我不知道如何解决它 可以在这个问题的底部找到完全可重复的样本 因此 如果您愿意 请创建数据库 设置 NUnit 测试和所有附带的类 并尝试在本地消除 N 1 这是我遇到的真实问题的匿名版本 众所周知 这段代码对于帮助
  • 如何挤出平面 2D 网格并赋予其深度

    我有一组共面 连接的三角形 即二维网格 现在我需要将其在 z 轴上挤出几个单位 网格由一组顶点定义 渲染器通过与三角形数组匹配来理解这些顶点 网格示例 顶点 0 0 0 10 0 0 10 10 0 0 10 0 所以这里我们有一个二维正方
  • 为什么拆箱枚举会产生奇怪的结果?

    考虑以下 Object box 5 int int int box int 5 int nullableInt box as int nullableInt 5 StringComparison enum StringComparison
  • strcmp 给出分段错误[重复]

    这个问题在这里已经有答案了 这是我的代码给出分段错误 include
  • 我在在线程序挑战编译器中遇到演示错误

    include
  • 双精度类型二维多维数组的 pinvoke 编组作为 c# 和 c++ 之间的输入和输出

    我有以下我正在尝试解决的双物质类型的 2d 多维数组的 c 和 c pinvoke 编组 我已经查看了以下热门内容以获得我目前拥有的内容使用双精度数组进行 P Invoke 在 C 和 C 之间编组数据 https stackoverflo
  • Googletest:如何异步运行测试?

    考虑到一个包含数千个测试的大型项目 其中一些测试需要几分钟才能完成 如果按顺序执行 整套测试需要一个多小时才能完成 通过并行执行测试可以减少测试时间 据我所知 没有办法直接从 googletest mock 做到这一点 就像 async选项
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检

随机推荐

  • 代码审计-Java项目&JDBC&Mybatis&Hibernate&注入&预编译&写法

    文章目录 Javaweb 数据库操作 模式 写法 预编译等 环境搭建 JDBC 注入分析 关于预编译 Mybatis 注入分析 Hibernate 注入分析 总结 Javaweb 代码审计SQL注入 INXEDU在线网校 Javaweb 数
  • python构建IP代理池(Proxy Pool)

    基本原理 代理实际上指的就是代理服务器 它的功能是代理网络用户去取得网络信息 也可以说它是网络信息的中转站 在我们正常请求一个网站时 是将请求发送给 Web 服务器 Web 服务器把响应传回给我们 如果设置了代理服务器 实际上就是在本机和服
  • 多数据源配置(application.properties或application.yml配置详情)

    1 导入Maven依赖
  • ssh key问题解决

    u r the butter of my bread the breath to my life Julie Julia 某些情况下 原来的ssh连接会失效 比如误删了 ssh下面的东西 这时 需要重新生成key并加入gitlab或gith
  • 列表的基本操作

    描述 在两行中分别输入一个字符串 分别将其转换为列表 a 和 b 按要求完成以下功能 1 输出两个列表的拼接结果 2 输出列表 a 重复3次的结果 3 输出列表 b 中第3个元素和最后一个元素 4 输出列表 a 中序号1至4之间的元素 5
  • git工具下载

    文章目录 下载客户端 git下载教程 git下载地址 Tortoise下载地址 Tortoise下载教程 Git以及Github详细解析教程 码云学习安装视频 下载客户端 git下载教程 https www cnblogs com xuew
  • Android 使用updatefun 来自动更新

    这几天研究了一下APP的自动更新 并且是那种最方便使用的 找了一下 找到一个框架 updatefun 使用方法比较简单 记录一下使用方法和遇到的问题 使用步骤 1 使用Android studio 的依赖方式 dependencies co
  • 快速定位当前页面的Activity

    方法1 通过AndroidStudio的Terminal 利用一个指令可以快速定位当前页面的类名 1 把手机用数据线连到电脑 手机打开到需要的定位的页面 2 打开AndroidStudio 在AndroidStudio底部选择Termina
  • 实现一个简单的python小脚本的一些必要步骤

    1 编写python代码时在开头添上 python27 2 设置环境变量路径 在系统变量path中新建一个你要运行python脚本的文件夹的绝对路径 D python 3 运行方式 a 直接双击xx py文件 b 添加环境变量后 在cmd中
  • 工程有限元(1)

    有限法的基本思想 有限元概述 结构分析问题 有限元法的思路 有限元法的一般步骤 本文内容是整理的 工程有限元 课程内容 便于日后复习以及读者学习 有限元概述 有限元法 Finite Element Anaslysis FEM 是通过数学描述
  • c++继承下

    继承的方式主要分为单继承 多继承 菱形继承 普通单继承 指向派生类的基类指针或者引用 其类型仍然属于基类类型 而不是派生类类型 include
  • for(auto i : v)遍历容器元素

    for auto i v 遍历容器元素 1 auto 2 auto 3 const auto 4 const auto C 11 新增了一种循环 基于范围 range based 的 for 循环 这简化了一种常见的循环任务 对数组 或容器
  • 计算机重新如何连接网络打印机,电脑怎样连接打印机,小编教你电脑如何连接网络打印机...

    打印机是办公室里经常会用到的一种办公设备 由于工作性质的不同 以及其他原因 网络打印机可以实现多台电脑连接 实现资源共享 网络打印机自带ip 只需指定ip就可以快速连接 那电脑如何连接网络打印机 下面 小编给大家讲解电脑连接网络打印机的技巧
  • 基于类帕累托贯序抽样算法求解单目标优化问题附matlab代码

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab完整代码及仿真定制内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器
  • 智能合约平台开发指南

    随着区块链技术的普及 智能合约平台已经成为了这个领域的一个重要趋势 智能合约可以自动化执行合同条款 大大减少了执行和监督合同条款所需的成本和时间 那么 如何开发一个智能合约平台呢 以下是一些关键步骤 一 选择合适的区块链平台 智能合约通常运
  • pgsql数据库实现导入导出

    pgsql数据库实现导入导出 1 导出表 pg dump h 数据库ip U 用户名 数据库名 t 表名 gt 路径 例 pg dump h 127 0 0 1 U sysdba data center t book gt data boo
  • Prompt入门

    Prompt的范式大抵是两种 续写Prefix 用在GPT2 3那种单向LM预训练模型上 输入 好好学习 翻译成英文 输出 good good study 完形填空 用在BERT那种MLM式预训练模型上 比如情感分类任务可以输入 这个饼不错
  • Idea 中 Git 不提交当前分支修改代码并切换分支

    1 当前分支修改代码切换分支 日常开发中 我们可能会碰到我们正在修改当前 01 分支的代码 突然要去修改另外一个 02 分支的代码情况 而我们 01 分支写的代码还未经过测试 并不能马上提交 这个时候我们切换到 02 分支就会有问题 比如弹
  • dubbo中的Mock实现机制

    Mock是SOA之中的一个很有用的功能 不仅可以用来进行服务降级 也可以用来在测试中模拟服务调用的各种异常情况 dubbo框架里面的mock是在服务使用者这一端实现的 下面对实现机制进行分析 1 Mock的植入 很显然 既然提供了mock机
  • C++spdlog学习总结

    C Spdlog学习笔记 spdlog简介 spdlog优点 一般日志功能设计 spdlog安装 spdlog琐碎知识点总结 spdlog程序测试 一 日志输出控制台 1 数据全部输出到控制台 2 指定某个等级以上的数据到控制台 二 输出格