C++ 好用的日志库--spdlog

news2024/11/18 7:32:25

背景

spdlog 是一个快速、异步的、header-only 的 C++ 日志库。它提供了简单易用的 API 并具有高性能和可扩展性。

下载和使用

下载

spdlog 库下载地址:github 链接

hello world

在使用时只需要 include 整个 /include/spdlog 文件夹即可。

#include "spdlog/spdlog.h"
int main() {
    spdlog::info("hello world");
    return 0;
}

运行结果如下:

如上图所示,spdlog 库上手非常简单。

基本概念

核心组件

spdlog 有以下基本组成部分:

  • Registry(日志记录器注册表):Registry 用于管理所有已创建的 Logger 对象。
  • Logger(日志记录器):Logger 是打印日志的核心对象,负责记录日志消息。可以根据需要创建多个Logger 对象。
  • Sink(日志输出):Sink 是 Logger 的目标输出位置,它指定了日志消息的最终存储位置。每个 Logger 内包含一个 Sink 组成的 vector。
  • Formatter(日志格式化):Formatter 用于格式化日志消息的输出格式。

通过以上组件,可以灵活地配置和使用 spdlog,以满足不同的日志需求。例如,可以创建多个 Logger 对象,并将它们的日志消息输出到不同的文件中;也可以自定义日志消息的格式,添加时间戳和其他元数据。

日志格式

spdlog 自带了默认的 formatter,其格式为:[日期时间] [logger名] [log级别] log内容。

日志级别

spdlog 提供了一组日志级别,用于控制记录哪些级别的日志消息:

  • trace:最详细的日志级别,提供追踪程序执行流程的信息。
  • debug:调试级别的日志信息,用于调试程序逻辑和查找问题。
  • info:通知级别的日志信息,提供程序运行时的一般信息。
  • warn:警告级别的日志信息,表明可能发生错误或不符合预期的情况。
  • error:错误级别的日志信息,表明发生了某些错误或异常情况。
  • critical:严重错误级别的日志信息,表示一个致命的或不可恢复的错误。
    通过设置日志记录器的级别,可以控制哪些级别的日志进行输出:
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    logger->set_level(spdlog::level::warn);

    logger->trace("trace message");
    logger->debug("debug message");
    logger->info("info message");
    logger->warn("warn message");
    logger->error("error message");
    logger->critical("critical message");

    return 0;
}

运行结果如下:

日志参数

spdlog 绑定了 fmt 库,可以用于格式化输出日志内容:

#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("log message {},{}","hello",123);
    return 0;
}

运行结果如下:

spdlog 快速上手

概述

spdlog 提供了一系列工厂函数用于创建 Logger。其中以 _mt 后缀的表示创建多线程的日志记录器、以 -st 后缀的表示创建单线程的日志记录器。

创建控制台 Logger

#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("hello world");
    return 0;
}

运行结果如下:

创建基本文件 Logger

#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() {
    auto logger = spdlog::basic_logger_mt("file","my_log.log");
    logger->info("hello world");
    return 0;
}

运行结果如下:

创建滚动文件 Logger

可以设置日志文件的大小及数量限定:

#include "spdlog/spdlog.h"
#include "spdlog/sinks/rotating_file_sink.h"
int main() {
    auto max_size = 1024*2;// 每个文件最大 2 k
    auto max_files = 3;//最多滚动 3 次
    auto logger = spdlog::rotating_logger_mt("file","my_log.log", max_size, max_files);

    for( int i = 0 ;i < 100;++i)
    {
        logger->info("rotating file log test");
    }
    return 0;
}

运行结果如下:

创建每日 Logger

可以在每天固定时间点创建一个新的日志文件:

#include "spdlog/spdlog.h"
#include "spdlog/sinks/daily_file_sink.h"
int main() {
    auto logger = spdlog::daily_logger_mt("daily_logger", "my_log.log", 0, 0);
    return 0;
}

创建异步日志

可以与 spdlog::async_factory 搭配使用,以实现异步日志。异步记录可以提高程序的性能,因为日志写入操作不会阻塞主线程。

#include "spdlog/async.h"
#include "spdlog/sinks/stdout_color_sinks.h"
int main() {
    auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("async_logger");
    logger->info("123");
    return 0;
}

运行结果如下:

创建 Logger

