C++ 日志库 spdlog 使用教程

Spdlog是一个快速、异步、线程安全的C++日志库,他可以方便地记录应用程序的运行状态,并提供多种输出格式。官网:https://github.com/gabime/spdlog

安装教程可以参考:https://blog.csdn.net/Harrytsz/article/details/144887297

Spdlog的优点:

  • 只包含头文件
  • 速度很快
  • 无需依赖第三方库
  • 支持跨平台
  • 支持多线程—线程安全
  • 可对日志文件进行循环输出
  • 可每日生成日志文件
  • 可支持控制台日志输出
  • 可选的异步输出
  • 可定义日志格式

1、日志级别

trace = SPDLOG_LEVEL_TRACE // 最低级(用来记录代码执行轨迹)
debug = SPDLOG_LEVEL_DEBUG //      (用来记录debug信息)
info = SPDLOG_LEVEL_INFO   // 在上面的测试例子中用过
warn = SPDLOG_LEVEL_WARN
err = SPDLOG_LEVEL_ERROR
critical = SPDLOG_LEVEL_CRITICAL
off = SPDLOG_LEVEL_OFF     // 最高级

2、控制台打印日志

方式一,最简单的使用方式,将日志输出到控制台。

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>

void stdout_easy()
{
    // 根据参数,在内部创建了一个名为 "console" 的 logger,返回 logger 的智能指针。
    auto console = spdlog::stdout_color_mt("console");
    // 通过智能指针调用 logger 对象的函数。
    console->info("hello world");
}
int main()
{
    stdout_easy();
    return 0;
}

输出结果:
在这里插入图片描述

方式二,

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>

void stdout_example() {
    // create a color multi-threaded logger
    // spdlog::stdout_color_mt("console")函数创建一个名字为console的console logger,
    // 把这个 logger 注册到 spdlog 的全局注册表中,并且返回指向这个 logger 的指针(shared_ptr)
    auto console_stdout = spdlog::stdout_color_mt("console");
    // 如果我们不知道上方创建函数返回的指针,我们也可以直接通过 logger 名称来获取对应 logger 
    // 通过 get 来获取已经创建好的 logger,获取方式通过指针名,同时使用 info 函数输出日志内容
    spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}

int main() {
    stdout_example();
    return 0;
}

输出结果:
在这里插入图片描述

3、保存日志文件

下面的案例将日志记录保存到文件中,以便于长久保存。basic_logger_mt 只是 spdlog 中封装的其中一个日志记录器,还有很多其他的记录器。

#include<iostream>
#include<spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>

int main() {
    // 创建一个名为 basic_logger 的日志记录器,并且返回指针。
    // mt 表示多线程,意味着多个线程中使用同一个日志记录器。
    // 需要注意的是在文件内添加数据是通过追加的形式进行的,并且如果没有创建文件会自动创建文件。
    auto basic_logger = spdlog::basic_logger_mt("basic_logger", "./logs/basic_log.txt");
    for (int i = 0; i < 10000; i++) {
        basic_logger->info("Test file logger{}", i);
    }
    return 0;
}

在这里插入图片描述

4、自定义日志记录器

我们根据以上的学习之后,大体了解了其中的运行机制,所以我们就可以不使用他封装的日志记录器,而是使用他内部的 logger 对象封装适合自己的日志记录器。

需要注意的是,所谓的不同的日志记录器其实就是不同的 logger 对象,那么用什么来区分不同的 logger 对象呢,就是用创建logger对象时传入的参数 sink 对象,这个sink对象标记着不同的类型,把他作为参数 logger 会根据 sink 对象类型的不同创建不同的日志记录器。

这种方式有很大的用途,比如我们可以创建一个日志记录器,但是里边有多个类型的 sink,从而可以一个 logger 能够实现多种效果。

#include<iostream>
#include<spdlog/spdlog.h>
//#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/stdout_color_sinks.h>

void make_console_spdlog() {
    /*auto console_level = std::make_shared<spdlog::sinks::stdout_sink_mt>();*/    // 黑白日志
    auto console_level = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();  // 彩色日志
    auto console = std::make_shared<spdlog::logger>("console", console_level);
    console->info("hello world");
}

int main() {
    make_console_spdlog();
    return 0;
}

输出结果:
在这里插入图片描述

注意:通过手动创建的日志记录器是无法通过名字在 spdlog 中进行查找的,因为他并没有注册到 spdlog 的全局注册表。

