高性能日志系统 日志格式化输出逻辑

概述

日志消息是由许多要素组成,而日志格式化的主要作用,即是对日志消息进行格式化,组织成自己指定好的字符串结构

总体架构

  • 日志消息(LogMsg)

    • 用于存储各种日志信息,例如存储日志的级别、时间、行号等信息

  • 格式化子项(FormerItem)

    • 用于输出每一条单个的日志消息

    • 该出设计成继承方法,首先实现一个抽象基类,该基类实现所有格式化子项的公共接口;然后子类继承该抽象类,实现其具体化的格式化逻辑,比如输出时间、级别等信息

  • 格式化器(Formatter)

    • 作用:首先根据用户指定的pattern 将其解析成字符串(key:value)数组存储,然后根据解析的结果,生成具体的FormatItem实例对象,然后将这些对象放入到Items中存储,从而实现不同场景下生成不同日志信息的目的。

    • 将解析和打印日志信息放在一起,目的是为了一个接口实现指定日志打印。

  • 该模块架构总结:采用工厂模式,创建具体格式化子项,可以根据用户提供的不同pattern生成不同的格式化子项。借助该模式,实现快速拓展格式化子项,因为只需要在抽象类下添加新的子类即可。

具体实现

格式化子项的定义与实现

  • 基础类:FormatItem
    • 共享指针管理创建的实例对象,父类指针指向子类对象,从而实现调用子类对象成员函数的目的
    • 定义纯虚函数 format,子类就是通过重写该函数,实现不同的格式化日志消息
  • 子类:具体实现不同日志内容
    • 子类重写父类的format函数,然后生成不同的日志信息,比如日志级别、格式化时间等

pattern解析逻辑

pattern解析逻辑

  • 初始状态

    • 初始化一些临时变量,format_key用于存储格式化字符(%d  %p)format_val用于存储子格式字符串(如时间格式),string_row用于存储非格式化字符
    • 使用一个向量arry来存储解析后的结果,也就是存储解析出来格式化指令和其信息(下文事例中key:value)
  • 遍历模式字符串

    • 遍历格式化字符串,逐字符处理
    • 如果遇到普通字符(非%,则将其加入string_row
    • 如果遇到%,需要进一步判断是格式化字符还是转义的%
      • 如果是%%,则将其视作单个%字符,追加存储在string_row中
  • 处理格式化字符

    • 如果遇到格式化字符先将前面的非格式化字符串(如果有)存入arry
    • 读取格式化字符,并检查是否有子格式字符串,检查后面是否存在{}(如时间格式中的{%Y-%m-%d})。
    • 将格式化字符和子格式字符串存入arry(详细理解结合下面表格解析)
  • 处理结束

    • 如果最后还有非格式化字符串未处理,将其存入arry
    • 遍历arry,创建相应的格式化项实例并存入_items向量中

【事例分析】abcd [%d{%H:%M:%S}] [ %p] %T%m%n   ---- 分析出格式化数组存储到arry,然后根据格式化数组构建格式化子项,最终添加到items中

keyval
nullptrtabcd[
d%H:%M:%S
nullptr][
pnullptr
nullptr]
Tnullptr
mnullptr
nnullptr

简单工厂模式应用分析

Formatter类就是一个工厂,其可以根据传入格式化指定的不同,创建不同的格式化子项(也及时FormatItem子类的实例),下面将具体分析实现。

工厂方法:根据解析的格式化字符,创建对象的格式化子项指针

FormatItem::ptr createItem(const std::string &fc, const std::string &subfmt) {if (fc == "m") return FormatItem::ptr(new MsgFormatItem(subfmt));if (fc == "p") return FormatItem::ptr(new LevelFormatItem(subfmt));if (fc == "c") return FormatItem::ptr(new NameFormatItem(subfmt));if (fc == "t") return FormatItem::ptr(new ThreadFormatItem(subfmt));if (fc == "n") return FormatItem::ptr(new NLineFormatItem(subfmt));if (fc == "d") return FormatItem::ptr(new TimeFormatItem(subfmt));if (fc == "f") return FormatItem::ptr(new CFileFormatItem(subfmt));   if (fc == "l") return FormatItem::ptr(new CLineFormatItem(subfmt));if (fc == "T") return FormatItem::ptr(new TabFormatItem(subfmt));return FormatItem::ptr(); 
}

工厂方法调用: 解析逻辑调用,即根据格式化字符解析成格式化子项后,然后将这些格式化子项存入到Items中,从而完成完成最终日志打印格式的目的 

bool parsePattern() {// ...  ...for (auto &it : arry) {if (std::get<2>(it) == 0) {FormatItem::ptr fi(new OtherFormatItem(std::get<0>(it)));_items.push_back(fi);} else {FormatItem::ptr fi = createItem(std::get<0>(it), std::get<1>(it));if (fi.get() == nullptr) {std::cout << "没有对应的格式化字符: %" <<  std::get<0>(it) << std::endl;return false;}_items.push_back(fi);}}// ... ...
}

单元功能测试

简单测试,基本功能验证

#include "formatter.hpp"
#include "message.hpp"
#include "level.hpp"
#include "util.hpp"
#include <iostream>int main() {// 创建一个测试日志消息std::string logger_name = "TestLogger";std::string file_name = __FILE__;std::string log_payload = "This is a test log message.";size_t line_number = __LINE__;bitlog::LogLevel::value log_level = bitlog::LogLevel::value::INFO;  // 正确使用 enum class 枚举值// 初始化 LogMsg 对象bitlog::LogMsg msg(logger_name, file_name, line_number, std::move(log_payload), log_level);// 定义一个格式化模式std::string pattern = "[%d{%Y-%m-%d %H:%M:%S}][%t][%p][%c][%f:%l] %m%n";// 创建 Formatter 对象bitlog::Formatter::ptr formatter(new bitlog::Formatter(pattern));// 格式化并输出到标准输出流std::string formatted_log = formatter->format(msg);std::cout << formatted_log << std::endl;return 0;
}