spdlog 提供的工厂方法封装了 sink 的创建过程,也可以根据需要先创建 sink ,再创建 Logger。

创建 Sink

sink 是将日志实际写入其目标(文件、控制台、数据库等)的对象,且应仅负责单个目标。

创建控制台 sink

 auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();

创建文件 sink

auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("my_log.log");

创建每日文件 sink

auto sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("my_log.log"23,59);

创建滚动文件 sink

auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("my_log.log",1048576 * 5, 3,false);

创建流输出 sink

std::ostringstream oss;
auto ostream_sink = std::make_shared<spdlog::sinks::ostream_sink_mt> (oss);

根据 sink 创建 Logger

创建单 sink 记录器

可以直接在构造函数中传入 sink 来创建 Logger:

int main() {
    auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto logger = std::make_shared<spdlog::logger>("my_logger", sink);
    logger->info("hello world ");
    return 0;
}

创建多 sink 记录器

也可以传入一个 sink 列表来创建 Logger,每个 sink 可单独设置日志级别和样式:

int main() {
    auto sink1 = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto sink2 = std::make_shared<spdlog::sinks::basic_file_sink_mt>("my_log.log");
    spdlog::sinks_init_list sinks = {sink1,sink2};

    auto logger = std::make_shared<spdlog::logger>("my_logger", sinks.begin(),sinks.end());

    logger->info("hello world ");
    return 0;
}

创建共用 sink 记录器

多个 Logger 也可以共用相同的 sink :

int main() {
    auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto logger1 = std::make_shared<spdlog::logger>("logger1", sink);
    auto logger2 = std::make_shared<spdlog::logger>("logger2", sink);
    auto logger3 = std::make_shared<spdlog::logger>("logger3", sink);

    logger1->info("hello world ");
    logger2->info("hello world ");
    logger3->info("hello world ");
    return 0;
}

Logger 注册与获取

spdlog 提供了一个全局注册和获取 logger 的方法。

Logger 注册

使用 spdlog 工厂方法创建的 logger 无需手动注册即可根据名称获取,手动创建的 logger 需要注册。

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

void register_logger()
{
    auto sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
    auto logger = std::make_shared<spdlog::logger>("my_logger", sink);
    spdlog::register_logger(logger);
}
int main() {
    register_logger();
    
    auto logger = spdlog::get("my_logger");
    logger->info("hello world");
    return 0;
}

Logger 删除

手动注册的全局 logger 也可以删除:

spdlog::drop("my_logger");//全局注册中删除指定 logger
spdlog::drop_all();// 删除所有注册的 logger

Logger 的使用与设置

设置默认 Logger

spdlog 提供了最为便捷的默认 logger,注意,该logger在全局公用:

spdlog::info("hello world");

可以设置自定义的 logger 为全局默认:

auto logger = spdlog::stdout_color_mt("my_log");
spdlog::set_default_logger(logger);

设置日志级别

logger 和 sink 都可以单独指定日志级别。

设置指定 Logger 级别

auto logger = spdlog::stdout_color_mt("my_log");
logger->set_level(spdlog::level::debug);

设置指定 sink 级别

auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
sink->set_level(spdlog::level::debug);

设置默认 Logger 级别

spdlog::set_level(spdlog::level::debug);

设置缓存刷新策略

创建好 Logger 后建议设置 flush 方式,否则可能无法立刻在文件中看到 logger 的内容:

定时刷新

spdlog::flush_every(std::chrono::seconds(5));// 定期为所有注册的logger隔5秒刷新

基于级别刷新

auto logger = spdlog::stdout_color_mt("my_log");
logger->flush_on(spdlog::level::warn);//遇到 warn 就立即刷新

手动刷新

auto logger = spdlog::stdout_color_mt("my_log");
logger->flush()// logger 将依次在每个 sink 上调用 flush

spdlog 使用进阶

记录自定义类型

用户自定义类型对象作为日志参数进行记录,需要重置输出运算符:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ostr.h"

class demo{
public:
    demo(int id_,const std::string &name_):id(id_),name(name_){}
private:
    int         id ;
    std::string name;
public:
    friend std::ostream& operator<<(std::ostream& os, const demo& d);
};

std::ostream &operator<<(std::ostream &os, const demo &d) {
    os << "id:"<<d.id<<", name:"<<d.name<<std::endl;
    return os;
}

