C++基于多设计模式下的同步异步日志系统day4

📟作者主页:慢热的陕西人

🌴专栏链接:C++基于多设计模式下的同步&异步日志系统

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

只要内容主要实现了同步日志消息的建造者模式的实现

在这里插入图片描述

文章目录

  • C++基于多设计模式下的同步&异步日志系统day4
    • 1.⽇志器类(Logger)设计(建造者模式)

C++基于多设计模式下的同步&异步日志系统day4

1.⽇志器类(Logger)设计(建造者模式)

⽇志器主要是⽤来和前端交互,当我们需要使⽤⽇志系统打印log的时候,只需要创建Logger对象,调⽤该对象debug、info、warn、error、fatal等⽅法输出⾃⼰想打印的⽇志即可,⽀持解析可变参数列表和输出格式,即可以做到像使⽤printf函数⼀样打印⽇志。
当前⽇志系统⽀持同步⽇志&异步⽇志两种模式,两个不同的⽇志器唯⼀不同的地⽅在于他们在⽇志的落地⽅式上有所不同:

  • 同步⽇志器:直接对⽇志消息进⾏输出。
  • 异步⽇志器:将⽇志消息放⼊缓冲区,由异步线程进⾏输出。

因此⽇志器类在设计的时候先设计出⼀个Logger基类,在Logger基类的基础上,继承出SyncLogger同步⽇志器和AsyncLogger异步⽇志器。且因为⽇志器模块是对前边多个模块的整合,想要创建⼀个⽇志器,需要设置⽇志器名称,设置⽇志输出等级,设置⽇志器类型,设置⽇志输出格式,设置落地⽅向,且落地⽅向有可能存在多个,整个⽇志器的创建过程较为复杂,为了保持良好的代码⻛格,编写出优雅的代码,因此⽇志器的创建这⾥采⽤了建造者模式来进⾏创建。