void make_console_spdlog() {
    auto console_level = std::make_shared<spdlog::sinks::stdout_sink_mt>();
    auto console = std::make_shared<spdlog::logger>("console", console_level);
    // 直接使用这种方式去获取日志记录器发生错误
    spdlog::get("console")->info("hello world!");
}

所以在创建日志记录器完成后,要将其注册到spdlog的全局注册表中。

#include<iostream>
#include<spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>

void make_console_spdlog() {
    auto console_level = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    auto console = std::make_shared<spdlog::logger>("console", console_level);
    // 将创建的日志记录器注册到全局注册表中
    spdlog::register_logger(console);
    spdlog::get("console")->info("hello world!");
}

int main() {
    make_console_spdlog();
    return 0;
}

5、多记录器共享文件

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>

void multi_file_write()
{
    // 创建filesink,以便多个日志共享器共享。
    auto filesink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("./logs/multilogger.txt");
    // 创建两个日志记录器来进行测试
    auto onelogger = std::make_shared<spdlog::logger>("onelogger", filesink);
    auto twologger = std::make_shared<spdlog::logger>("twologger", filesink);
    // 注册进spdlog的全局注册表
    spdlog::register_logger(onelogger);
    spdlog::register_logger(twologger);

    for (int i = 0; i < 100; i++) {
        onelogger->info("onelogger write:{}", i);
        twologger->info("twologger write:{}", i);
    }
}

int main() {
    multi_file_write();
    return 0;
}

在这里插入图片描述

6、日志分割

在我们的应用程序有很多日志需要记录时,每个日志文件就会变得非常大,这样就会影响应用程序的性能以及稳定性。所以为了解决这个问题,可以使用Spdlog的日志分割功能。

rotating log 滚动日志,当日志文件超出规定大小时,会删除当前日志文件中所有内容,重新开始写入

#include<iostream>
#include<spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>

void rotaing_file() {
    // 首先定义要使用分割文件的大小和数量
    int max_size = 1048576 * 5;
    int file_size = 3;
    // 创建 rotaing_logger_mt 日志记录器
    auto rotaing_file = spdlog::rotating_logger_mt("rotaing_file", "./logs/rotaingLogger.txt", max_size, file_size);

    for (int i = 0; i < 100000; i++) {
        rotaing_file->info("rotaing file:{}", i);
    }
}

int main() {
    rotaing_file();
    return 0;
}

7、定时日志

Spdlog还可以实现定时创建文件存储日志的功能。

#include<iostream>
#include<spdlog/spdlog.h>
#include <spdlog/sinks/daily_file_sink.h>

void daily_log() {
    // 创建daily_log日志
    // 参数介绍 1日志记录器名称 2文件名 3时 4分
    auto daily_log = spdlog::daily_logger_mt("daily_log", "./logs/daily_log.txt", 16, 18);

    for (int i = 0; i < 100000; i++) {
        daily_log->info("daily log print {}", i);
    }
}

int main() {
    daily_log();
    return 0;
}

8、记录器包含多个 sink

#include<iostream>
#include<spdlog/spdlog.h>
#include<spdlog/sinks/stdout_color_sinks.h>
#include<spdlog/sinks/basic_file_sink.h>

void multi_sink() {
    // 创建两个sink,一个用于控制台输出,一个用于文件输出
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("./logs/multi_sink.txt");
    // 设置两者输出等级
    console_sink->set_level(spdlog::level::info);
    file_sink->set_level(spdlog::level::debug);

    // 将两种不同的sink放到一个日志输出器中
    auto multi_sink = std::make_shared<spdlog::logger>("multi_sink", spdlog::sinks_init_list({ console_sink, file_sink }));
    multi_sink->set_level(spdlog::level::debug);
    // 输出不同等级日志测试
    multi_sink->info("hello world info");
    multi_sink->debug("hello world debug");
}

int main() {
    multi_sink();
    return 0;
}

9、其他介绍

