当前位置: 首页 > news >正文

C++项目 —— 基于多设计模式下的同步异步日志系统(5)(单例模式)

C++项目 —— 基于多设计模式下的同步&异步日志系统(5)(单例模式)

  • 一个问题
  • 单例模式实现
      • 1. 单例模式:全局唯一实例
        • 功能:
        • 实现细节:
        • 作用:
      • 2. 日志器的注册与查找
        • 功能:
        • 实现细节:
        • 作用:
      • 3. 默认日志器(Root Logger)
        • 功能:
        • 实现细节:
        • 作用:
      • 4. 线程安全性
        • 功能:
        • 实现细节:
        • 作用:
      • 5. 数据结构
        • 功能:
        • 实现细节:
        • 作用:
      • 总结
  • 建造者类扩展

我们在上次已经完成了建造者类的编写,建造者类的编写可以帮助我们很好的组建我们的对象。还没有看过上一次的小伙伴可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/147361584?spm=1011.2415.3001.5331

但是又会有新的问题出现了:

一个问题

我们来看看这段测试代码:

// 测试函数
void testLocalLogger() 
{// 创建局部建造者logs::LocalLogger local_logger;local_logger.buildLoggerName("synclogger");local_logger.buildLoggerlevel(logs::Loglevel::value::DEBUG);local_logger.buildFormatter("abc[%d{%H:%M:%S}][%c]%T%m%n");logs::BaseLogger::ptr logger = local_logger.build();logger->debug("main.cc", 53, "%s","格式化功能测试....");
}

我们是在这个函数体的内部创建了logger,第一点,这个logger出了函数作用域就不能工作了(因为被销毁了);第二点,如果我有一个函数就创建一个logger,不仅资源利用低下,还特别不容易统一管理。

所以这时候我们想到了单例模式,想详细了解单例模式的小伙伴可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/136603292?spm=1011.2415.3001.5331

单例模式可以保证全局只会有一个实例,可以做到资源利用的最大化:

