第三部分:Spdlog 日志库的实现原理

Spdlog 是一个快速、异步的 C++ 日志库,被广泛应用于 C++ 项目中。在这篇文章中,我们将探讨 Spdlog 日志库的实现原理。

Spdlog 的结构

Spdlog 由五个主要组件构成:Loggers、Sinks、Formatters、Async Logger 和 Registry。每个组件都扮演着不同的角色,共同协作记录并输出日志消息。

  • Loggers :是 Spdlog 最基本的组件,负责记录日志消息。在 Spdlog 中,一个 Logger 对象代表着一个日志记录器,应用程序可以使用 Logger 对象记录不同级别的日志消息。
  • Sinks :决定了日志消息的输出位置。在 Spdlog 中,一个 Sink 对象代表着一个输出位置,例如控制台、文件、网络等。应用程序可以将不同的日志消息发送到不同的 Sink 中。
  • Formatters :负责将日志消息转换为特定格式。在 Spdlog 中,一个 Formatter 对象代表着一个消息格式器,应用程序可以使用不同的 Formatter 对象将日志消息转换为不同的格式。
  • Async Logger :是 Spdlog 的异步记录器,它负责将日志消息异步地写入到目标 Sink 中。当应用程序调用 Logger 对象记录一个日志消息时,该消息会被加入到一个队列中,然后异步地写入目标 Sink 中。这样可以避免多个线程同时访问 Sink,从而确保线程安全性。
  • Registry :用于管理 Spdlog 的所有组件。在 Spdlog 中,所有的 Loggers、Sinks、Formatters 和 Async Logger 都在一个全局注册表中注册,Registry 用于管理这些组件。

Image

Spdlog 记录日志的流程

当应用程序调用 Spdlog 记录日志时,Spdlog 的流程如下:

  1. 获取一个 Logger 对象。
  2. 使用该 Logger 对象记录一个日志消息,该消息包括日志级别、时间戳、线程 ID、文件名和行号等信息。
  3. 将日志消息传递给 Formatter,将消息转换为特定格式。
  4. 将格式化后的消息传递给 Async Logger。
  5. Async Logger 将消息写入目标 Sink,完成日志记录。

Spdlog 的流程非常简单,但是每个组件都扮演着重要的角色。Loggers 负责记录日志消息,Sinks 决定了日志消息的输出位置,Formatters 负责将日志消息转换为特定格式,Async Logger 异步地将日志消息写入到目标 Sink 中,Registry 用于管理这些组件。

Spdlog 的线程安全性

spdlog 允许我们自由创建线程安全和非线程安全(单线程)的日志,其设置在基类base_skin 中,

template<typename Mutex>
class SPDLOG_API base_sink : public sink
{
public:
    void log(const details::log_msg &msg) final;
protected:
    Mutex mutex_;
}

template<typename Mutex>
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
{
    std::lock_guard<Mutex> lock(mutex_);
    sink_it_(msg);
}

每个sink都会继承 base_sink,通过模板参数 Mutex 传入锁。可以看到写日志函数 log 调用了 std::lock_guard 来使用锁。

Mutex 可以自定义,需要提供下面两个接口:

void lock();
void unlock();

在实际使用中如果想要线程安全,可以传入c++的 mutex(c++11开始支持),也可以自定义。如下是一个声明线程安全例子:

using kafka_sink_mt = kafka_sink<std::mutex>;

当然spdlog 也为我们提供了单线程的 mutex:

struct null_mutex
{
    void lock() const {}
    void unlock() const {}
};

using kafka_sink_st = kafka_sink<spdlog::details::null_mutex>;

Spdlog 的同步和异步模式

同步模式

在同步模式下,Spdlog 将日志消息直接写入目标 Sink,不使用内存队列。这种模式下,应用程序在记录日志消息时,必须等待消息写入目标 Sink 后才能继续执行。同步模式可以保证日志消息的实时性,但是可能会影响程序的性能,特别是在大量记录日志消息时。如果应用程序不需要实时记录日志消息,可以使用异步模式来提高性能。