通过上面的学习,我们学会了 spdlog 其函数的基本使用方式,下面对 spdlog 中常用的一些函数和类进行介绍。

  • level_enum:日志等级枚举,包括trace、info、debug、warn、err、critical、off、n_levels。
  • sink:日志记录槽,进行底层操作(比如格式化内容,输出内容到控制台/文件)的类。spdlog自带的几种sinks已经能满足日常需求,也可以派生其基类设计新的 sink 以满足特殊的需求。sink类主要使用的函数包括:
    • set_pattern(const std::string&):设置日志输出格式。
    • set_level(level_enum):设置日志输出的最低等级。
    • log(log_msg):由logger自动调用,外部不会主动调用。
  • logger:日志记录器,被顶层调用来输出日志的类。一个logger对象中存储有多个sink,当调用logger的日志输出函数时,logger会调用自身存储的所有sink对象的log(log_msg)函数进行输出。与自身的sink对应,spdlog也自带了几种logger。logger类主要使用的函数包括:
    • set_pattern(const std::string&):设置logger包括的所有sink的日志输出内容格式。
    • set_level(level_enum):设置logger日志输出最低等级,如果logger包含的sink没有设置日志等级的话,则会为其设置日志等级。
    • log(level_enum level, log_msg content):按照level等级进行输出content,logger其中日志输出最低等级小于或等于level的sink会执行输出操作。
    • info(content, arg1, arg2…):按照trace等级进行输出,输出内容由content与后面的采纳数格式化组成。荣磊的函数还包括debug/trace/warn…。
  • st/mt:对象版本,spdlog 中 logger 对象和 sink 对象都有两种版本,一种是以 st 结尾的单线程版本,以及以 mt 结尾的多线程版本。
    • st:单线程版本,不用加锁,效率更高。
    • mt:多线程版本,用于多线程程序是线程安全的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/949219.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

音视频入门基础:MPEG2-PS专题(3)——MPEG2-PS格式简介

一、引言 本文对MPEG2-PS格式进行简介。 进行简介之前&#xff0c;请各位先下载MPEG2-PS的官方文档。ITU-T和ISO/IEC都分别提供MPEG2-PS的官方文档。但是ITU提供的文档是免费的&#xff0c;ISO/IEC是付费的&#xff0c;所以我们主要阅读ITU提供的官方文档&#xff0c;比如较新…

Multisim更新:振幅调制器+解调器(含仿真程序+文档+原理图+PCB)

前言 继3年前设计的&#xff1a;Multisim&#xff1a;振幅调制器的设计&#xff08;含仿真程序文档原理图PCB&#xff09;&#xff0c;有读者表示已经不能满足新需求&#xff0c;需要加上新的解调器功能&#x1f602;&#x1f602;&#x1f602;&#xff0c;鸽了很久这里便安排…

BGP(Border Gateway Protocol)路由收集器

全球 BGP&#xff08;边界网关协议&#xff09;路由收集器的分布情况以及相关数据。以下是主要的信息解读&#xff1a; 地图标记&#xff1a; 每个绿色点代表一个路由收集器的位置。路由收集器分布在全球不同的地区&#xff0c;覆盖了五大区域&#xff1a; ARIN&#xff08;美…

【Rust自学】10.5. 生命周期 Pt.1:生命周期的定义与意义、借用检查器与泛型生命周期

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 10.5.1. 什么是生命周期 Rust的每个引用都有自己的生命周期&#xff0c;生命周期的作用是让引用保持有效&#xff0c;也可以说它是保持引…

Vue2: table加载树形数据的踩坑记录

table中需要加载树形数据,如图: 官网给了两个例子,且每个例子中的tree-props都是这么写的: :tree-props="{children: children, hasChildren: hasChildren}" 给我一种错觉,以为数据结构中要同时指定children和hasChildren字段,然而,在非懒加载模式下,数据结…

深入了解 SSL/TLS 协议及其工作原理

深入了解 SSL/TLS 协议及其工作原理 一. 什么是 SSL/TLS?二. SSL/TLS 握手过程三. SSL/TLS 数据加密与传输四. 总结 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子都在歌唱 一. 什么是 SSL/TLS? 安全套接层&am…

sqlserver sql转HTMM邮件发送

通过sql的形式&#xff0c;把表内数据通过邮件的形式发送出去 declare title varchar(100) DECLARE stat_date CHAR(10),create_time datetime SET stat_dateCONVERT(char(10),GETDATE(),120) SET create_timeDATEADD(MINUTE,-20,GETDATE()) DECLARE xml NVARCHAR (max) DECLAR…

用QT实现 端口扫描工具1

安装在线QT&#xff0c;尽量是完整地自己进行安装&#xff0c;不然会少包 参考【保姆级图文教程】QT下载、安装、入门、配置VS Qt环境-CSDN博客 临时存储空间不够。 Windows系统通常会使用C盘来存储临时文件。 修改临时文件存储位置 打开系统属性&#xff1a; 右键点击“此电…

Selenium 自动化,如何下载正确的 ChromeDriver