单例模式实现

    class LoggerManager{public:static LoggerManager& getInstance(){// c++11之后,针对静态局部变量,编译器在编译的层面实现了线程安全// 当静态局部变量在没有构造完成之前,其他的线程进入就会阻塞static LoggerManager eton;return eton;}void addLogger(logs::BaseLogger::ptr& logger){if(hasLogger(logger->name()))return;std::unique_lock<std::mutex> lock(_mutex);_loggers.insert(std::make_pair(logger->name(),logger));}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;}logs::BaseLogger::ptr getLogger(const std::string &name){std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);if (it == _loggers.end()){return BaseLogger::ptr();}return it->second;}logs::BaseLogger::ptr rootLogger(){return _root_logger;}private:LoggerManager(){std::unique_ptr<logs::LoggerBuilder> builder(new logs::LocalLogger());builder->buildLoggerName("root");_root_logger = builder->build();_loggers.insert(std::make_pair("root", _root_logger));}std::mutex _mutex;logs::BaseLogger::ptr _root_logger; // 默认日志器std::unordered_map<std::string, BaseLogger::ptr> _loggers;};

这段代码实现了一个 日志管理器(LoggerManager,用于集中管理和操作日志器(BaseLogger)。它的主要作用是提供一个全局唯一的日志管理器实例,负责创建、存储和检索日志器。以下是详细的功能说明和设计目的:


1. 单例模式:全局唯一实例

功能:
  • LoggerManager 使用单例模式,确保在整个应用程序中只有一个全局的 LoggerManager 实例。
  • 通过静态方法 getInstance() 提供对这个唯一实例的访问。
实现细节:
static LoggerManager& getInstance()
{static LoggerManager eton;return eton;
}
  • 静态局部变量 eton 在 C++11 及之后的标准中是线程安全的,编译器会自动处理多线程环境下的初始化问题。
  • 这种设计使得 LoggerManager 的实例可以在任何地方轻松获取,而无需手动创建或管理。
作用:
  • 提供一个全局访问点,方便集中管理所有日志器。
  • 避免重复创建多个日志管理器实例,节省资源并减少潜在冲突。

2. 日志器的注册与查找

功能:
  • LoggerManager 提供了添加、查找和获取日志器的功能,用于统一管理所有的日志器。
实现细节:
  • 添加日志器

    void addLogger(logs::BaseLogger::ptr& logger)
    {if (hasLogger(logger->name()))return;std::unique_lock<std::mutex> lock(_mutex);_loggers.insert(std::make_pair(logger->name(), logger));
    }
    
    • 检查是否已经存在同名的日志器,避免重复添加。
    • 使用互斥锁 _mutex 确保多线程环境下的线程安全性。
  • 检查日志器是否存在

    bool hasLogger(const std::string &name)
    {std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);return it != _loggers.end();
    }
    
    • 查找 _loggers 中是否存在指定名称的日志器。
  • 获取日志器

    logs::BaseLogger::ptr getLogger(const std::string &name)
    {std::unique_lock<std::mutex> lock(_mutex);auto it = _loggers.find(name);if (it == _loggers.end())return BaseLogger::ptr();return it->second;
    }
    
    • 如果找到指定名称的日志器,则返回对应的智能指针;否则返回空指针。
作用:
  • 提供了一种统一的方式来管理日志器的生命周期。
  • 避免了日志器的重复创建和命名冲突。
  • 支持动态添加和查找日志器,满足不同模块的需求。

3. 默认日志器(Root Logger)

功能:
  • LoggerManager 在构造函数中创建了一个默认的日志器(root logger),用于记录未指定目标的日志。
实现细节:
LoggerManager()
{std::unique_ptr<logs::LoggerBuilder> builder(new logs::LocalLogger());builder->buildLoggerName("root");_root_logger = builder->build();_loggers.insert(std::make_pair("root", _root_logger));
}
  • 使用 LoggerBuilder 创建一个名为 "root" 的默认日志器。
  • 将该日志器存储在 _root_logger 中,并将其注册到 _loggers 中。
作用:
  • 提供了一个全局可用的默认日志器,避免日志丢失的风险。
  • 用户可以通过 rootLogger() 方法直接访问默认日志器。

4. 线程安全性

功能:
  • 在多线程环境中,多个线程可能同时访问或修改 _loggers,因此需要确保线程安全性。
实现细节:
  • 使用 std::mutexstd::unique_lock 来保护对 _loggers 的访问:

    std::mutex _mutex;
    
  • 在每个涉及 _loggers 的操作(如添加、查找)中,都加锁以确保数据一致性。

作用:
  • 避免了多线程环境下的数据竞争问题。
  • 提高了代码的健壮性和可靠性。

5. 数据结构

功能:
  • 使用 std::unordered_map 存储日志器,键是日志器名称,值是日志器的智能指针。
实现细节:
std::unordered_map<std::string, BaseLogger::ptr> _loggers;
  • std::unordered_map 提供了高效的查找性能(平均时间复杂度为 O(1))。
  • 使用智能指针(std::shared_ptr)管理日志器的生命周期,避免内存泄漏。
作用:
  • 提供了一种高效的方式来存储和检索日志器。
  • 自动管理日志器的内存,减少手动释放资源的负担。

总结

LoggerManager 的主要作用是:

  1. 全局管理日志器

    • 通过单例模式提供一个全局唯一的日志管理器,集中管理所有日志器。
    • 支持动态添加、查找和获取日志器。
  2. 默认日志器

    • 提供一个默认的日志器(root logger),用于记录未指定目标的日志。
  3. 线程安全性

    • 使用互斥锁保护对日志器集合的访问,确保多线程环境下的安全性。
  4. 灵活性与扩展性

    • 支持动态扩展,可以轻松添加新的日志器。
    • 使用 LoggerBuilder 构建日志器,支持灵活的配置选项。

通过这种方式,LoggerManager 解决了日志系统中常见的全局管理、线程安全、默认日志器等问题,使得日志系统的使用更加方便、可靠和高效。

建造者类扩展

因为我们使用了单例模式,有了全局的变量,所以我们可以扩建我们的建造者类让它也能够帮我们建造全局的日志器:

        // 设置全局日志器的建造者:在局部的基础上增加了一个功能,将日志器添加到单例对象中class GlobaloggerBuild : public LoggerBuilder{public:BaseLogger::ptr build() override{assert(_logger_name.empty() == false); // 必须要有日志器名称if (_formetter.get() == nullptr){_formetter = std::make_shared<Formetter>();}if (_sinks.empty()){buildSink<StdoutSink>();}BaseLogger::ptr logger;if (_logger_type == loggerType::LOGGER_ASYNC){logger = std::make_shared<AsyncLogger>(_logger_name, _limt_level, _formetter, _sinks, _looper_type);}else{logger = std::make_shared<SyncLogger>(_logger_name, _limt_level, _formetter, _sinks);}LoggerManager::getInstance().addLogger(logger);return logger;}};

我们可以来测试一下:

#include "utils.hpp"
#include "level.hpp"
#include "message.hpp"
#include "fometter.hpp"
#include "sink.hpp"
#include "logger.hpp"// 测试函数
void testLocalLogger() 
{logs::GlobaloggerBuild global_logger;global_logger.buildLoggerName("synclogger");logs::BaseLogger::ptr logger = global_logger.build();logger->debug("main.cc", 53, "%s", "格式化功能测试....");
}int main()
{testLocalLogger() ;
}

在这里插入图片描述

http://www.xdnf.cn/news/49195.html

相关文章:

  • css3新特性第四章(渐变)
  • 集合框架(重点)
  • Jsp技术入门指南【八】利用EL表达式开发无脚本的JSP页面
  • HttpClient
  • 网络基础与 HTTP 协议
  • JavaScript forEach介绍(JS forEach、JS for循环)
  • 精益数据分析(7/126):打破创业幻想,拥抱数据驱动
  • 在 Node.js 中设置响应的 MIME 类型
  • Tailwindcss 入门 v4.1
  • rag搭建,是如何进行向量匹配检索的?
  • jsch(shell终端Java版)
  • LeRobot 项目部署运行逻辑(二)—— Mobile Aloha 真机部署
  • Vue3 打印网页内容
  • 通过Dify快速搭建本地AI智能体开发平台
  • 高边开关和低边开关的区别
  • 前端工程化之自动化部署
  • MVCC介绍
  • 《AI大模型应知应会100篇》第28篇:大模型在文本创作中的应用技巧
  • Matlab FCM模糊聚类
  • AI 编程工具——使用cursor创建一个mcp服务,并在cursor中调用
  • 使用LSTM动态调整SIMPLE算法松弛因子的CFD仿真训练程序
  • 使用tshark命令解析tcpdump抓取的数据包
  • 2025年4月19日
  • 【第四十一周】文献阅读:HippoRAG:受神经生物学启发的大型语言模型长期记忆机制
  • STM32 CubeMx下载及安装(一)
  • 【leetcode100】一和零
  • HarmonyOS-ArkUI-动画分类简介
  • javaSE.链表
  • 前端知识深度学习
  • [论文阅读]Making Retrieval-Augmented Language Models Robust to Irrelevant Context