easylogging++的那些事(四)源码分析(十三)LogFormat类

在上一篇文章中我们介绍完了 Logger和RegisteredLoggers相关接口,今天我们来看看 LogFormat 类。

LogFormat 类用于管理日志格式配置当中的 FORMAT 配置项,一个 base::LogFormat 类实例对应一个 FORMAT 配置项。
LogFormat 类的声明如下:

class LogFormat : public Loggable
{
public:
    LogFormat(void);
    LogFormat(Level level, const base::type::string_t &format);
    LogFormat(const LogFormat &logFormat);
    LogFormat(LogFormat &&logFormat);
    LogFormat &operator=(const LogFormat &logFormat);
    virtual ~LogFormat(void) {}
    bool operator==(const LogFormat &other);

    /// @brief Updates format to be used while logging.
    /// @param userFormat User provided format
    void parseFromFormat(const base::type::string_t &userFormat);

    inline Level level(void) const
    {
        return m_level;
    }

    inline const base::type::string_t &userFormat(void) const
    {
        return m_userFormat;
    }

    inline const base::type::string_t &format(void) const
    {
        return m_format;
    }

    inline const std::string &dateTimeFormat(void) const
    {
        return m_dateTimeFormat;
    }

    inline base::type::EnumType flags(void) const
    {
        return m_flags;
    }

    inline bool hasFlag(base::FormatFlags flag) const
    {
        return base::utils::hasFlag(flag, m_flags);
    }

    virtual void log(el::base::type::ostream_t &os) const
    {
        os << m_format;
    }

protected:
    /// @brief Updates date time format if available in currFormat.
    /// @param index Index where %datetime, %date or %time was found
    /// @param [in,out] currFormat current format that is being used to format
    virtual void updateDateFormat(std::size_t index, base::type::string_t &currFormat) ELPP_FINAL;

    /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level
    virtual void updateFormatSpec(void) ELPP_FINAL;

    inline void addFlag(base::FormatFlags flag)
    {
        base::utils::addFlag(flag, &m_flags);
    }

private:
    Level m_level;                     // FORMAT配置项对应的日志记录器的日志级别
    base::type::string_t m_userFormat; // 临时保存用户传入的FORMAT配置项的字符串形式的值
    base::type::string_t m_format;     // 配置文件当中单个日志记录器的FORMAT配置项的字符串形式的值
    std::string m_dateTimeFormat;     // FORMAT配置项字符串形式值当中日期时间部分
    base::type::EnumType m_flags;     // 存放FORMAT配置项格式指示器对应的FormatFlags的按位或的结果
    std::string m_currentUser;
    std::string m_currentHost;
    friend class el::Logger; // To resolve loggerId format specifier easily
};

构造函数

LogFormat::LogFormat(void) : m_level(Level::Unknown),
                             m_userFormat(base::type::string_t()),
                             m_format(base::type::string_t()),
                             m_dateTimeFormat(std::string()),
                             m_flags(0x0),
                             m_currentUser(base::utils::OS::currentUser()),
                             m_currentHost(base::utils::OS::currentHost())
{
}

LogFormat::LogFormat(Level level, const base::type::string_t &format)
    : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()), m_currentHost(base::utils::OS::currentHost())
{
    // 解析FORMAT配置项
    parseFromFormat(m_userFormat);
}

LogFormat::LogFormat(const LogFormat &logFormat) : m_level(logFormat.m_level),
                                                   m_userFormat(logFormat.m_userFormat),
                                                   m_format(logFormat.m_format),
                                                   m_dateTimeFormat(logFormat.m_dateTimeFormat),
                                                   m_flags(logFormat.m_flags),
                                                   m_currentUser(logFormat.m_currentUser),
                                                   m_currentHost(logFormat.m_currentHost)
{
}

LogFormat::LogFormat(LogFormat &&logFormat)
{
    m_level = std::move(logFormat.m_level);
    m_userFormat = std::move(logFormat.m_userFormat);
    m_format = std::move(logFormat.m_format);
    m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
    m_flags = std::move(logFormat.m_flags);
    m_currentUser = std::move(logFormat.m_currentUser);
    m_currentHost = std::move(logFormat.m_currentHost);
}

    parseFromFormat 接口会面会详细介绍。

赋值运算符

LogFormat &LogFormat::operator=(const LogFormat &logFormat)
{
    if (&logFormat != this)
    {
        m_level = logFormat.m_level;
        m_userFormat = logFormat.m_userFormat;
        m_dateTimeFormat = logFormat.m_dateTimeFormat;
        m_flags = logFormat.m_flags;
        m_currentUser = logFormat.m_currentUser;
        m_currentHost = logFormat.m_currentHost;
    }
    return *this;
}

== 运算符

bool LogFormat::operator==(const LogFormat &other)
{
    return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
           m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
}

解析 FORMAT 配置项