复杂测试 :多线程环境

#include "formatter.hpp"
#include "message.hpp"
#include "level.hpp"
#include "util.hpp"
#include <iostream>
#include <thread>
#include <vector>void log_test_message(std::string logger_name, bitlog::LogLevel::value log_level, std::string message, std::string pattern) {std::string file_name = __FILE__;size_t line_number = __LINE__;bitlog::LogMsg msg(logger_name, file_name, line_number, std::move(message), log_level);bitlog::Formatter::ptr formatter(new bitlog::Formatter(pattern));std::string formatted_log = formatter->format(msg);std::cout << formatted_log << std::endl;
}int main() {// 不同的格式模式std::vector<std::string> patterns = {"[%d{%Y-%m-%d %H:%M:%S}][%t][%p][%c][%f:%l] %m%n","[%d{%H:%M:%S}][%p] %m (%f:%l)%n","[%t] %m%n"};// 不同的日志级别std::vector<bitlog::LogLevel::value> levels = {bitlog::LogLevel::value::DEBUG,bitlog::LogLevel::value::INFO,bitlog::LogLevel::value::WARN,bitlog::LogLevel::value::ERROR,bitlog::LogLevel::value::FATAL};// 日志消息std::vector<std::string> messages = {"这是一条调试信息。","系统正在启动。","检测到潜在问题。","处理过程中发生错误。","系统故障,需要立即采取措施!"};// 多线程记录日志std::vector<std::thread> threads;for (const auto &pattern : patterns) {for (size_t i = 0; i < levels.size(); ++i) {threads.emplace_back(log_test_message, "MultiThreadLogger", levels[i], messages[i], pattern);}}// 等待所有线程完成for (auto &thread : threads) {thread.join();}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Summernote 富文本编辑器的内容变成只读模式

我 | 在这里 ⭐ 全栈开发攻城狮、全网10W粉丝、2022博客之星后端领域Top1、专家博主。 &#x1f393;擅长 指导毕设 | 论文指导 | 系统开发 | 毕业答辩 | 系统讲解等。已指导60位同学顺利毕业 ✈️个人公众号&#xff1a;热爱技术的小郑。回复 Java全套视频教程 或 前端全套视频…

【安卓】动态加载布局技巧

文章目录 使用限定符常见限定符 使用最小宽度限定符 使用限定符 修改FragmentTest项目中的activity_main.xml文件 <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:orientation"horizontal"android:layout_width&quo…

JavaScript constructor原型原型继承

constructor 在 JavaScript 中&#xff0c;构造函数是一种特殊的函数&#xff0c;使用 new 关键字来调用&#xff0c;用于创建对象实例。JavaScript 中的构造函数通常通过 function 关键字定义。 例如&#xff1a; function Person(name, age) {this.name name;this.age a…

4.MySQL数据类型

目录 数据类型 ​编辑数值类型 tinyint类型 bit类型 float类型 decimal类型 字符串类型 char类型 varchar varchar和char的区别 日期和时间类型 数据类型 数值类型 说明一下&#xff1a;MySQL本身是不支持bool类型的&#xff0c;当把一个数据设置成bool类型时&#x…

C#复习之封装_构造函数,析构函数,垃圾回收

知识点一&#xff1a;构造函数 基本概念 在实例化对象时 会调用的用于初始化的函数 如果不写 默认存在一个无参构造函数 构造函数的写法 1.没有返回值 2.函数名和类名必须相同 3.没有特殊需求时 一般都是public的 4.构造函数可以被重载 5.this代表当前调用该函数的对象自己 注…

【多线程】synchronized原理

文章目录 一、锁升级 (面试经常考)偏向锁 二、锁消除三、锁粗化锁的粒度 四、相关面试题 结合 锁策略&#xff0c;我们就可以总结出&#xff0c;synchronized具有以下特性&#xff1a; 乐观悲观&#xff0c;自适应重量轻量&#xff0c;自适应自旋挂起等待&#xff0c;自适应非…

Gradio 快速开发网页应用

Gradio 是一个开源的 Python 框架&#xff0c;可以快速开发页面&#xff0c;Gradio 主要用于 AI 模型 Demo 的开发&#xff0c;通过几行代码可以快速生成一个 Web Demo&#xff0c;由于 AI 算法工程师使用的都是 Python 语言&#xff0c;使用 Python 开发 Demo 会相对简单&…

演示:基于WPF的DrawingVisual开发GS(2019)1822号矢量中国地图一

一、目的&#xff1a;基于WPF的DrawingVisual开发的矢量地图 二、预览 默认样式 深黑样式 深蓝色样式 深蓝色透明样式 演示&#xff1a;基于WPF的DrawingVisual开发GS(2019)1822号矢量中国地图二-CSDN博客VS2022&#xff0c;net7演示&#xff1a;基于WPF的DrawingVisual开发GS…

DVWA—SQL(Blind)实例

DVWA—SQL Injection&#xff08;Blind&#xff09;实例 预备知识 在SQL注入中会有回显注入和盲注入的形式&#xff0c;在前面的文章中展示了回显注入的情况&#xff0c;而盲注入就是当我输入查询语句的时候数据并不会直接显示在页面中而是以程序员规定的输出格式来进行显示&…

【数据结构和算法】(基础篇二)——链表

链表 数组最麻烦的地方就是其在建立之后大小固定&#xff0c;对于增删数据很不方便。链表的出现解决了这个问题&#xff0c;链表的元素不是在内存中连续存储的&#xff0c;而是通过指针链接在一起的节点集合&#xff0c;这样的设计让链表有了动态的大小。链表是树和图结构的基…

Windows11 WSL2 Ubuntu编译安装perf工具

在Windows 11上通过WSL2安装并编译perf工具&#xff08;Linux性能分析工具&#xff09;可以按以下步骤进行。perf工具通常与Linux内核一起发布&#xff0c;因此你需要确保你的内核版本和perf版本匹配。以下是安装和编译perf的步骤&#xff1a; 1. 更新并升级系统 首先&#x…

Unity数据持久化 之 Json序列化与反序列化

语法规则可以看这篇文章&#xff1a;Unity数据持久化 之 Json 语法速通-CSDN博客 Q:Unity是通过什么来对Json文件进行处理的&#xff1f; A:JsonUtility&#xff1a;Unity 提供了 JsonUtility 类&#xff0c;用于将对象序列化为 JSON 字符串或将 JSON 字符串反序列化为对象。…

大数据-72 Kafka 高级特性 稳定性-事务 (概念多枯燥) 定义、概览、组、协调器、流程、中止、失败

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

DC-5靶机渗透测试

DC-5靶场 文章目录 DC-5靶场信息收集漏洞发现漏洞利用 --- 日志文件包含漏洞利用 --- 文件包含过滤器链的RCEshell反弹权限提升 信息收集 使用--scriptvuln扫描发现了一个thankyou.php界面 感觉会有问题&#xff0c;前往访问网站信息 漏洞发现 来到thankyou.php界面&#xff…

haproxy详解

目录 一、haproxy简介 二、什么是负载均衡 2.1 负载均衡的类型 2.2.1 硬件 2.2.2 四层负载均衡 2.2.3 七层负载均衡 2.2.4 四层和七层的区别 三、haproxy的安装及服务信息 3.1 示例的环境部署&#xff1a; 3.2 haproxy的基本配置信息 3.2.1 global 配置参数介绍 3…

sleuth+zipkin分布式链路追踪

概述 结构图 常见的链路追踪 cat zipkin pinpoint skywalking sleuth Sleuth介绍 Trace Span Annotation 使用Sleuth 添加依赖 <!--sleuth--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starte…

运维工具的衍化对运维工作的新挑战

运维工具的衍化对运维工作产生了深远的影响&#xff0c;这些影响体现在多个方面&#xff0c;包括提升运维效率、优化资源配置、增强故障应对能力、促进团队协作与沟通&#xff0c;以及面临新的挑战如数据安全和隐私保护等。运维工具的衍化对运维工作带来了多方面的新挑战&#…

Yolo-World初步使用

Yolo v8目前已经支持Yolo-World&#xff0c;整理一下初步使用步骤。 使用步骤 1 先下载Yolo-World的pt文件&#xff0c;下载地址&#xff1a;GitHub - AILab-CVC/YOLO-World: [CVPR 2024] Real-Time Open-Vocabulary Object Detection 官网应该是点这里&#xff08;有个笑脸…

【C#】读取与写入txt文件内容

在 C# 中读取和写入文本文件内容是一个常见的任务。以下是使用几种不同方法读取和写入文本文件的示例。 一、读取txt文件内容 1.1 使用 StreamReader using System; using System.IO;class Program {static void Main(){string filePath "C:\path\to\your\file.txt&qu…

QT多语言工具实现支持生成ts文件,ts文件和xlsx文件互转

一. 工具介绍 1.如果你是Qt项目,为多语言发愁的话,看到这篇文件,恭喜你有福啦!工具截图如下:​ 2.在项目开发的过程中,尽量将所有需要翻译的文本放在一个文件中,qml翻译用一个文件,cpp用一个,如下: test.h #pragma once /******************************************…