当前位置: 首页 > 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() ;
}

在这里插入图片描述

相关文章:

  • Gen - CDPT举例说明:动态上下文前缀(输入先和标签结合,输出结果会更贴近标签内容)
  • 【ROS】航点导航功能
  • 解决vscode找不到Python自定义模块,报错No module named ‘xxx‘
  • 【Redis】Redis中的常见数据类型(一)
  • 通过爬虫方式实现头条号发布视频(2025年4月)
  • 常见的页面报错
  • Spring MVC 如何体现 Model-View-Controller 各自的职责?它们之间是如何协作的?
  • VS Code 远程连接服务器:Anaconda 环境与 Python/Jupyter 运行全指南。研0大模型学习(第六、第七天)
  • LicheeRV Nano 与Ubuntu官方risc-v 镜像混合
  • xss学习3之服务端session
  • 大数据开发知识1:数据仓库
  • Java表达式1.0
  • 相对路径和绝对路径解析
  • 遇到QT进程启动失败。被调用的程序丢失,或者您可能没有足够的权限来调用该程序。
  • 聊一聊接口测试后垃圾数据如何清理?
  • C语言状态字与库函数详解:概念辨析与应用实践
  • 【leetcode刷题日记】lc.152-乘积最大子数组
  • 念去去千里烟波,雾霭沉沉楚天阔
  • 大语言模型推理能力的强化学习现状理解GRPO与近期推理模型研究的新见解
  • 网络基础与 HTTP 协议
  • 陈杨梅:刷到“棉花糖爸爸”寻女视频,隐约觉得自己就是爸爸要找的孩子
  • “科技+萌点”机器人马拉松刷屏!宇树回应“半马摔倒”
  • 亚太峰会上哪个词最火?我们问了问AI
  • 42岁北京大学科学技术与医学史系副教授陈昊逝世
  • 民生访谈|事关餐饮消费券、外牌车置换更新补贴,上海市商务委回应
  • 企业跨境支付的最大挑战及解决方案