int main() {
    demo d(10,"demo_1");
    auto logger = spdlog::stdout_color_mt("console");
    logger->info("log message {}",d);
    return 0;
}

运行结果如下:

记录 vector 中数据

vector 对象可以直接作为参数进行输出:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ranges.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    std::vector<int> vec{1,2,3,4,5};

    logger->info("vector data :{}",vec);
    return 0;
}

运行结果如下:

记录运行时间

使用 spdlog::stopwatch 对象可以记录代码运行时间:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/stopwatch.h"
#include <thread>

void test()
{
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
int main() {
    auto logger = spdlog::stdout_color_mt("console");
    spdlog::stopwatch sw;
    test();
    logger->info("test run {} seconds",sw);
    return 0;
}

运行结果如下:

记录十六进制数据

使用 to_hex 可以吧二进制数据转十六进制进行记录:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/bin_to_hex.h"
int main() {
    auto logger = spdlog::stdout_color_mt("console");

    unsigned char data[] = {0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56};
    logger->info("Hex string:  {} ", spdlog::to_hex(std::begin(data), std::begin(data)+sizeof(data)));

    return 0;
}

运行结果如下:

还可以显示对应 ASCII 值:

int main() {
    auto logger = spdlog::stdout_color_mt("console");

    unsigned char data[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
    
    logger->info("Hex string:  {:a} ", spdlog::to_hex(std::begin(data), std::begin(data)+sizeof(data),4));
    return 0;
}

运行结果如下:

记录文件名及行号

在使用 spdlog 记录日志时,可以通过格式化字符串来包含方法名、行号和文件名的信息:

#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
#include <spdlog/spdlog.h>
#include "spdlog/sinks/stdout_color_sinks.h"

int main() {
    spdlog::set_pattern("[%H:%M:%S] [%n] [%^---%L---%$] [%s:%#] [%!] %v");
    auto logger = spdlog::stdout_color_mt("my_log");
    SPDLOG_LOGGER_INFO(logger, "This is a log message");
    return 0;
}

运行结果如下:

一定确保 SPDLOG_ACTIVE_LEVEL 定义的日志级别低于或等于你期望的日志级别,并且在包含 spdlog.h 之前定义了它。

其他特殊 Logger

qt sink

qt_sink 可以向 QTextBrowser、QTextEdit 等控件输出日志消息:

#include "spdlog/sinks/qt_sinks.h"
auto logger = spdlog::qt_logger_mt("QLogger",ui->textBrowser);
logger->info("hello QTextBrowser");
logger->warn("this msg from spdlog");

运行结果如下:

msvc sink

msvc_sink 使用 OutputDebugStringA 向 Windows调试接收器发生日志记录:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/msvc_sink.h"
int main() {
    auto sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
    auto logger = std::make_shared<spdlog::logger>("msvc_logger", sink);
    logger->info("debug log test...");
    return 0;
}

运行结果如下:

消息过滤 sink

dup_filter_sink 可以实现重复消息删除:

#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/dup_filter_sink.h"
int main() {
    auto sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    auto dup_filter_sink= std::make_shared<spdlog::sinks::dup_filter_sink_mt>(std::chrono::seconds(5));
    dup_filter_sink->add_sink(sink);

    auto logger = std::make_shared<spdlog::logger>("msvc_logger", dup_filter_sink);
    logger->info("hello world");
    logger->info("hello world");
    logger->info("hello world");
    logger->info("hello world");
    logger->info("log msg");
   
    return 0;
}

运行结果如下:

ringbuffer sink

ringbuffer_sink 将最新的日志消息保存在内存中:

#include <spdlog/spdlog.h>
#include "spdlog/sinks/ringbuffer_sink.h"
#include "spdlog/fmt/ranges.h"

int main() {
    auto ringbuffer_sink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(5);
    auto logger = std::make_shared<spdlog::logger>("ringbuffer_logger", ringbuffer_sink);

    for (int i = 0; i < 20; ++i) {
        logger->info("Log message {}", i);
    }

    std::vector<std::string> log_messages = ringbuffer_sink->last_formatted(1);
    spdlog::info("{}",log_messages);

    return 0;
}

运行结果如下:

udp sink

spdlog 提供的一个封装了 UDP 传输的 logger。它可以将日志记录通过 UDP 协议发送到指定的目标地址和端口:

#include <spdlog/spdlog.h>
#include "spdlog/sinks/udp_sink.h"
int main() {
    spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091);
    auto my_logger = spdlog::udp_logger_mt("udplog", cfg);
    my_logger->info("hello world");
    
    return 0;
}

支持回调的 Logger

callback_logger_mt 是一个支持设置调用回调函数的日志记录器:

#include <spdlog/spdlog.h>
#include <iostream>
#include "spdlog/sinks/callback_sink.h"
int main() {
    auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg & msg) {
        std::cout << msg.payload.data() << std::endl;
    });

    logger->info("123");
    return 0;
}