异步模式

在异步模式下,日志消息被加入到一个内存队列中,然后异步地写入目标 Sink。异步模式可以提高日志记录的性能,尤其是在多线程环境下,因为它可以避免多个线程同时访问 Sink,从而提高线程安全性。

在 Spdlog 中,异步模式由 Async Logger 实现。Async Logger 在后台运行一个线程,负责从内存队列中获取日志消息,并将其写入目标 Sink 中。Async Logger 可以配置多个 Sink,每个 Sink 都会有一个独立的内存队列。

Spdlog 提供了两种内存队列实现:unbounded 和 bounded。unbounded 内存队列没有大小限制,可以一直增长,直到内存耗尽。bounded 内存队列有一个固定的大小,超过大小限制后,新的消息将被丢弃。

在使用异步模式时,需要注意以下事项:

  • 处理内存队列时可能会出现内存分配问题和锁竞争问题,需要谨慎设计和测试。
  • 如果内存队列大小有限制,需要根据应用程序的需求和硬件资源进行适当的调整。
  • 在应用程序退出时,需要等待所有日志消息写入完成,否则可能会丢失一些日志消息。

异步模式可以大大提高日志记录的性能,但是也需要谨慎使用和测试。如果内存队列大小限制不当或处理不当,可能会导致内存占用过高或日志消息丢失等问题。

Spdlog 的性能

Spdlog 是一个高性能的日志库,它的性能优于其他许多日志库。Spdlog 的异步记录器和多线程支持使得它能够快速地记录大量的日志消息。

下面是spdlog性能:

同步模式:

[info] **************************************************************
[info] Single thread, 1,000,000 iterations
[info] **************************************************************
[info] basic_st         Elapsed: 0.17 secs        5,777,626/sec
[info] rotating_st      Elapsed: 0.18 secs        5,475,894/sec
[info] daily_st         Elapsed: 0.20 secs        5,062,659/sec
[info] empty_logger     Elapsed: 0.07 secs       14,127,300/sec
[info] **************************************************************
[info] C-string (400 bytes). Single thread, 1,000,000 iterations
[info] **************************************************************
[info] basic_st         Elapsed: 0.41 secs        2,412,483/sec
[info] rotating_st      Elapsed: 0.72 secs        1,389,196/sec
[info] daily_st         Elapsed: 0.42 secs        2,393,298/sec
[info] null_st          Elapsed: 0.04 secs       27,446,957/sec
[info] **************************************************************
[info] 10 threads, competing over the same logger object, 1,000,000 iterations
[info] **************************************************************
[info] basic_mt         Elapsed: 0.60 secs        1,659,613/sec
[info] rotating_mt      Elapsed: 0.62 secs        1,612,493/sec
[info] daily_mt         Elapsed: 0.61 secs        1,638,305/sec
[info] null_mt          Elapsed: 0.16 secs        6,272,758/sec

异步模式:

[info] -------------------------------------------------
[info] Messages     : 1,000,000
[info] Threads      : 10
[info] Queue        : 8,192 slots
[info] Queue memory : 8,192 x 272 = 2,176 KB 
[info] -------------------------------------------------
[info] 
[info] *********************************
[info] Queue Overflow Policy: block
[info] *********************************
[info] Elapsed: 1.70784 secs     585,535/sec
[info] Elapsed: 1.69805 secs     588,910/sec
[info] Elapsed: 1.7026 secs      587,337/sec
[info] 
[info] *********************************
[info] Queue Overflow Policy: overrun
[info] *********************************
[info] Elapsed: 0.372816 secs    2,682,285/sec
[info] Elapsed: 0.379758 secs    2,633,255/sec
[info] Elapsed: 0.373532 secs    2,677,147/sec