/// @brief Updates format to be used while logging.
/// @param userFormat User provided format
void LogFormat::parseFromFormat(const base::type::string_t &userFormat)
{
    // We make copy because we will be changing the format
    // i.e, removing user provided date format from original format
    // and then storing it.
    base::type::string_t formatCopy = userFormat; // copy on write
    m_flags = 0x0;
    auto conditionalAddFlag = [&](const base::type::char_t *specifier, base::FormatFlags flag)
    {
        std::size_t foundAt = base::type::string_t::npos;
        while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos)
        {
            // 找到对应的日志格式指示器并且前一个字符是%
            if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar)
            {
                // 这里感觉逻辑有点怪异,已经有了这个日志格式指示器才将%%处理成%,难道%%不是无论在什么情况下都应该转义成%吗?
                if (hasFlag(flag))
                {
                    // If we already have flag we remove the escape chars so that '%%' is turned to '%'
                    // even after specifier resolution - this is because we only replaceFirst specifier
                    formatCopy.erase(foundAt - 1, 1);
                    // 其实这里不需要偏移也可以,因为删除前一个字符之后,当前的字符必然向前移动了,这样,当前的这个偏移量实际上是指向%的下一个字符了。
                    ++foundAt;
                }
            }
            else
            {
                // 找到对应的日志格式指示器并且前一个字符不是%,没有日志格式指示器,则追加这个日志格式指示器
                if (!hasFlag(flag))
                    addFlag(flag);
            }
        }
    };
    conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
    conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
    conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
    conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
    conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
    conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
    conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
    conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
    conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
    conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
    conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
    conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
    conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
    conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
    // For date/time we need to extract user's date format first
    std::size_t dateIndex = std::string::npos;
    if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos)
    {
        while (dateIndex != std::string::npos && dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar)
        {
            dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
        }
        if (dateIndex != std::string::npos)
        {
            addFlag(base::FormatFlags::DateTime);
            // 对日期格式指示器的部分做相应调整
            updateDateFormat(dateIndex, formatCopy);
        }
    }
    m_format = formatCopy;
    // 替换日期格式字符串当中日志级别格式串,用户格式串以及主机格式串为实际需要写进日志的内容
    updateFormatSpec();
}

    base::FormatFlags 对应日志格式指示器

/// @brief Format flags used to determine specifiers that are active for performance improvements.
enum class FormatFlags : base::type::EnumType
{
    DateTime = 1 << 1,      // 对应%datetime
    LoggerId = 1 << 2,      // 对应%logger
    File = 1 << 3,          // 对应%file
    Line = 1 << 4,          // 对应%line
    Location = 1 << 5,      // 对应%loc
    Function = 1 << 6,      // 对应%func
    User = 1 << 7,          // 对应%user
    Host = 1 << 8,          // 对应%host
    LogMessage = 1 << 9,    // 对应%msg
    VerboseLevel = 1 << 10, // 对应%vlevel
    AppName = 1 << 11,      // 对应%app
    ThreadId = 1 << 12,     // 对应%thread
    Level = 1 << 13,        // 对应%level
    FileBase = 1 << 14,     // 对应%fbase
    LevelShort = 1 << 15    // 对应%levshort
};

    updateDateFormat 接口的定义如下:

// index-日期时间格式部分的起始位置,currFormat-FORMAT配置项对应的经过调整后的字符串形式的值
void LogFormat::updateDateFormat(std::size_t index, base::type::string_t &currFormat)
{
    if (hasFlag(base::FormatFlags::DateTime))
    {
        // 日期时间格式部分格式:%datetime{%Y-%M-%d %H:%m:%s.%g}
        // 跳过“%datatime”这部分,指向日期时间格式部分格式中“{”
        index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
    }
    const base::type::char_t *ptr = currFormat.c_str() + index;
    if ((currFormat.size() > index) && (ptr[0] == '{'))
    {
        // User has provided format for date/time
        ++ptr;
        // count统计日期时间格式部分格式中"{%Y-%M-%d %H:%m:%s.%g}"这部分的长度
        int count = 1; // Start by 1 in order to remove starting brace
        std::stringstream ss;
        for (; *ptr; ++ptr, ++count)
        {
            if (*ptr == '}')
            {
                ++count; // In order to remove ending brace
                break;
            }
            // 非"{}"字符临时保存进ss中,即ss中保存的内容为%datetime{%Y-%M-%d %H:%m:%s.%g}中的"%Y-%M-%d %H:%m:%s.%g"
            ss << static_cast<char>(*ptr);
        }
        // currFormat中删除日期时间格式部分格式中"{%Y-%M-%d %H:%m:%s.%g}"这部分内容,只保留“%datetime”
        currFormat.erase(index, count);
        // m_dateTimeFormat的内容为"%Y-%M-%d %H:%m:%s.%g",即实际日期时间格式部分
        m_dateTimeFormat = ss.str();
    }
    else
    {
        // 格式无效,使用默认的日期时间格式"%Y-%M-%d %H:%m:%s,%g"
        // static const char* kDefaultDateTimeFormat="%Y-%M-%d %H:%m:%s,%g";
        //  No format provided, use default
        if (hasFlag(base::FormatFlags::DateTime))
        {
            m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
        }
    }
}

    updateFormatSpec 接口的定义如下:

// 替换FORMAT配置项字符串形式值当中日志级别格式串,用户格式串以及主机格式串为实际内容
void LogFormat::updateFormatSpec(void)
{
    // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
    if (m_level == Level::Debug)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kDebugLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kDebugLevelShortLogValue);
    }
    else if (m_level == Level::Info)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kInfoLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kInfoLevelShortLogValue);
    }
    else if (m_level == Level::Warning)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kWarningLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kWarningLevelShortLogValue);
    }
    else if (m_level == Level::Error)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kErrorLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kErrorLevelShortLogValue);
    }
    else if (m_level == Level::Fatal)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kFatalLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kFatalLevelShortLogValue);
    }
    else if (m_level == Level::Verbose)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kVerboseLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kVerboseLevelShortLogValue);
    }
    else if (m_level == Level::Trace)
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, base::consts::kTraceLevelLogValue);
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, base::consts::kTraceLevelShortLogValue);
    }

    if (hasFlag(base::FormatFlags::User))
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, m_currentUser);
    }

    if (hasFlag(base::FormatFlags::Host))
    {
        base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, m_currentHost);
    }
    // Ignore Level::Global and Level::Unknown
}

至此,LogFormat 类的实现就介绍完了,下一篇我们开始分析其他一些工具类。

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