目录
在上一篇文章中我们介绍完了 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
类的实现就介绍完了,下一篇我们开始分析其他一些工具类。