目录
在上一篇我们介绍完了 Storage 类的其他接口。今天我们来看看 Logger
类和 RegisteredLoggers
类的接口。
Logger 类
已经介绍过的接口
在 总体设计 框架设计部分我们介绍了
Logger
类保存的一些信息。
在 日志格式配置方式 中我们介绍了Logger
类与日志配置相关的一些接口。相关接口的声明如下:void configure(const Configurations &configurations); void resolveLoggerFormatSpec(void) const; Logger(const std::string &id, const Configurations &configurations, base::LogStreamsReferenceMapPtr logStreamsReference); void reconfigure(void);
在 CLOG 宏 其他相关类 中我们介绍了
Logger
类与日志输出相关的一些接口。相关接口的声明如下:bool isValidId(const std::string &id) void flush(void); void flush(Level level, base::type::fstream_t *fs); void initUnflushedCount(void);
在 类 printf 接口 中我们详细分析了
Logger
的类printf
接口的实现。构造函数
Logger::Logger(const std::string &id, base::LogStreamsReferenceMapPtr logStreamsReference) : m_id(id), m_typedConfigurations(nullptr), m_parentApplicationName(std::string()), m_isConfigured(false), m_logStreamsReference(logStreamsReference) { initUnflushedCount(); }
initUnflushedCount
接口在CLOG
宏 其他相关类 中已经介绍过了,这里就不多说了。Logger::Logger(const std::string &id, const Configurations &configurations, base::LogStreamsReferenceMapPtr logStreamsReference) : m_id(id), m_typedConfigurations(nullptr), m_parentApplicationName(std::string()), m_isConfigured(false), m_logStreamsReference(logStreamsReference) { initUnflushedCount(); configure(configurations); }
configure
接口在 日志格式配置方式 中已经介绍过了,这里就不多说了。Logger::Logger(const Logger &logger) { base::utils::safeDelete(m_typedConfigurations); m_id = logger.m_id; m_typedConfigurations = logger.m_typedConfigurations; m_parentApplicationName = logger.m_parentApplicationName; m_isConfigured = logger.m_isConfigured; m_configurations = logger.m_configurations; m_unflushedCount = logger.m_unflushedCount; m_logStreamsReference = logger.m_logStreamsReference; }
赋值运算符
Logger &Logger::operator=(const Logger &logger) { if (&logger != this) { base::utils::safeDelete(m_typedConfigurations); m_id = logger.m_id; m_typedConfigurations = logger.m_typedConfigurations; m_parentApplicationName = logger.m_parentApplicationName; m_isConfigured = logger.m_isConfigured; m_configurations = logger.m_configurations; m_unflushedCount = logger.m_unflushedCount; m_logStreamsReference = logger.m_logStreamsReference; } return *this; }
析构函数
virtual ~Logger(void) { base::utils::safeDelete(m_typedConfigurations); }
日志输出支持
// 用于直接日志输出支持 virtual inline void log(el::base::type::ostream_t &os) const { os << m_id.c_str(); }
获取日志记录器 ID
const std::string &id(void) const { return m_id; }
应用程序名称相关接口
// 获取应用程序名称 inline const std::string &parentApplicationName(void) const { return m_parentApplicationName; } // 设置应用程序名称 inline void setParentApplicationName(const std::string &parentApplicationName) { m_parentApplicationName = parentApplicationName; }
获取日志记录器对应的 Configurations 对象
inline Configurations *configurations(void) { return &m_configurations; }
获取日志记录器对应的 TypedConfigurations 对象
inline base::TypedConfigurations *typedConfigurations(void) { return m_typedConfigurations; }
日志构建器
// 获取日志构建器 inline LogBuilder *logBuilder(void) const { return m_logBuilder.get(); } // 设置日志构建器 inline void setLogBuilder(const LogBuilderPtr &logBuilder) { m_logBuilder = logBuilder; }
LogBuilder
类稍后会详细分析。日志记录器的指定日志级别是否启用
inline bool enabled(Level level) const { return m_typedConfigurations->enabled(level); }
获取日志记录器对应的字符串流对象
inline base::type::stringstream_t &stream(void) { return m_stream; }
RegisteredLoggers 类
RegisteredLoggers
类用于管理注册的日志记录器。
先看下继承关系:
公有继承自base::utils::Registry <Logger, std::string>
。
底层容器为std::unordered_map <std::string, Logger*>
。基于
Registry
的iterator
别名定义了自身的迭代器类型别名iterator
和const_iterator
:// 实际iterator就是std::unordered_map<std::string, Logger*>::iterator typedef typename Registry<Logger, std::string>::iterator iterator; // 实际const_iterator就是std::unordered_map<std::string, Logger*>::iconst_iterator typedef typename Registry<Logger, std::string>::const_iterator const_iterator;
当前类中未直接使用
iterator
和const_iterator
类型。
Registry
类模板在后面分析easylogging++的设计理念时会详细介绍。
RegisteredLoggers
类的实现如下:class RegisteredLoggers : public base::utils::Registry<Logger, std::string> { public: explicit RegisteredLoggers(const LogBuilderPtr &defaultLogBuilder); virtual ~RegisteredLoggers(void) { unsafeFlushAll(); } inline void setDefaultConfigurations(const Configurations &configurations) { base::threading::ScopedLock scopedLock(lock()); m_defaultConfigurations.setFromBase(const_cast<Configurations *>(&configurations)); } inline Configurations *defaultConfigurations(void) { return &m_defaultConfigurations; } // 根据日志记录器id获取对应的日志记录器 Logger *get(const std::string &id, bool forceCreation = true); // 根据注册回调标识ID获取注册对应的回调 template <typename T> inline bool installLoggerRegistrationCallback(const std::string &id) { return base::utils::Utils::installCallback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); } // 根据注册回调标识ID获取注销对应的回调 template <typename T> inline void uninstallLoggerRegistrationCallback(const std::string &id) { base::utils::Utils::uninstallCallback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); } // 根据注册回调标识ID获取对应的注册回调对象指针 template <typename T> inline T *loggerRegistrationCallback(const std::string &id) { return base::utils::Utils::callback<T, base::type::LoggerRegistrationCallbackPtr>(id, &m_loggerRegistrationCallbacks); } bool remove(const std::string &id); // 根据日志记录器ID查找对应的日志记录器 inline bool has(const std::string &id) { return get(id, false) != nullptr; } // 删除日志记录器 inline void unregister(Logger *&logger) { base::threading::ScopedLock scopedLock(lock()); base::utils::Registry<Logger, std::string>::unregister(logger->id()); } inline LogStreamsReferenceMapPtr logStreamsReference(void) { return m_logStreamsReference; } // 刷新全部的日志文件(线程安全) inline void flushAll(void) { base::threading::ScopedLock scopedLock(lock()); unsafeFlushAll(); } inline void setDefaultLogBuilder(LogBuilderPtr &logBuilderPtr) { base::threading::ScopedLock scopedLock(lock()); m_defaultLogBuilder = logBuilderPtr; } private: LogBuilderPtr m_defaultLogBuilder; // 默认的日志构建器 Configurations m_defaultConfigurations; // 默认的日志配置 base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; // 日志文件流相关 std::unordered_map<std::string, base::type::LoggerRegistrationCallbackPtr> m_loggerRegistrationCallbacks; // 日志注册回调对象容器,key->日志记录器注册回调ID,value->日志记录器注册回调 friend class el::base::Storage; // 刷新全部的日志文件(非线程安全) void unsafeFlushAll(void); }; RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr &defaultLogBuilder) : m_defaultLogBuilder(defaultLogBuilder) { m_defaultConfigurations.setToDefault(); m_logStreamsReference = std::make_shared<base::LogStreamsReferenceMap>(); }
Configurations
类已经在 日志格式配置管理类 详细介绍过了。根据日志记录器 id 获取对应的日志记录器
Logger *RegisteredLoggers::get(const std::string &id, bool forceCreation) { base::threading::ScopedLock scopedLock(lock()); Logger *logger_ = base::utils::Registry<Logger, std::string>::get(id); // 当不存在forceCreation为true时,新建对应id的logger并注册,同时调用注册回调 if (logger_ == nullptr && forceCreation) { bool validId = Logger::isValidId(id); if (!validId) { ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); return nullptr; } logger_ = new Logger(id, m_defaultConfigurations, m_logStreamsReference); logger_->m_logBuilder = m_defaultLogBuilder; // 注册新的日志记录器 registerNew(id, logger_); LoggerRegistrationCallback *callback = nullptr; // 遍历全部的日志记录器注册事件回调 for (const std::pair<std::string, base::type::LoggerRegistrationCallbackPtr> &h : m_loggerRegistrationCallbacks) { callback = h.second.get(); if (callback != nullptr && callback->enabled()) { callback->handle(logger_); } } } return logger_; }
registerNew
接口后面分析 easylogging++的设计理念时会详细分析,这里就不多说了。根据日志记录器 id 删除对应的日志记录器
bool RegisteredLoggers::remove(const std::string &id) { if (id == base::consts::kDefaultLoggerId) { return false; } // get has internal lock Logger *logger = base::utils::Registry<Logger, std::string>::get(id); if (logger != nullptr) { // unregister has internal lock unregister(logger); } return true; }
刷新全部的日志文件
void RegisteredLoggers::unsafeFlushAll(void) { ELPP_INTERNAL_INFO(1, "Flushing all log files"); // m_logStreamsReference保存了当前正在使用的所有的日志文件流。 for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference->begin(); it != m_logStreamsReference->end(); ++it) { if (it->second.get() == nullptr) continue; it->second->flush(); } }
其他相关类
LogBuilder 类
LogBuilder
类用来将LogMessage
调整(如是否需要加入换行,以及是否添加彩色显示时需要增加的相关标记用于彩色显示)。
LogBuilder
类是抽象类,只能被继承,派生类需要实现build
接口。class LogBuilder : base::NoCopy { public: LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...") } // build接口主要用于调整LogMessage virtual base::type::string_t build(const LogMessage *logMessage, bool appendNewLine) const = 0; // 调整LogMessage用于彩色显示 void convertToColoredOutput(base::type::string_t *logLine, Level level); private: bool m_termSupportsColor; friend class el::base::DefaultLogDispatchCallback; }; // 调整LogMessage用于彩色显示 void LogBuilder::convertToColoredOutput(base::type::string_t *logLine, Level level) { if (!m_termSupportsColor) return; const base::type::char_t *resetColor = ELPP_LITERAL("\x1b[0m"); if (level == Level::Error || level == Level::Fatal) *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; else if (level == Level::Warning) *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; else if (level == Level::Debug) *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; else if (level == Level::Info) *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; else if (level == Level::Trace) *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; }
DefaultLogBuilder 类
DefaultLogBuilder
类继承自LogBuilder
类,实现了build
接口。
DefaultLogBuilder
类主要作为 easylogging++的默认日志构建器,用于初始化Storage
全局管理类。class DefaultLogBuilder : public LogBuilder { public: base::type::string_t build(const LogMessage *logMessage, bool appendNewLine) const; }; base::type::string_t DefaultLogBuilder::build(const LogMessage *logMessage, bool appendNewLine) const { base::TypedConfigurations *tc = logMessage->logger()->typedConfigurations(); // LogFormat:日志格式工具类用于管理日志格式(单个日志记录器的FORMAT配置项) const base::LogFormat *logFormat = &tc->logFormat(logMessage->level()); // 配置文件当中单个日志记录器的FORMAT配置项的字符串形式的值 base::type::string_t logLine = logFormat->format(); char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; const char *bufLim = buff + sizeof(buff); // 下面依次检查有没有指定的日志格式指示器,有则替换为实际的内容 if (logFormat->hasFlag(base::FormatFlags::AppName)) { // App name base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, logMessage->logger()->parentApplicationName()); } if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { // Thread ID base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, ELPP->getThreadName(base::threading::getCurrentThreadId())); } if (logFormat->hasFlag(base::FormatFlags::DateTime)) { // DateTime base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), &tc->subsecondPrecision(logMessage->level()))); } if (logFormat->hasFlag(base::FormatFlags::Function)) { // Function base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); } if (logFormat->hasFlag(base::FormatFlags::File)) { // File base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); } if (logFormat->hasFlag(base::FormatFlags::FileBase)) { // FileBase base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); base::utils::File::buildBaseFilename(logMessage->file(), buff); base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); } if (logFormat->hasFlag(base::FormatFlags::Line)) { // Line char *buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); } if (logFormat->hasFlag(base::FormatFlags::Location)) { // Location char *buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); buf = base::utils::Str::addToBuff(buff, buf, bufLim); buf = base::utils::Str::addToBuff(":", buf, bufLim); buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); } if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { // Verbose level char *buf = base::utils::Str::clearBuff(buff, 1); buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); } if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { // Log message base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); } #if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) // 自定义的日志格式指示器用对应注册的日志格式解析器解析的结果来替换 el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); ELPP_UNUSED(lock_); // 依次遍历所有的日志格式解析器,对所有自定义的格式都替换一遍 for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin(); it != ELPP->customFormatSpecifiers()->end(); ++it) { std::string fs(it->formatSpecifier()); base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); } #endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) if (appendNewLine) logLine += ELPP_LITERAL("\n"); return logLine; }
build
接口本身并不复杂,这里就不多说了。
base::utils::Str
类是通用字符串操作的工具类,base::utils::File
是通用文件操作的工具类,后面文章会详细介绍。
至此,Logger
类和 RegisteredLoggers
类就介绍完了,下一篇我们开始介绍 LogFormat
类。