/*完成日志器模块:1.抽象日志器基类2.派生出不同的子类(同步日志器类 & 异步日志器类)*/
#ifndef __M_LOGER_H__
#define __M_LOGER_H__#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif#include"format.hpp"
#include"level.hpp"
#include"message.hpp"
#include"sink.hpp"
#include"util.hpp"#include<atomic>
#include<stdio.h>
#include<mutex>
#include<stdarg.h>
#include<cassert>namespace xupt
{class Logger{public:using ptr = std::shared_ptr<Logger>;Logger(const std::string& logger_name,LogLevel::value level,Formatter::ptr &formatter,std::vector<LogSink::ptr> sinks):_logger_name(logger_name),_limit_level(level),_formatter(formatter),_sinks(sinks.begin(), sinks.end()){}/*完成日志构造消息,并进行格式化,得到格式化后的日志消息字符串---然后进行落地输出*/void debug(const std::string &file, size_t line, const std::string &fmt, ...){//1.判断当前的日志是否到达了输出等级if(LogLevel::value::DEBUG < _limit_level) { return ; }va_list ap;va_start(ap, fmt);char* res;int ret = vasprintf(&res, fmt.c_str(), ap);if(ret == -1){std::cout << "vasprintf failed!\n";return ;}va_end(ap); //将ap置空serialize(LogLevel::value::DEBUG, file, line, res);free(res);}void info(const std::string &file, size_t line, const std::string &fmt, ...){//1.判断当前的日志是否到达了输出等级if(LogLevel::value::INFO < _limit_level) { return ; }va_list ap;va_start(ap, fmt);char* res;int ret = vasprintf(&res, fmt.c_str(), ap);if(ret == -1){std::cout << "vasprintf failed!\n";return ;}va_end(ap); //将ap置空serialize(LogLevel::value::INFO, file, line, res);free(res);}        void warn(const std::string &file, size_t line, const std::string &fmt, ...){//1.判断当前的日志是否到达了输出等级if(LogLevel::value::WARN < _limit_level) { return ; }va_list ap;va_start(ap, fmt);char* res;int ret = vasprintf(&res, fmt.c_str(), ap);if(ret == -1){std::cout << "vasprintf failed!\n";return ;}va_end(ap); //将ap置空serialize(LogLevel::value::WARN, file, line, res);free(res);}             void error(const std::string &file, size_t line, const std::string &fmt, ...){//1.判断当前的日志是否到达了输出等级if(LogLevel::value::ERROR < _limit_level) { return ; }va_list ap;va_start(ap, fmt);char* res;int ret = vasprintf(&res, fmt.c_str(), ap);if(ret == -1){std::cout << "vasprintf failed!\n";return ;}va_end(ap); //将ap置空serialize(LogLevel::value::ERROR, file, line, res);free(res);}             void fatal(const std::string &file, size_t line, const std::string &fmt, ...){//1.判断当前的日志是否到达了输出等级if(LogLevel::value::FATAL < _limit_level) { return ; }va_list ap;va_start(ap, fmt);char* res;int ret = vasprintf(&res, fmt.c_str(), ap);if(ret == -1){std::cout << "vasprintf failed!\n";return ;}va_end(ap); //将ap置空serialize(LogLevel::value::FATAL, file, line, res);free(res);}     protected:void serialize(LogLevel::value level, const std::string &file, size_t line, char* str){//2.构造LogMsg对象LogMsg msg(level, line, file, _logger_name, str);//3.通过格式化工具对LogMsg进行格式化,得到格式化后的日志字符串std::stringstream ss;_formatter->format(ss, msg);//5.进行日志落地log(ss.str().c_str(), ss.str().size());}/*抽象接口完成实际的落地输出--不同的日志器会有不同的实际落地方式*/virtual void log(const char *data, size_t len) = 0;protected:std::mutex _mutex;                         // 保证过程的线程安全std::string _logger_name;                  // 日志器名称std::atomic<LogLevel::value> _limit_level; // 日志等级Formatter::ptr _formatter;                 // 格式化std::vector<LogSink::ptr> _sinks;          // 用一个数组来存放日志落地位置};//同步日志器class SyncLogger : public Logger{public:SyncLogger(const std::string& logger_name,LogLevel::value level,Formatter::ptr &formatter,std::vector<LogSink::ptr> sinks):Logger(logger_name, level, formatter, sinks){}protected:virtual void log(const char *data, size_t len){std::unique_lock<std::mutex> lock(_mutex);if(_sinks.empty()) return;for(auto &sink : _sinks){sink->log(data,len);}}};enum class LoggerType{LOGGER_SYNC,LOGGER_ASYNC};/*使用建造者模式来建造日志器,而不用用户直接去构造日志器,减少用户的使用复杂度*///1.抽象一个日志器建造者类(完成日志器对象所需零部件的构造 & 日志器的构建)//  1.设置日志器类型//  2.将不同类型日志器的创建放到同一个日志器建造类中完成class LoggerBuilder{public:LoggerBuilder():_logger_type(LoggerType::LOGGER_SYNC),_limit_level(LogLevel::value::DEBUG){}void buildLoggerType(LoggerType type) { _logger_type = type; }void buildLoggerName(const std::string & name) { _logger_name = name; }void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }void buildFomatter(const std::string &pattern) { _formatter = std::make_shared<Formatter>(pattern); }template<typename SinkType, typename ...Args>void buildSink(Args &&... args){LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);_sinks.push_back(psink);}virtual Logger::ptr build() = 0;protected:LoggerType _logger_type;std::string _logger_name;                  // 日志器名称std::atomic<LogLevel::value> _limit_level; // 日志等级Formatter::ptr _formatter;                 // 格式化std::vector<LogSink::ptr> _sinks;          // 用一个数组来存放日志落地位置};//2.派生出具体的建造者类---局部的日志器建造者&全局的日志器建造者(后边添加了全局单例管理器之后,将日志器添加全局管理)class LocalLoggerBuilder : public LoggerBuilder{public:Logger::ptr build() override{assert(_logger_name.empty() == false); //必须有日志器名称if(_formatter.get() == nullptr){_formatter = std::make_shared<Formatter>();}if(_sinks.empty()){buildSink<StdoutSink>();}if(_logger_type == LoggerType::LOGGER_ASYNC){//返回异步日志器...}//返回同步日志器return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);}};  }#endif

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

Kubernetes的Sevice管理

服务原理: 所有服务都是根据这个服务衍生或者变化出来,根服务---- 服务感知后端靠标签 slelector 标签选择器 kubectl label pods web1 appweb kubectl cluter-info dump | grep -i service-cluster-ip-range 服务ip取值范围 Service 管理: 创建服务: --- kind: Serv…

React富文本编辑器开发(六)

现在&#xff0c;相关的基础知识我们应该有个大概的了解了&#xff0c;但离我们真正的开发出一个实用型的组件还有一段距离&#xff0c;不过不用担心&#xff0c;我们离目标已经越来越近。 以现在我们所了解的内容而言&#xff0c;或许你发现了一个问题&#xff0c;就是我们的编…

CentOS配网报错:network is unreachable

常用命令&#xff1a; 打开&#xff1a; cd /etc/sysconfig/network-scripts/ 修改&#xff1a; vim ifcfg-ens33 打开修改&#xff1a; vim /etc/sysconfig/network-scripts/ifcfg-ens33 保存&#xff1a; 方法1&#xff1a;ESCZZ&#xff08;Z要大写&#xff09; 方…

LabelImg官方文档摘录

LabelImg官方文档&#xff1a;https://github.com/HumanSignal/labelImg 注释&#xff08;annotation&#xff09;以 PASCAL VOC 格式保存为 XML 文件&#xff0c;这是ImageNet使用的格式。此外&#xff0c;它还支持 YOLO 和 CreateML 格式。 安装 使用CSDN博主打包的程序&a…

Linux:地址空间的转换以及线程的理解和使用

文章目录 线程的理解地址空间的转换问题总结 线程的优点线程的缺点线程的健壮性问题 本篇主要进行对于进程和线程的理解&#xff0c;以及对于线程的一部分使用方法和使用的原理 线程的理解 首先回顾前面一篇的内容中&#xff0c;对于进程的基本认识&#xff1a; 什么是线程&…

OWASP TOP 10解析:构建坚不可摧的Web应用安全防线

当涉及到Web应用程序安全的话题时&#xff0c;OWASP&#xff08;开放式Web应用程序安全项目&#xff09;的TOP 10是一个不可忽视的参考点。OWASP TOP 10列举了当前Web应用程序中最严重的安全风险&#xff0c;帮助开发人员、测试人员和安全专业人员更好地理解并针对这些风险采取…

【LeetCode:2368. 受限条件下可到达节点的数目 + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Mybatis实战(1)

mybatis-pageHelper 1&#xff0c;添加依赖&#xff1a; <dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency><!--pag…

SpringBoot-yaml语法

1.概念 在Springboot的项目中&#xff0c;配置文件有以下几种格式&#xff1a; Application.propertiesApplication.yamlApplication.yml 其中官方推荐我们使用yaml的格式(因为能表示的数据类型很多样) 2.基本语法 # yaml形式的配置文件# 普通的key-value&#xff08;分号之后…

用numpy搭建自己的神经网络

搭建之前的基础与思考 构建模型的基本思想&#xff1a; 构建深度学习的过程&#xff1a;产生idea&#xff0c;将idea转化成code&#xff0c;最后进行experiment&#xff0c;之后根据结果修改idea&#xff0c;继续idea–>code–>experiment的循环&#xff0c;直到最终训练…

matplotlib条形图

matplotlib条形图 假设你获取到了2017年内地电影票房前20的电影(列表a)和电影票房数据(列表b), 那么如何更加直观的展示该数据? from matplotlib import pyplot as plta ["Wolf Warrior 2", "Fast and Furious 8", "Kung Fu Yoga", "Jo…

【LiveData】LiveData转换及操作符分析

使用示例 LiveData操作符可以将一个LiveData转换为另一个LiveData 当源LiveData发生变更时&#xff0c;会自动通知目标LiveData val srcLiveData : LiveData<T>val dstLiveData : LiveData<R>dstLiveData srcLiveData.distinctUntilChanged().switchMap{returnsw…

线性表——单链表的增删查改

本节复习链表的增删查改 首先&#xff0c; 链表不是连续的&#xff0c; 而是通过指针联系起来的。 如图&#xff1a; 这四个节点不是连续的内存空间&#xff0c; 但是彼此之间使用了一个指针来连接。 这就是链表。 现在我们来实现链表的增删查改。 目录 单链表的全部接口…

位运算---求n的二进制表示中第k位是1还是0 (lowbit)

操作&#xff1a; 先把第k位移到最后一位&#xff08;右边第一位&#xff09; 看个位是1还是0 lowbit(x)&#xff1a;返回x的最右边的1。 原理&#xff1a; 其中 &#xff0c;意思是 是 的补码。 就可以求出最右边的一位1。 应用&#xff1a; 当中 的个数。 int re…

AI-数学-高中-33概率-事件的关系与运算

原作者视频&#xff1a;【概率】【一数辞典】2事件的关系与运算_哔哩哔哩_bilibili 事件&#xff1a; 和/并事件&#xff1b;积/交事件&#xff1b;互诉事件&#xff1b;对立(补集)事件&#xff1b;

【详识JAVA语言】面向对象程序三大特性之二:继承

继承 为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序是就需要考虑。 比如&…

04.其他方案

其他方案 1.事务状态表调⽤⽅重试接收⽅幂等 介绍 调⽤⽅维护⼀张事务状态表&#xff08;或者说事务⽇志、⽇志流⽔&#xff09;&#xff0c;在每次调⽤之前&#xff0c;落盘⼀条事务流⽔&#xff0c;⽣成⼀个全局的事务ID 事务开始之前的状态是Begin&#xff0c;全部结束之…

Go语言进阶篇——文件

文件的打开 文件的常见的两种打开方式是基于os包所提供的两个函数: func Open(name string) (*File,error) func OpenFile(name string flag int perm FileMode) (*File,error)相对于前者&#xff0c;OpenFile可以提供更加细致的操作&#xff0c;而前者就是对后者的一个简单封…

码垛工作站:食品生产企业的转型助推器

在当今高度自动化的工业生产中&#xff0c;码垛工作站的应用正逐渐成为一种趋势。某食品生产企业在面临市场竞争加剧、人工成本上升等多重压力下&#xff0c;决定引入码垛工作站&#xff0c;以期实现生产流程的升级与变革。 一、码垛工作站引入背景 该企业主要从事休闲食品的…

Android 中的 LinearLayout 布局

在 Android 开发中&#xff0c;布局是至关重要的一部分&#xff0c;它决定了应用程序的界面结构和用户体验。LinearLayout 是 Android 中最常用的布局之一&#xff0c;它以线性方式排列子视图&#xff0c;可以垂直或水平布局。在这篇博客中&#xff0c;我们将深入了解 LinearLa…