目录
- el::base::Writer类
- el::base::NullWriter类
- el::LogMessage类
- el::Logger类
- el::base::MessageBuilder类
- el::base::DispatchAction枚举类型
在上一篇中我们分析了 CLOG
宏 日志信息保存 的流程,今天我们看看前面 CLOG
宏所使用到的一些相关类。
el::base::Writer类
另一个
construct
接口//声明 Writer &construct(Logger *logger, bool needLock = true); // 定义 Writer &Writer::construct(Logger *logger, bool needLock) { m_logger = logger; // initializeLogger前面已经介绍过了,这里就不多说了 initializeLogger(logger->id(), false, needLock); // 初始化m_messageBuilder m_messageBuilder.initialize(m_logger); return *this; }
这个接口可以使用
Logger
作为参数来初始化Writer
对象,而不必使用 Logger ID 作为参数在RegisteredLoggers
当中查找,提高效率。这个接口主要用于使用
Logger
的类printf
接口记录日志的场景中,后面介绍到这一块时,会详细分析。
el::base::NullWriter类
这个类重载的两个输出运算符就直接返回了对象本身,什么也没做,也就相当于不输出日志了。
class NullWriter : base::NoCopy
{
public:
NullWriter(void) {}
// Null manipulator
inline NullWriter &operator<<(std::ostream &(*)(std::ostream &))
{
return *this;
}
template <typename T>
inline NullWriter &operator<<(const T &)
{
return *this;
}
inline operator bool()
{
return true;
}
};
el::LogMessage类
主要用于作为其他类的成员或者函数参数,日志相关信息的 wrapper
类,都是一些成员函数的获取接口,没什么可说的。
class LogMessage
{
public:
LogMessage(Level level, const std::string &file, base::type::LineNumber line, const std::string &func,
base::type::VerboseLevel verboseLevel, Logger *logger) : m_level(level), m_file(file), m_line(line), m_func(func),
m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str())
{
}
inline Level level(void) const
{
return m_level;
}
inline const std::string &file(void) const
{
return m_file;
}
inline base::type::LineNumber line(void) const
{
return m_line;
}
inline const std::string &func(void) const
{
return m_func;
}
inline base::type::VerboseLevel verboseLevel(void) const
{
return m_verboseLevel;
}
inline Logger *logger(void) const
{
return m_logger;
}
inline const base::type::string_t &message(void) const
{
return m_message;
}
private:
Level m_level; // 日志级别
std::string m_file; // 日志所在的源文件
base::type::LineNumber m_line; // 日志所在的行号
std::string m_func; // 日志所在的函数
base::type::VerboseLevel m_verboseLevel; // 日志对应的详细级别
Logger *m_logger; // 日志对应的日志记录器
base::type::string_t m_message; // 日志的内容部分(消息格式指示符当中的%msg代表的内容)
};
el::Logger类
el:: Logger 被 el:: base:: writer 类使用,用于读取配置,不直接写日志
在 easylogging++的总体设计 中介绍过 el:: Logger
的一些成员变量,这里分析其部分接口。
isValidId接口
static const char *kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; // 检验 logger id 是否有效 bool Logger::isValidId(const std::string &id) { for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { return false; } } return true; }
base:: utils::Str
是 easylogging++内部封装的字符操作的工具类,日志记录器的id
必须是kValidLoggerIdSymbols
当中的字符flush接口
1、
void Logger:: flush(void);
// 刷新日志记录器的所有日志级别的日志文件 void Logger::flush(void) { ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); base::threading::ScopedLock scopedLock(lock()); base::type::EnumType lIndex = LevelHelper::kMinValid; LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { flush(LevelHelper::castFromInt(lIndex), nullptr); return false; // 返回false是为了让每个日志级别都能够flush }); }
上面
LevelHelper:: forEachLevel
的实现如下:// 从指定级别开始遍历,对于每个级别执行一些操作(fn) void LevelHelper::forEachLevel(base::type::EnumType *startIndex, const std::function<bool(void)> &fn) { base::type::EnumType lIndexMax = LevelHelper::kMaxValid; do { if (fn()) { break; } *startIndex = static_cast<base::type::EnumType>(*startIndex << 1); } while (*startIndex <= lIndexMax); } //flush中调用LevelHelper::forEachLevel传递的fn为匿名lamba表达式: [&](void) ->bool { // 刷新日志记录器的指定日志级别的日志文件 flush(LevelHelper::castFromInt(lIndex), nullptr); return false; }
2、
void flush(Level level, base:: type:: fstream_t *fs);
// 刷新日志记录器的指定日志级别的日志文件 void Logger::flush(Level level, base::type::fstream_t *fs) { if (fs == nullptr && m_typedConfigurations->toFile(level)) { // 获取日志级别对应的文件流 fs = m_typedConfigurations->fileStream(level); } if (fs != nullptr) { // 文件存在则刷新文件 fs->flush(); // 重置日志级别对应的未刷新次数 std::unordered_map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level); if (iter != m_unflushedCount.end()) { iter->second = 0; } // 检查日志级别对应的日志文件是否要进行日志回旋 Helpers::validateFileRolling(this, level); } }
initUnflushedCount接口
initUnflushedCount
主要用于初始化日志记录器的所有日志级别对应的日志文件的未刷新次数void Logger::initUnflushedCount(void) { m_unflushedCount.clear(); base::type::EnumType lIndex = LevelHelper::kMinValid; LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); return false; }); }
LevelHelper:: forEachLevel
的作用上面已经介绍过了,这里就不多说了。
el::base::MessageBuilder类
用于支持各种类型的日志输出
成员变量
Logger* m_logger; //日志记录器 const base::type::char_t* m_containerLogSeparator; //输出容器时,各个元素之间的分隔符。
成员函数
主要是重载了各种类型的输出运算符,同时支持流操控符(如
std:: endl
)支持流操控符
inline MessageBuilder &operator<<(std::ostream &(*OStreamMani)(std::ostream &)) { m_logger->stream() << OStreamMani; return *this; }
支持内置类型
// char ELPP_SIMPLE_LOG(char) // bool ELPP_SIMPLE_LOG(bool) // signed short ELPP_SIMPLE_LOG(signed short) // unsigned short ELPP_SIMPLE_LOG(unsigned short) // signed int ELPP_SIMPLE_LOG(signed int) // unsigned int ELPP_SIMPLE_LOG(unsigned int) // signed long ELPP_SIMPLE_LOG(signed long) // unsigned long ELPP_SIMPLE_LOG(unsigned long) // float ELPP_SIMPLE_LOG(float) // double ELPP_SIMPLE_LOG(double) // char* ELPP_SIMPLE_LOG(char *) // const char* ELPP_SIMPLE_LOG(const char *) // const void* ELPP_SIMPLE_LOG(const void *) // long double ELPP_SIMPLE_LOG(long double)
ELPP_SIMPLE_LOG
宏,前面日志信息保存流程中已经介绍过了。支持std::string 类型
inline MessageBuilder &operator<<(const std::string &msg) { return operator<<(msg.c_str()); }
支持std::wstring 类型
inline MessageBuilder &operator<<(const std::wstring &msg) { return operator<<(msg.c_str()); }
支持wchar_t*类型
MessageBuilder &MessageBuilder::operator<<(const wchar_t *msg) { if (msg == nullptr) { m_logger->stream() << base::consts::kNullPointer; return *this; } #if defined(ELPP_UNICODE) m_logger->stream() << msg; #else // 非unicode时,将wchar*转为char* char *buff_ = base::utils::Str::wcharPtrToCharPtr(msg); m_logger->stream() << buff_; free(buff_); #endif if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { m_logger->stream() << " "; } return *this; }
支持容器相关类型
// 支持一个模板参数的容器 #define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ template <typename T> \ inline MessageBuilder &operator<<(const temp<T> &template_inst) \ { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ } // 支持2个模板参数的容器 #define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ template <typename T1, typename T2> \ inline MessageBuilder &operator<<(const temp<T1, T2> &template_inst) \ { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ } // 支持3个模板参数的容器 #define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ template <typename T1, typename T2, typename T3> \ inline MessageBuilder &operator<<(const temp<T1, T2, T3> &template_inst) \ { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ } // 支持4个模板参数的容器 #define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ template <typename T1, typename T2, typename T3, typename T4> \ inline MessageBuilder &operator<<(const temp<T1, T2, T3, T4> &template_inst) \ { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ } // 支持5个模板参数的容器 #define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ template <typename T1, typename T2, typename T3, typename T4, typename T5> \ inline MessageBuilder &operator<<(const temp<T1, T2, T3, T4, T5> &template_inst) \ { \ return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ }
上面这几个宏内部都是委托给
writeIterator
这个成员函数模板来实现的:template <class Iterator> MessageBuilder &writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { m_logger->stream() << ELPP_LITERAL("["); for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { operator<<(*begin_); m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeparator : ELPP_LITERAL("")); } if (begin_ != end_) { m_logger->stream() << ELPP_LITERAL("..."); } m_logger->stream() << ELPP_LITERAL("]"); if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { m_logger->stream() << " "; } return *this; }
实现也不复杂,就是遍历容器对应的迭代器,依次保存在
m_logger
对应的stream
(字符串流对象)中。相关容器的宏使用:
//STL容器 ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset)>>> //boost容器 ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set)>>> //QT容器 ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack)>>> //WXWIDGETS容器 ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector)
可以对照相关文档看这些容器为啥分别用的这些宏,主要是不同容器实际的模板参数个数的区别。
对于其他的一些类型,el::base::MessageBuilde
r 提供了相应类型的输出运算符的重载,因为实现和上面类似,这里只是列出了声明:// std::pair template <class First, class Second> MessageBuilder &operator<<(const std::pair<First, Second> &pair_); // std::bitset template <std::size_t Size> MessageBuilder &operator<<(const std::bitset<Size> &bitset_); // std::array template <class T, std::size_t Size> inline MessageBuilder &operator<<(const std::array<T, Size> &array); // QString inline MessageBuilder &operator<<(const QString &msg); // QByteArray inline MessageBuilder &operator<<(const QByteArray &msg); // QStringRef inline MessageBuilder &operator<<(const QStringRef &msg); // qint64 inline MessageBuilder &operator<<(qint64 msg); // quint64 inline MessageBuilder &operator<<(quint64 msg); // QChar inline MessageBuilder &operator<<(QChar msg); // QLatin1String inline MessageBuilder &operator<<(const QLatin1String &msg); // QPair<First, Second> template <typename First, typename Second> MessageBuilder &operator<<(const QPair<First, Second> &pair_); // QMap<K, V> template <typename K, typename V> MessageBuilder &operator<<(const QMap<K, V> &map_); // QMultiMap<K, V> template <typename K, typename V> inline MessageBuilder &operator<<(const QMultiMap<K, V> &map_); // QHash<K, V> template <typename K, typename V> MessageBuilder &operator<<(const QHash<K, V> &hash_); // QMultiHash<K, V> template <typename K, typename V> inline MessageBuilder &operator<<(const QMultiHash<K, V> &multiHash_);
支持WXWIDGETS 相关类型
#define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) #define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) #define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")")
MAKE_CONTAINERELPP_FRIENDLY
宏定义如下:#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ el::base::type::ostream_t &operator<<(el::base::type::ostream_t &ss, const ContainerType &container) \ { \ const el::base::type::char_t *sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); \ ContainerType::const_iterator elem = container.begin(); \ ContainerType::const_iterator endElem = container.end(); \ std::size_t size_ = container.SizeMethod; \ ss << ELPP_LITERAL("["); \ for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) \ { \ ss << ElementInstance; \ ss << ((i < size_ - 1) ? sep : ELPP_LITERAL("")); \ } \ if (elem != endElem) \ { \ ss << ELPP_LITERAL("..."); \ } \ ss << ELPP_LITERAL("]"); \ return ss; \ }
实现也不复杂,就是遍历容器对应的迭代器,依次保存在
ss
流中。
之所以这里还需要ElementInstance
这个宏参数,从上面的使用可以看到,WXWIDGETS
相关容器类型的元素值的获取方式是有区别的,有的是一个解引用,有的是两次解引用,还有的希望定制化输出对应元素的值,所以这里提供了出来一个宏参数,让使用者来决定如何针对容器元素定制化输出。其他类型的支持
template <class Class> ELPP_SIMPLE_LOG(const Class &)
上面这个宏展开后为:
template <class Class> MessageBuilder &operator<<(const Class &msg) { m_logger->stream() << msg; // 需要Class类型支持<<运算符 if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { m_logger->stream() << " "; } return *this; }
el::base::DispatchAction枚举类型
enum class DispatchAction : base::type::EnumType
{
None = 1,
NormalLog = 2, // 用户日志
SysLog = 4 // 系统 syslog 日志(需要系统支持 syslog 日志)
};
CLOG
宏的实现到这里就介绍完了,下一篇我们继续介绍其他日志记录宏的实现。