easylogging++的那些事(四)原始碼分析(十二)Logger和RegisteredLoggers相關介面
在上一篇我們介紹完了 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
類。