[C++]——同步异步日志系统(7)

同步异步日志系统

  • 一、日志器管理模块(单例模式)
    • 1.1 对日志器管理器进行设计
    • 1.2 实现日志器管理类的各个功能
    • 1.3. 设计一个全局的日志器建造者
    • 1.4 测试日志器管理器的接口和全局建造者类
  • 二、宏函数和全局接口设计
    • 2.1 新建一个.h,文件,文件里面放我们写的.hpp(各个模块文件)
    • 2.2 对宏函数与全局接口进行功能测试

一、日志器管理模块(单例模式)

⽇志的输出,我们希望能够在任意位置都可以进⾏,但是当我们创建了⼀个⽇志器之后,就会受到⽇志器所在作⽤域的访问区域限制。 因此,为了突破访问区域的限制,我们创建⼀个⽇志器管理类,且这个类是⼀个单例类,这样的话, 我们就可以在任意位置来通过管理器单例获取到指定的⽇志器来进⾏⽇志输出了。

日志器管理器:
作用1:对所有创建的日志器进行管理
特性:将管理器设计为单例
作用2:可以在程序的任意位置,获取相同的单例对象,获取其中的日志器进行日志输出
拓展:单例管理器创建的时候,默认先创建一个日志器(用于进行标准输出的打印)
目的:让用户在不创建任何日志器的情况下,也能进行标准输出的打印,方便用户使用

设计:
管理的成员:

1.默认日志器
2.所管理的日志器数组
3.互斥锁

提供的接口:

1添加日志器管理
2.判断是否管理了指定名称的日志器
3.获取指定名称的日志器
4.获取默认日志器

1.1 对日志器管理器进行设计

    // 日志器管理模块
    class LoggerManager
    {
    public:
        // 1添加日志器管理
        void addLogger(Logger::ptr &logger);
        // 2.判断是否管理了指定名称的日志器
        bool hasLogger(const std::string &name);
        // 3.获取指定名称的日志器
        Logger::ptr getLogger(const std::string &name);
        // 4.获取默认日志器
        Logger::ptr rootLogger();
        // 5. 获取单例句柄
       static LoggerManager &getInstance();

    private:
        // 构造函数私有化
        LoggerManager() {}

    private:
        // 1.默认日志器
        Logger::ptr _root_logger;
        // 2.所管理的日志器数组
        std::vector<Logger::ptr> _loggers;
        // 3.互斥锁
        std::mutex _mutex;
    };

1.2 实现日志器管理类的各个功能

    // 日志器管理模块
    class LoggerManager
    {
    public:
        // 1添加日志器管理
        void addLogger(Logger::ptr &logger)
        {
            // 如果已经有了日志器,就不需要再添加
            if (hasLogger(logger->name()))
                return;
            std::unique_lock<std::mutex> lock(_mutex); // 添加日志器之前加锁
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        // 2.判断是否管理了指定名称的日志器
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex); // 判断之前加锁
            auto it = _loggers.find(name);             // 查找日志器
            if (it == _loggers.end())
            {
                // 代表没找到
                return false;
            }
            return true;
        }
        // 3.获取指定名称的日志器
        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex); // 获取之前加锁
            auto it = _loggers.find(name);             // 查找日志器
            if (it == _loggers.end())
            {
                // 代表没找到,返回一个空的智能指针
                return Logger::ptr();
            }
            return it->second; // 日志器所对应的值
        }
        // 4.获取默认日志器
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }
        // 5. 获取单例句柄
        static LoggerManager &getInstance()
        {
            // 在c++11之后,针对静态局部变量,编译器在编译的层面上实现了线程安全
            // 当静态局部变量在没有构造完成之前,其他的线程进入就会阻塞
            static LoggerManager eton;
            return eton;
        }

    private:
        // 构造函数私有化
        LoggerManager()
        {
            // 构造一个日志器建造者
            std::unique_ptr<logslearn::LoggerBuilder> builder(new logslearn::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build(); // 建造者构建对象,没有建造的就用默认对象
            // 把默认构造的日志器管理起来
            _loggers.insert(std::make_pair("root", _root_logger));
        }

    private:
        // 1.默认日志器
        Logger::ptr _root_logger;
        // 2.所管理的日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
        // 3.互斥锁
        std::mutex _mutex;
    };

1.3. 设计一个全局的日志器建造者