运行结果如下:

微信搜索“编程猿来如此”关注公众号获取更多内容。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【excel技巧】进阶版:excel文件批量提取文件名

前面给大家分享了简单的excel提取文件名方法&#xff0c;今天继续分享&#xff0c;进阶版excel文件批量提取文件名的方法。 点击excel工具栏中的【数据】功能&#xff0c;点击获取数据 – 来自文件 – 从文件夹 然后找到需要提取文件名的文件夹&#xff0c;点击打开&#xff0…

直线导轨使用中常见的问题有哪些?

直线导轨作为设备的核心部件之一&#xff0c;起着导向和支撑的作用功能。目前&#xff0c;已被广泛应用在各行各业中&#xff0c;大到机械设备&#xff0c;小到抽屉&#xff0c;我们都能看到直线导轨的身影&#xff0c;可以说&#xff0c;直线导轨已经悄无声息的进入到我们的生…

OC与Swift的相互调用

OC调用Swift方法 1、在 Build Settings 搜索 Packaging &#xff0c;设置 Defines Module 为 YES 2、新建 LottieBridge.swift 文件&#xff0c;自动生成桥 ProductName-Bridging-Header.h 3、在 LottieBridge.swift 中&#xff0c;定义Swift类继承于OC类&#xff0c;声明 obj…

站点可靠性工程 (SRE)

随着世界各地的组织努力开发安全、可靠、可扩展且可持续的 IT 基础架构&#xff0c;对高效基础架构监控和管理的需求日益增长&#xff0c;企业正在用不可扩展的遗留架构换取现代解决方案&#xff0c;在尖端技术的推动下&#xff0c;这些使基础设施管理过程更加顺畅和轻松&#…

Django学习笔记-默认的用户认证系统(auth)

一、Django默认的用户认证系统 Django 自带一个用户验证系统。它负责处理用户账号、组、权限和基于cookie的用户会话。 Django 验证系统处理验证和授权。简单来说&#xff0c;验证检验用户是否是他们的用户&#xff0c;授权决定已验证用户能做什么。这里的术语验证用于指代这…

【云原生】K8S二进制搭建一

目录 一、环境部署1.1操作系统初始化 二、部署etcd集群2.1 准备签发证书环境在 master01 节点上操作在 node01与02 节点上操作 三、部署docker引擎四、部署 Master 组件4.1在 master01 节点上操 五、部署Worker Node组件 一、环境部署 集群IP组件k8s集群master01192.168.243.1…

虹科方案 | 虹科AR助力汽车产业降本增效,实现数字化转型!

虹科AR远程解决方案 将高性能的Vuzix AR眼镜与工业远程软件相结合&#xff0c;一线员工使用AR眼镜呼叫专家&#xff0c;由远程专家进行诊断并给出建议&#xff0c;支持一线员工与远程专家实时语音视频交互、AR标注指引、发送文件图片并进行会议录制&#xff0c;帮助一线员工解…

8.物联网操作系统之事件标志组

。事件标志组定义 FreeRTOS事件标志组介绍 FreeRTOS事件标志组工作原理 一。事件标志组定义 信号量信号量只能实现任务与单个事件或任务间的同步。但是某些任务可能会需要与多个事件或任务进行同步&#xff0c;此时就可以使用事件标志组来解决。事件标志组能够实现某个任务与…

opencv36-形态学操作-膨胀 cv2.dilate()

膨胀操作是形态学中另外一种基本的操作。膨胀操作和腐蚀操作的作用是相反的&#xff0c;膨胀操作能对图像的边界进行扩张。膨胀操作将与当前对象&#xff08;前景&#xff09;接触到的背景点合并到当前对象内&#xff0c;从而实现将图像的边界点向外扩张。如果图像内两个对象的…

