Easylogging++源码分析

更新时间:2023-05-04 09:48:05 阅读: 评论:0

Easylogging++源码分析
⼀.
使⽤开源项⽬最⼤的好处就是可以看它的源码来加深你的理解,理解了其实现原理,则使⽤起来必定更加得⼼应⼿。
下⾯⼏个类是Easylogging中最重要的⼏个类,弄明⽩了这⼏个类就能弄懂各项功能的实现:
Loger:调试者
RegisteredLoggers:调试者仓库,即多个调试者的集合
Writer:调试器
Configuration:配置器
Configurations:配置器仓库,即多个配置器的集合
调试者,调试器,配置器三者的关系如下:
当开始⼀次log输出时,Writer找到⼀个对应的Loger,该Loger保存了⾃⼰的配置信息,根据这些配置信息,Writer决定是否输出该log,或者以什么格式将log输出到哪个⽂件中。
上⾯的流程就是每次log的输出过程。从源码中可以很清晰的看到这个流程,展开宏LINFO:
#define LINFO CINFO("trivial")
"trivial"是Loger的标识符号,表明这条log将使⽤"trivial"调试器。类似的还有其他的调试器:
#define BINFO CINFO("business")
#define SINFO CINFO("curity")
等等调试器。再继续⼀层层的展开宏,最后可以发现:
#define _ELPP_LOG_WRITER(_logger, _level) easyloggingpp::internal::Writer(\
_logger, easyloggingpp::internal::Aspect::Normal, _level, __func__, __FILE__, __LINE__)
实际上是定义了⼀个Writer匿名对象。所以,每当我们开始⼀条log的输出时,就会⽣成⼀个Writer匿名对象。对于匿名对象,在其定义的地⽅,⼀⾏结束后即是其⽣命周期的结束,即这时会执⾏其析构函数。
Writer的构造函数中,会根据loggerId_(即前⾯说的"trivial")获取到对应的loger:
logger_ = registeredLoggers->get(loggerId_, fal);
这⾥的registeredLoggers就是Loger仓库了,这个仓库是什么时候在哪⾥建⽴的?这个会在后⾯讲。
然后在Writer的析构函数中,会进⾏log的真正处理,调⽤buildAndWriterLine()函数,这个函数做的事如下:
(1)先从loger中拿到配置信息:
TypedConfigurations* conf_ = logger_->typedConfigurations_;
(2)根据配置信息,加载log到Writer的成员变量currLine_中,展⽰⼀部分代码如下:
if (f_ & constants_->kAppName) {
v_ = logger_->applicationName();
fs_ = constants_->APP_NAME_FORMAT_SPECIFIER;
internal::utilities::LogManipulator::updateFormatValue(fs_, v_, currLine_, constants_);
}
// Logger ID
if (f_ & constants_->kLoggerId) {
v_ = logger_->id();
fs_ = constants_->LOGGER_ID_FORMAT_SPECIFIER;
internal::utilities::LogManipulator::updateFormatValue(fs_, v_, cu凉拌花生米 rrLine_, constants_月季花期 );
}
// Log message
if (f_ & constants_->kLogMessage) {
fs_ = constants_->LOG_MESSAGE_FORMAT_SPECIFIER;
internal::utilities::LogManipulator::updateFormatValue(fs_, logger_->stream()->str(), currLine_, constants_);
其中,上⾯的Log message即是我们本⾝要输出的,其保存在logger_的std::stringstream* stream_;中。例如:
LINFO<<"test!";
即这时我们要输出的test!"信息就保存中stream_成员中。这个过程就是在之前的⽂章<Easylogging的封装使⽤>中讲到的,在Writer 的<<;操作符重载函数中,其针对每种数据类型都有对应的重载函数,包括⼀些第三⽅库的数据类型,或者⾃定义的类型,例如下⾯⼏个例⼦:
inline Writer& operator<<(const std::string& log_) {
if (!proceed_) { return *this; }
_ELPP_STREAM(logger_) << log_;
return *this;
}
inline Writer& operator<<(signed short log_) {
if (!proceed_) { return *this; }
_ELPP_STREAM(logger_) << log_;
return *this;
}
inline Writer& operator<<(const QStringRef& log_) {
if (!proceed_) { return *this; }
return operator<<(log_.toString());
}
template <clas小学五年级古诗 s Class>
inline Writer& operator<<(const Class& class_) {
if (!proceed_) { return *this; }
_ELPP_STREAM(logger_) << class_;
return *this;
}
(3)根据配置信息,将currLine_输出到⽂件或者标志参差 输出中:
if (logger_->stream_) {
if (logger_->typedConfigurations_->toFile(verity_)) {
safeWriteToFile(verity_, logger_, currLine_);
}
if (logger_->typedConfigurations_->toStandardOutput(verity_)) {
std::cout << currLine_;
}
到这⾥,⼀条log就输出完毕。
⼆.
_INITIALIZE_EASYLOGGINGPP
展开该宏:
#define _INITIALIZE_EASYLOGGINGPP                                \
namespace easyloggingpp {                                    \
namespace internal {                                      \
ScopedPointer<RegisteredLoggers> registeredLoggers(  \
new RegisteredLoggers());                &nbs秋天的味道 p;   \
}                                                        \
}
该宏其实就是在easylogginggpp::internal作⽤域内定义了⼀个ScopedPointer<RegisteredLoggers>对象。ScopedPointer是Easylogging内部实现的⼀个智能指针模板类,类似于C++11的  shared_ptr。这⾥创建的智能指针要管理的对象是RegisteredLoggers,所以在这⾥给智能指针传的参数是new ⼀个RegisteredLoggers对象,⾄此,Loggers仓库创建完成。另外需要说的⼀点是,这⾥⽤智能指针进⾏
仓库管理的好处是,我们只需在刚开始使⽤Easylogging时进⾏初始化,⽽不⽤在不使⽤Easylogging时去⼿动清理。
RegisteredLoggers的构造函数中会创建⼏个默认的Logger:
registerNew(new Logger("trivial", constants_, conf));
registerNew(new Logger("business", constants_));
registerNew(new Logger("curity", constants_));
registerNew(new Logger("performance", constants_, confPe养胃的菜 rformance));
之前提到的"trivial" Loger就是在这时创建的。
再来看看RegisterdLoggers是如何管理多个Loger的,这个的实现也很有意思。RegisteredLoggers继承于⼀个模板类
template<class Class, class Predicate>
class Registry {
作者是这样解释Registry模板类的使⽤的:
//! Internal repository ba to manage memory on heap. Ud internally, you should not u it.
即它是⼀个基于堆内存管理的内部仓库。模板参数Class是管理的对象的类型,模板参数Predicate是⼀个函数对象,⽤来 判断Logger是否符合某个条件。
Registry类的数据成员如下:
private:
std::vector<Class*> list_;
即通过std::vector管理了所有对象的指针,每当注册⼀个新的对象时,就是把该对象的指针推到vector中:
inline void registerNew(Class* c_) {
list_.push_back(c_);
}
从仓库中获取⼀个特定的Loger时,会遍历整个vector,通过模板参数Predicate这个函数对象去判断是否是要找的那个Logger:
template<typename T, typename T2>
Class* get(const T& t_, const T2& t2_) {
Iterator iter = std::find_if(list_.begin(), list_.end(), Predicate(t_, t2_));
if (iter != list_.end() && *iter != NULL) {
return *iter;
}
return NULL;
}
RegisteredLoggers就是通过这个Registry类来管理多个Logger的:
class RegisteredLoggers : public internal::Registr门缝里看人歇后语 y<Logger, Logger::Predicate>
实际上,Configurations和Configuration之间的关系类似于RegisteredLoggers和Logger的关系,都是仓库管理多个对象,所以Configurations的实现也是继承于Registry的:
class Configurations : public internal::Registry<internal::Configuration, internal::Configuration::Predicate>
三.
Easylogging中的⼏个重要的概念和类的实现都讲了,还有其他的⼀些细节值得关注,例如:跨平台的实现,线程安全,配置⽂件的解析等等,有兴趣的话,可以去翻翻源码,对理解这个开源库还是很有帮助的。

本文发布于:2023-05-04 09:48:05,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/855680.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:配置   对象   仓库
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图