在局部的日志器建造者上增加一个功能:将日志器添加到单例对象中

    //设计一个全局的日志器建造者-在局部的日志器建造者上增加一个功能:将日志器添加到单例对象中
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            // 必须要有日志器名称
            assert(_logger_name.empty() == false);
            // 必须要有formatter//必须要有格式化器,没有就要创建
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 如果没有落地方式就给它添加一个标准输出的默认落地方式
            if (_sliks.empty())
            {
                buildSink<StdoutSink>();
            }
            //默认日志器
            Logger::ptr logger;
             // 如果类型为LOGGER_ASYNC,那么日志器为异步日志器
            if (logger_type == LoggerType::LOGGER_ASYNC)
            {
                // 返回异步日志器对象
                logger=std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sliks, _looper_type);
            }else{
                // 返回同步日志器的对象
                logger=std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sliks); // r日志器名字,等级,格式化,落地方式
            }
            
            //把日志器添加到日志器管理器中
            LoggerManager::getInstance().addLogger(logger);
            // 返回同步日志器的对象
            return logger;
        }
    };

1.4 测试日志器管理器的接口和全局建造者类

在main函数里创建日志器,在普通函数里写日志。

// 测试代码
#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "logger.hpp"
#include <unistd.h>
#include "buffer.hpp"
#include "looper.hpp"
#include <fstream>

void test_log(){
    //日志器管理器
    logslearn::Logger::ptr logger=logslearn::LoggerManager::getInstance().getLogger("async_logger");
    //测试日志打印
    logger->debug(__FILE__, __LINE__, "%s", "测试日志");
    logger->info(__FILE__, __LINE__, "%s", "测试日志");
    logger->warn(__FILE__, __LINE__, "%s", "测试日志");
    logger->error(__FILE__, __LINE__, "%s", "测试日志");
    logger->fatal(__FILE__, __LINE__, "%s", "测试日志");
    size_t count = 0;
    while (count < 100000)
    {
        logger->fatal(__FILE__, __LINE__, "测试日志-%d", count++);
    }


}
int main()
{  
    //测试日志器管理模块
    //先要构造一个建造者出来
    //全局建造者构造日志器
    std::unique_ptr<logslearn::LoggerBuilder> builder(new logslearn::GlobalLoggerBuilder());
    //建造者构建零部件
    builder->buildLoggerName("async_logger");
    builder->buildLoggerLevel(logslearn::loglevel::value::WARN);
    builder->buildLoggerFormatter("[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n");
    builder->buildLoggerType(logslearn::LoggerType::LOGGER_ASYNC);
    builder->buildEnabeUnSafeAsync();//切换模式
    builder->buildSink<logslearn::StdoutSink>();                                 // 标准输出落地
    builder->buildSink<logslearn::FileSink>("./logfile/async.log"); // 文件落地方式
   
    builder->build();
    // builder->buildSink<logslearn::RoolBySizeSink>("./logfile/roll-", 1024 * 1024); // 滚动文件落地方式
    test_log();

    return 0;
}

二、宏函数和全局接口设计

提供全局接口&宏函数, 对日志系统接口,进行使用便捷性优化

思想:

1.提供获取指定日志器的全局接口(避免用户自己操作单例对象)
2.使用宏函数对日志器的接口进行代理(代理模式)
3.提供宏函数,直接通过默认日志器进行日志的标准输出打印(不要获取日志器了)

2.1 新建一个.h,文件,文件里面放我们写的.hpp(各个模块文件)

方便外界使用者进行调用

#ifndef __M_LOGSLEARN_H__
#define __M_LOGSLEARN_H__
#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "logger.hpp"
#include <unistd.h>
#include "buffer.hpp"
#include "looper.hpp"
#include <fstream>
namespace logslearn
{
    //  1.提供获取指定日志器的全局接口(避免用户自己操作单例对象)
    Logger::ptr getLogger(const std::string &name) // 指定日志器
    {
        return logslearn::LoggerManager::getInstance().getLogger(name);
    }
    Logger::ptr rootLogger() // 默认日志器
    {
        return logslearn::LoggerManager::getInstance().rootLogger();
    }
//  2.使用宏函数对日志器的接口进行代理(代理模式)
#define debug(fmt, ...) debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define info(fmt, ...) info(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define warn(fmt, ...) warn(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define error(fmt, ...) error(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define fatal(fmt, ...) fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
//  3.提供宏函数,直接通过默认日志器进行日志的标准输出打印(不要获取日志器了)
// 方法1
// #define DEBUG(logger,fmt,...) logger->denug(fmt,##__VA_ARGS__)
// #define DLOG(fmt,...)  DEBUG(rootLogger(),fmt,##__VA_ARGS__)   //变成_root_logger->debug(fmt,...)
// 方法2
#define DEBUG(fmt, ...) logslearn::rootLogger()->debug(fmt, ##__VA_ARGS__)
#define INFO(fmt, ...) logslearn::rootLogger()->info(fmt, ##__VA_ARGS__)
#define WARN(fmt, ...) logslearn::rootLogger()->warn(fmt, ##__VA_ARGS__)
#define ERROR(fmt, ...) logslearn::rootLogger()->error(fmt, ##__VA_ARGS__)
#define FATAL(fmt, ...) logslearn::rootLogger()->fatal(fmt, ##__VA_ARGS__)

}