在 Python 的 Selenium 自动化操作中&#xff0c;chromedriver 是不可或缺的驱动程序。没有正确安装对应版本的驱动&#xff0c;运行代码时常常会遇到报错问题&#xff0c;比如 “session not created: This version of ChromeDriver only supports Chrome version XX”。 今天…

泊松融合 实例2025

目录 例子1: 实现代码: 原作者代码: 本博客直接给出来最好的效果和源代码 参数说明: 效果不好,不推荐的参数:MONOCHROME_TRANSFER,NORMAL_CLONE 例子1: 目标图: 原图: 效果图: 实现代码: 坐标是要目标图上中心点坐标: import cv2if __na

前端如何从入门进阶到高级

在前端学习的道路上&#xff0c;我们将其划分为三个阶段&#xff1a;入门、实战和进阶。以下是各阶段的学习指南 一、入门阶段 在入门阶段&#xff0c;我们的目标是掌握前端的基本语法和知识&#xff0c;以便能够独立解决一些基础问题。这一阶段&#xff0c;我们建议通过视频…

Python爬虫基础——认识网页结构(各种标签的使用)

1、添加<div>标签的代码定义了两个区块的宽度和高度均为100px&#xff0c;边框的格式也相同&#xff0c;只是区块中显示的内容不同&#xff1b; 2、添加<ul>和<ol>标签分别用于定义无序列表和有序列表。<il>标签位于<ul>标签或<ol>标签之…

基于W2605C语音识别合成芯片的智能语音交互闹钟方案-AI对话享受智能生活

随着科技的飞速发展&#xff0c;智能家居产品正逐步渗透到我们的日常生活中&#xff0c;其中智能闹钟作为时间管理的得力助手&#xff0c;也在不断进化。基于W2605C语音识别与语音合成芯片的智能语音交互闹钟&#xff0c;凭借其强大的联网能力、自动校时功能、实时天气获取、以…

Python提取目标Json键值:包含子嵌套列表和字典

目标&#xff1a;取json中所有的Name、Age字典 思路&#xff1a;递归处理字典中直接包含子字典的情况&#xff0c; import jsondef find_targ_dicts(data,key1,key2):result {}if isinstance(data, dict):if key1 in data and key2 in data: # 第一层字典中包含key1和key2re…

你已经分清JAVA中JVM、JDK与JRE的作用和关系了吗?

你已经分清JAVA中JVM、JDK与JRE的作用和关系了吗&#xff1f; 一. JVM、JDK与JRE的关系二. JVM、JDK与JRE的作用2.1 什么是JVM&#xff1f;2.2 什么是JDK&#xff1f;2.3 什么是JRE&#xff1f; 前言 点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有…

深度学习blog-RAG构建高效生成式AI的优选路径

RAG&#xff08;Retrieval-Augmented Generation&#xff09; 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;模型的性能和应用场景也不断扩展。其中&#xff0c;检索增强生成&#xff08;RAG, Retrieval-Augmented Generation&#xff09;模型作为一种新…

数据中台与数据治理服务方案[50页PPT]

本文概述了数据中台与数据治理服务方案的核心要点。数据中台作为政务服务数据化的核心&#xff0c;通过整合各部门业务系统数据&#xff0c;进行建模与加工&#xff0c;以新数据驱动政府管理效率提升与政务服务能力增强。数据治理则聚焦于解决整体架构问题&#xff0c;确保数据…

AI生成PPT,效率与创意的双重升级

AI生成PPT&#xff0c;效率与创意的双重升级&#xff01;在信息化高速发展的今天&#xff0c;我们的工作节奏被无限压缩&#xff0c;效率成为了衡量工作能力的重要指标。而制作PPT这种事&#xff0c;总是让人又爱又恨——既想做得出彩&#xff0c;又不想花费大量时间。现在有了…

【HF设计模式】05-单例模式

声明&#xff1a;仅为个人学习总结&#xff0c;还请批判性查看&#xff0c;如有不同观点&#xff0c;欢迎交流。 摘要 《Head First设计模式》第5章笔记&#xff1a;结合示例应用和代码&#xff0c;介绍单例模式&#xff0c;包括遇到的问题、采用的解决方案、以及达到的效果。…

嵌入式linux系统中QT信号与槽实现

第一:Qt中信号与槽简介 信号与槽是Qt编程的基础。因为有了信号与槽的编程机制,在Qt中处理界面各个组件的交互操作时变得更加直观和简单。 槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。 案例操作与实现: #ifndef …