我们在上次已经完成了建造者类的编写,建造者类的编写可以帮助我们很好的组建我们的对象。还没有看过上一次的小伙伴可以点击这里:
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
)。它的主要作用是提供一个全局唯一的日志管理器实例,负责创建、存储和检索日志器。以下是详细的功能说明和设计目的:
LoggerManager
使用单例模式,确保在整个应用程序中只有一个全局的 LoggerManager
实例。getInstance()
提供对这个唯一实例的访问。static LoggerManager& getInstance()
{
static LoggerManager eton;
return eton;
}
eton
在 C++11 及之后的标准中是线程安全的,编译器会自动处理多线程环境下的初始化问题。LoggerManager
的实例可以在任何地方轻松获取,而无需手动创建或管理。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;
}
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()
方法直接访问默认日志器。_loggers
,因此需要确保线程安全性。使用 std::mutex
和 std::unique_lock
来保护对 _loggers
的访问:
std::mutex _mutex;
在每个涉及 _loggers
的操作(如添加、查找)中,都加锁以确保数据一致性。
std::unordered_map
存储日志器,键是日志器名称,值是日志器的智能指针。std::unordered_map<std::string, BaseLogger::ptr> _loggers;
std::unordered_map
提供了高效的查找性能(平均时间复杂度为 O(1))。std::shared_ptr
)管理日志器的生命周期,避免内存泄漏。LoggerManager
的主要作用是:
全局管理日志器:
默认日志器:
root logger
),用于记录未指定目标的日志。线程安全性:
灵活性与扩展性:
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() ;
}