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

news2024/12/23 19:11:45

同步异步日志系统

  • 一、日志器管理模块(单例模式)
    • 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.coloradmin.cn/o/1931136.html

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

相关文章

视图库对接系列(GA-T 1400)十九、视图库对接系列(级联)注册

背景 在上一章视图库对接系列(GA-T 1400)十八、视图库对接系列(级联)代码生成中我们已经把代码生成了,那怎么实现级联? 我们可以抓包看设备是怎么注册到我们平台的, 那我们就怎么实现就可以了。 实现 先看设备注册到我们服务端的包 步骤 注册我们可以参考视图库对接系列(…

Data类中的常用方法

Calender类 java.util.Calendar是一个抽象的基类&#xff0c;创建对象需要使用静态方法Calendar.getInstance()完成。通过Calendar对象可以获得详细的日历信息&#xff0c;例如年、月、日、小时、分和秒&#xff0c;Calendar的子类可以实现特定的日历系统。 当前时间 Calenda…

CV每日论文--2024.7.16

1、Radiance Fields from Photons 中文标题&#xff1a;光子的辐射场 简介&#xff1a;神经辐射场(NeRFs)已成为从多个视角捕获的图像进行高质量视图合成的事实标准方法。然而,在野外环境下捕获图像时,仍存在许多挑战,例如低光、高动态范围或快速运动导致的模糊重建和明显的伪…

【MySQL进阶篇】索引

1、索引概述 索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c…

高职院校专业群的生成机制研究

摘要&#xff1a;本研究针对我国产业转型升级背景下高职院校专业群的生成机制进行了深入探讨。运用案例分析法&#xff0c;从生成决策、生成目标、生成规则三个维度出发&#xff0c;对专业群的生成机制进行了系统分析。研究发现&#xff0c;高职院校专业群的生成是一个与产业环…

容器之Harbor

Harbor 是一个开源的企业级 Docker 镜像仓库&#xff0c;用于存储、签名和扫描 Docker 镜像。它由 VMware 开发&#xff0c;旨在增强用户在容器化环境中的安全性和效率。以下是对 Harbor 的详细介绍&#xff1a; 主要功能 1. 镜像管理 镜像存储&#xff1a;提供高效的存储管理…

誉天教育与武汉晴川学院携手开展鸿蒙实训营,共筑鸿蒙生态新篇章!

在数字经济蓬勃发展的今天&#xff0c;鸿蒙系统作为华为自主研发的操作系统&#xff0c;正逐步构建起一个开放、协同、共赢的生态体系。为了进一步推动鸿蒙生态的繁荣发展&#xff0c;培养更多具备鸿蒙原生应用开发能力的专业人才&#xff0c;誉天教育与武汉晴川学院强强联合&a…

苹果公司等科技巨头涉嫌违规使用 YouTube 视频训练 AI|TodayAI

YouTube 强调&#xff0c;使用其平台内容训练人工智能违反服务条款&#xff0c;苹果公司&#xff08;Apple&#xff09;、Anthropic 等科技巨头却涉嫌违规 根据 Proof News 和 Wired 合作的一项调查报告&#xff0c;超过 17 万个 YouTube 视频成为了苹果公司、Anthropic、Nvid…

2024年大数据高频面试题(中篇)

文章目录 Kafka为什么要用消息队列为什么选择了kafkakafka的组件与作用(架构)kafka为什么要分区Kafka生产者分区策略kafka的数据可靠性怎么保证ack应答机制(可问:造成数据重复和丢失的相关问题)副本数据同步策略ISRkafka的副本机制kafka的消费分区分配策略Range分区分配策略…

单链表算法 - 环形链表I

. - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/linked-list-cycle/description/思路: 代码: /*** Definition for sin…

Docker的数据管理和网络通信

目录 一、Docker 的数据管理 1&#xff0e;数据卷 2&#xff0e;数据卷容器 二、端口映射 三、容器互联&#xff08;使用centos镜像&#xff09; 四、*Docker 镜像的创建 1&#xff0e;基于现有镜像创建 2&#xff0e;基于本地模板创建 3&#xff0e;基于Dockerfile 创…

第二十章 Nest 大文件分片上传

在前端的文件上传功能中&#xff0c;只要请求头里定义 content-type 为 multipart/form-data&#xff0c;内容就会以下面形式传递到服务端&#xff0c;接着服务器再按照multipart/form-data的格式去提取数据 获取文件数据但是当文件体积很大时 就会出现一个问题 文件越大 请求的…

cpp 强制转换

一、static_cast static_cast 是 C 中的一个类型转换操作符&#xff0c;用于在类的层次结构中进行安全的向上转换&#xff08;从派生类到基类&#xff09;或进行不需要运行时类型检查的转换。它主要用于基本数据类型之间的转换、对象指针或引用的向上转换&#xff08;即从派生…

Mysql缓存调优的基本知识(附Demo)

目录 前言1. 配置2. 缓存3. 策略 前言 基本的知识推荐阅读&#xff1a; java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09;Mysql优化高级篇&#xff08;全&#xff09;Mysql底层原理详细剖析常见面试题&#xff08;全&#xff09; MySQL…

【网络安全的神秘世界】HackBar绕过许可证

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 &#x1f344;问题描述 用Firefox浏览器安装hackbar插件后&#xff0c;按F12键&#xff0c;发现如下提示&#xff1a;无法使…

防火墙的带宽管理

一、实验拓扑 目录 一、实验拓扑 二、实验要求 三、实验步骤 3.1将防火墙组网改成双机热备的组网形式&#xff0c;做负载分担模式&#xff0c;游客区和DMZ区走FW3&#xff0c;生产区和办公区的流量走FW1 3.1.1心跳线设置 3.2启用双机热备&#xff0c;并配置VRRP组 3.2.1…

MySQL常用命令的实战应用

MySQL常用命令全攻略&#xff1a;从入门到精通的实用指南 在数字化时代的浪潮中&#xff0c;MySQL作为关系型数据库管理系统的佼佼者&#xff0c;以其稳定性和高效性赢得了全球用户的青睐。无论是新手还是资深开发者&#xff0c;掌握MySQL的常用命令都是提升数据库管理能力的基…

内行人才知道的白酒术语

&#x1f61c;宝子们&#xff0c;今天来给大家分享一些只有内行人懂的白酒术语&#xff0c;让你在酒桌上也能显得很专业&#xff01;&#x1f4aa; ⬆️基酒术语解释&#xff1a;所谓基酒就是最基础的酒&#xff0c;也叫原浆酒&#xff0c;是指成酒后不经过勾调的酒液。基酒度…

数据库内核研发学习之路(三)创建postgres内置函数

本章之前已经讲明白了我们的postgres如何进行编译安装&#xff0c;这是很重要的一步&#xff0c;接下来就是学会对postgres进行小的改动&#xff0c;然后保证依然能够顺利编译安装运行&#xff01; 本章续讲内容如何创建一个内置函数。 1、内置函数和用户自定义函数的区别 熟…

国产精品ORM框架-SqlSugar详解 进阶功能 集成整合 脚手架应用 附源码 云草桑 专题二

国产精品ORM框架-SqlSugar详解 SqlSugar初识 专题一-CSDN博客 sqlsugar 官网-CSDN博客 4、进阶功能 5、集成整合 6、脚手架应用 4、进阶功能 4.1、生命周期 Queryable 什么时候操作库 Queryable是一个引用类型 Queryable拷贝机制 {ISugarQueryable<Student> quer…