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 类。

posted @ 2022-12-08 20:03  节奏自由  阅读(60)  评论(0编辑  收藏  举报