1. 程式人生 > 其它 >easylogging++的那些事(四)原始碼分析(十二)Logger和RegisteredLoggers相關介面

easylogging++的那些事(四)原始碼分析(十二)Logger和RegisteredLoggers相關介面

目錄

在上一篇我們介紹完了

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*>

    基於 Registryiterator 別名定義了自身的迭代器類型別名 iteratorconst_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;

    當前類中未直接使用 iteratorconst_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 類。