#endif

通过两套宏函数,简化了用户对日志的输出工作

2.2 对宏函数与全局接口进行功能测试

测试一:测试第一组宏是否正确
输出打印的结果符合我们的预期
在这里插入图片描述
测试二:对宏函数与全局接口进行测试

可以自定义格式按要求进行输出,需要自己去创建日志器(这里是吧日志打印到文件和屏幕两种落地方法)

// 测试代码
#include "logslearn.h"
void test_log(){
   // //日志器管理器
    logslearn::Logger::ptr logger=logslearn::LoggerManager::getInstance().getLogger("async_logger");
    //测试日志打印
    logger->debug( "%s", "测试日志");
    logger->info( "%s", "测试日志");
    logger->warn( "%s", "测试日志");
    logger->error( "%s", "测试日志");
    logger->fatal("%s", "测试日志");
    size_t count = 0;
    while (count < 100000)
    {
        logger->fatal( "测试日志-%d", count++);
    }
}
int main()
{
    
    //全局建造者构造日志器
    std::unique_ptr<logslearn::LoggerBuilder> builder(new logslearn::GlobalLoggerBuilder());
    //建造者构建零部件
    builder->buildLoggerName("async_logger");
    builder->buildLoggerLevel(logslearn::loglevel::value::WARN);
    builder->buildLoggerFormatter("[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n");
    builder->buildLoggerType(logslearn::LoggerType::LOGGER_ASYNC);
    builder->buildEnabeUnSafeAsync();//切换模式
    builder->buildSink<logslearn::StdoutSink>();                                 // 标准输出落地
    builder->buildSink<logslearn::FileSink>("./logfile/async.log"); // 文件落地方式
   
    builder->build();
    // builder->buildSink<logslearn::RoolBySizeSink>("./logfile/roll-", 1024 * 1024); // 滚动文件落地方式
    test_log();

    return 0;
}

在这里插入图片描述

日志器是以默认的格式向屏幕输出

// 测试代码
#include "logslearn.h"
void test_log(){
    //测试日志打印
    DEBUG("%s", "测试日志");//注意,宏替换过后命名空间就没了
    INFO("%s", "测试日志");
    WARN("%s", "测试日志");
    ERROR("%s", "测试日志");
    FATAL("%s", "测试日志");
    size_t count = 0;
    while (count < 100000)
    {
        FATAL("测试日志-%d", count++);
    }
}
int main()
{
    test_log();
    return 0;
}

在这里插入图片描述

经过10来天的奋战,终于吧同步异步日志系统写完了。

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

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

相关文章

小欧吃苹果-OPPO 2024届校招正式批笔试题-数据开发(C卷)

在处理这个问题前&#xff0c;先看一个经典的贪心算法题目。信息学奥赛一本通&#xff08;C版&#xff09;在线评测系统http://ybt.ssoier.cn:8088/problem_show.php?pid1320 注意移动纸牌的贪心策略并不是题目中给出的移动次序&#xff1a;第1堆纸牌9<10&#xff0c;因为是…

几何相关计算

目录 一、 判断两个矩形是否相交 二、判断两条线段是否相交 三、判断点是否在多边形内 四、垂足计算 五、贝塞尔曲线 六、坐标系 一、 判断两个矩形是否相交 当矩形1的最大值比矩形2的最小值都小&#xff0c;那矩形1和矩形2一定不相交&#xff0c;其他同理。 struct Po…

【STM32】按键控制LED光敏传感器控制蜂鸣器(江科大)

一、按键控制LED LED.c #include "stm32f10x.h" // Device header/*** 函 数&#xff1a;LED初始化* 参 数&#xff1a;无* 返 回 值&#xff1a;无*/ void LED_Init(void) {/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENAB…

醇香之旅:探索红酒的无穷魅力

在浩渺的饮品世界里&#xff0c;红酒如同一颗璀璨的星辰&#xff0c;闪烁着诱人的光芒。它以其不同的醇香和深邃的韵味&#xff0c;吸引着无数人的目光。今天&#xff0c;就让我们一起踏上这场醇香之旅&#xff0c;探索雷盛红酒所带来的无穷魅力。 一、初识红酒的醇香 当我们…

去除重复字母

题目链接 去除重复字母 题目描述 注意点 s 由小写英文字母组成1 < s.length < 10^4需保证 返回结果的字典序最小&#xff08;要求不能打乱其他字符的相对位置&#xff09; 解答思路 本题与移掉 K 位数字类似&#xff0c;需要注意的是&#xff0c;并不是每个字母都能…

张量分解(4)——SVD奇异值分解