原文链接:https://www.cnblogs.com/listenwind666/p/17262275.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:第三部分:Spdlog 日志库的实现原理 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月19日

相关文章

  • 【Visual Leak Detector】配置项 ReportEncoding

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 配置文件中配置项 ReportEncoding 的使用方法。 同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 配置文件使用说明 2. 设置输出报告的编码格式 2.1 测试代码 2.2 ReportEncoding = ascii 时的输出 2.3 ReportEn…

    C++ 2023年4月18日
    00
  • 洛谷:P5716日份天数

    题目描述 输入年份和月份,输出这一年的这一月有多少天。需要考虑闰年。 输入格式 输入两个正整数,分别表示年份 \(y\) 和月数 \(m\),以空格隔开。 输出格式 输出一行一个正整数,表示这个月有多少天。 样例 #1 样例输入 #1 1926 8 样例输出 #1 31 样例输入 #2 2000 2 样例输出 #2 29 提示 数据保证 \(1583 \le…

    C++ 2023年4月24日
    00
  • 驱动开发:探索DRIVER_OBJECT驱动对象

    本章将探索驱动程序开发的基础部分,了解驱动对象DRIVER_OBJECT结构体的定义,一般来说驱动程序DriverEntry入口处都会存在这样一个驱动对象,该对象内所包含的就是当前所加载驱动自身的一些详细参数,例如驱动大小,驱动标志,驱动名,驱动节等等,每一个驱动程序都会存在这样的一个结构。 首先来看一下微软对其的定义,此处我已将重要字段进行了备注。 typ…

    C++ 2023年4月18日
    00
  • 【Visual Leak Detector】Release 模式下使用 VLD

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍如何在 Release 模式下使用 VLD。同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 思路概述 2. 在 QT 中实践 1. 思路概述 要在 RELEASE 模式下使用 VLD,必须在包含头文件 vld.h 前预先定义 VLD_FORCE_ENABLE 宏(参考 VL…

    C++ 2023年4月17日
    00
  • luogu_P1040 [NOIP2003 提高组] 加分二叉树

    P1040 [NOIP2003 提高组] 加分二叉树 – 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意:给你一颗中序遍历为1到n的二叉树,和每个节点的val。树的值=左子树的值×右子树的值+根的val,空树值为1,求整个树最大值和这个值树的前序遍历。 题解:区间dp。dp[l][r]表示最大值,root[l][r]表示最大值的根,枚举区…

    C++ 2023年4月27日
    00
  • 【Visual Leak Detector】配置项 SelfTest

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇介绍 VLD 配置文件中配置项 SelfTest 的使用方法。 同系列文章目录可见 《内存泄漏检测工具》目录 目录 说明 1. 配置文件使用说明 2. 设置是否开启泄漏自检 2.1 测试代码 2.2 SelfTest = off 时的输出 2.3 SelfTest = on 时的输出 2.4 …

    C++ 2023年4月18日
    00
  • 从0开始学习c++

    常量指针与指针常量 #include<iostream> using namespace std; int main() { int a = 10; int b = 20; // 常量指针与指针常量 // 1.常量指针 const修饰指针 指针的指向是可以修改的(指针变量中存的地址值可以修改) 指针指向的值不能改(不能通过解引用的形式修改地址中存…

    C++ 2023年4月24日
    00
  • 网络框架重构之路plain2.0(c++23 without module) 综述

    最近互联网行业一片哀叹,这是受到三年影响的后遗症,许多的公司也未能挺过寒冬,一些外资也开始撤出市场,因此许多的IT从业人员加入失业的行列,而且由于公司较少导致许多人求职进度缓慢,很不幸本人也是其中之一。自从参加工作以来,一直都是忙忙碌碌,开始总认为工作只是为了更好的生活,但是一旦工作停下来后自己就觉得失去了一点什么,所以很少有像最近这两个月左右空闲的时光。人…

    C++ 2023年4月17日
    00
合作推广
合作推广
分享本页
返回顶部