接口测试原理和基本步骤

目录 1、接口测试原理 2、接口测试的实现 3、接口测试用例 4、接口测试工具 5、HTTP协议 6、JMeter 7、抓包 8、接口测试可以发现什么样的Bug&#xff1f; 1、接口测试原理 接口测试&#xff0c;实际上是针对于接口做测试的。 那么接口是什么&#xff1f; 软件开发&…

人力管理系统servlet+jsp人事考勤员工部门java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 人力管理系统servletjsp 系统有1权限&#xff1a;管理…

Linux - gcc/g++工具使用

gcc/g是用于编译C/C程序的编译器 1.编译过程 1. 预处理&#xff08;头文件展开&#xff0c;条件编译&#xff0c;进行宏替换&#xff0c;去注释等) 2. 编译&#xff08;C语言汇编语言) 3. 汇编&#xff08;汇编->可重定位目标二进制文件&#xff0c;不可以被执行的&#xff…

图解系列 非对称加密应用场景

非对称加密使用一对密钥&#xff0c;分别是公钥&#xff08;public key&#xff09;和私钥&#xff08;private key&#xff09;。 使用场景 加密场景 加密场景 公钥加密、私钥解密&#xff1a; 公钥加密&#xff1a;在这种场景下&#xff0c;使用接收方的公钥对数据进行加密…

Spring源码解析(六):bean定义后置处理器ConfigurationClassPostProcessor

Spring源码系列文章 Spring源码解析(一)&#xff1a;环境搭建 Spring源码解析(二)&#xff1a;bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三)&#xff1a;bean容器的刷新 Spring源码解析(四)&#xff1a;单例bean的创建流程 Spring源码解析(五)&…

数据结构 | 搜索和排序——排序

目录 一、冒泡排序 二、选择排序 三、插入排序 四、希尔排序 五、归并排序 六、快速排序 排序是指将集合中的元素按照某种顺序排序的过程。 一、冒泡排序 冒泡排序多次遍历列表。它比较相邻的元素&#xff0c;将不合顺序的交换。每一轮遍历都将下一个最大值放到正确的位…

校园跑腿小程序运营攻略

作为一名校园跑腿小程序的运营者&#xff0c;你可能会面临诸如用户获取、平台推广、服务质量保证等挑战。在本篇推文中&#xff0c;我将为你提供一些关键的运营策略&#xff0c;帮助你成功运营校园跑腿小程序。 1. 用户获取和留存 用户是校园跑腿小程序成功的关键。以下是一些…

非凸科技受邀参加中科大线上量化分享

7月30日&#xff0c;非凸科技受邀参加由中国科学技术大学管理学院学生会、超级量化共同组织的“打开量化私募的黑箱”线上活动&#xff0c;分享量化前沿以及求职经验&#xff0c;助力同学们拿到心仪的offer。 活动上&#xff0c;非凸科技量化策略负责人陆一洲从多个角度分享了如…

基于Windows手动编译openssl和直接安装openssl

零、环境 win10-64位 VS2019 一、手动编译 1、安装perl https://platform.activestate.com/ActiveState-Projects/ActiveState-Perl-5.36.0 两种方法都没能成功。。第一种下载后会得到一个 state-remote-installer.exe&#xff0c;然后安装时会在命令行中执行&#xff0c;…

PtahDAO:全球首个DAO治理资产信托计划的金融平台

金融科技是当今世界最具创新力和影响力的领域之一&#xff0c;区块链技术作为金融科技的核心驱动力&#xff0c;正在颠覆传统的金融模式&#xff0c;为全球用户提供更加普惠、便捷、安全的金融服务。在这个变革的浪潮中&#xff0c;PtahDAO&#xff08;普塔道&#xff09;作为全…

UMS攸信入选2023年先进制造业倍增计划企业名单,为企业发展增添新助力!

根据《厦门市人民政府关于印发先进制造业倍增计划实施方案&#xff08;2022-2026年&#xff09;的通知》&#xff08;厦府规〔2022〕3号&#xff09;&#xff0c;经市政府专题会研究&#xff0c;确定2023年先进制造业倍增计划企业名单。 关于2023年先进制造业倍增计划企业名单和…