&#x1f345; 写在前面 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;这里是hyk写算法了吗&#xff0c;一枚致力于学习算法和人工智能领域的小菜鸟。 &#x1f50e;个人主页&#xff1a;主页链接&#xff08;欢迎各位大佬光临指导&#xff09; ⭐️近…

01 机器学习概述

目录 1. 基本概念 2. 机器学习三要素 3. 参数估计的四个方法 3.1 经验风险最小化 3.2 结构风险最小化 3.3 最大似然估计 3.4 最大后验估计 4. 偏差-方差分解 5. 机器学习算法的类型 6. 数据的特征表示 7. 评价指标 1. 基本概念 机器学习&#xff08;Machine Le…

【python】PyQt5的窗口界面的各种交互逻辑实现,轻松掌控图形化界面程序

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

C# modbus 图表

控件&#xff1a;chart1(图表)&#xff0c;cartesianChart1(第三方添加图表)&#xff0c;timer(时间) 添加第三方&#xff1a; 效果&#xff1a;图标会根据连接的温度&#xff0c;湿度用timer时间进行改变 Chart1控件样式&#xff1a;Series添加线条&#xff0c;颜色&#xf…

【算法】LRU缓存

难度&#xff1a;中等 题目&#xff1a; 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;…

2024牛客暑期多校训练营1 A题 解题思路

前言&#xff1a; 今年和队友报了牛客暑期多校比赛&#xff0c;写了一下午结果除了签到题之外只写出了一道题&#xff08;A&#xff09;&#xff0c;签到题没什么好说的&#xff0c;其他题我也没什么好说的&#xff08;太菜了&#xff0c;根本写不出来&#xff09;&#xff0c;…

SAP ABAP性能优化

1.前言 ABAP作为SAP的专用的开发语言&#xff0c;衡量其性能的指标主要有以下两个方面&#xff1a; 响应时间&#xff1a;对于某项特定的业务请求&#xff0c;系统在收到请求后需要多久返回结果 吞吐量&#xff1a;在给定的时间能&#xff0c;系统能够处理的数据量 2. ABAP语…

FFMPEG录屏入门指南【转载】

文章非原创&#xff0c;为防失联而转载&#xff1a;【原创】FFMPEG录屏入门指南 - 博客园 (cnblogs.com) 【原创】FFMPEG录屏入门指南 最近部门内部在做技术分享交流&#xff0c;需要将内容录制成视频存档。很自然的想到了去网上找一些录屏的软件&#xff0c;试过了几款诸如屏幕…

昇思25天学习打卡营第13天|CycleGAN 图像风格迁移互换全流程解析

目录 数据集下载和加载 可视化 构建生成器 构建判别器 优化器和损失函数 前向计算 计算梯度和反向传播 模型训练 模型推理 数据集下载和加载 使用 download 接口下载数据集&#xff0c;并将下载后的数据集自动解压到当前目录下。数据下载之前需要使用 pip install dow…

LabVIEW设备检修信息管理系统

开发了基于LabVIEW设计平台开发的设备检修信息管理系统。该系统应用于各种设备的检修基地&#xff0c;通过与基地管理信息系统的连接和数据交换&#xff0c;实现了本地检修工位数据的远程自动化管理&#xff0c;提高了设备的检修效率和安全性。 项目背景 现代设备运维过程中信…

QT小细节

QT小细节 1 QTextToSpeech1.1 cmake1.2 qmake QT6 6.7.2 1 QTextToSpeech 从下图可以看到&#xff0c;分别使用qmake或者cmake编译情况下的&#xff0c;QTextToSpeech的使用方法 QTextToSpeech官方链接&#xff0c;也可以直接在QT Creator的帮助中搜索 1.1 cmake 将上图中的…

jmeter之变量随机参数化以及解决多线程不会随机变化

参考链接&#xff1a; https://www.cnblogs.com/Testing1105/p/12743475.html jmeter 使用random函数多线程运行时数据不会随机变化&#xff1f;_jmeter 线程组循环执行时 变量不变-CSDN博客 1、如下图所示&#xff0c;需要对请求参数 autor 和phone进行随机参数化 2、目前有…

FullCalendar日历组件集成实战(20)

背景 有一些应用系统或应用功能&#xff0c;如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件&#xff0c;但功能比较简单&#xff0c;用来做数据展现勉强可用。但如果需要进行复杂的数据展示&#xff0c;以及互动操作如通过点击添加事件&#xff0…

【Java--数据结构】二叉树

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 树结构 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合 注意&#xff1a;树形结构中&#xff0c;子…

昇思MindSpore学习开始

昇思MindSpore是一个全场景深度学习框架&#xff0c;旨在实现易开发、高效执行、全场景统一部署三大目标。 其中&#xff0c;易开发表现为API友好、调试难度低&#xff1b;高效执行包括计算效率、数据预处理效率和分布式训练效率&#xff1b;全场景则指框架同时支持云、边缘以…