easylogging++日志库流式日志的写入,依赖于el::base::Writer类的析构,以debug日志为例:具体代码如下:
#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) #define CLOG(LEVEL, ...) C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) #if ELPP_DEBUG_LOG # define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) #define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
宏替换之后就是调用了Writer类的一个构造函数和一个construct()成员函数,等同于下面代码:
el::base::Writer(...).construct(...)
单独调用一个构造函数的话,会产生一个临时对象,在语句结束后,这个临时对象会被析构,进而触发析构函数中的日志写入逻辑,可以通过下列代码进行验证:
1 #include <iostream> 2 #include <sstream> 3 4 class A 5 { 6 public: 7 A() { } 8 9 ~A() 10 { 11 std::cout << m_ss.str() << std::endl; 12 } 13 14 template<typename T> 15 A& operator << (const T& t) 16 { 17 m_ss << t; 18 return *this; 19 } 20 21 private: 22 std::ostringstream m_ss; 23 }; 24 25 #define SLOG() LOG(A) 26 27 #define LOG(a) 28 a() 29 30 class B 31 { 32 public: 33 B() {} 34 ~B() 35 { 36 std::cout << "~B()" << std::endl; 37 } 38 }; 39 40 int main() 41 { 42 std::cout << ".........begin........." << std::endl; 43 SLOG() << "first...."; 44 B(); 45 std::cout << "..........middle......." << std::endl; 46 B b; 47 b = B(); 48 std::cout << ".........end.........." << std::endl; 49 return 0; 50 }
以上代码编译输出结果如下:
$ g++ macro.cpp -o main $ ./main .........begin......... first.... ~B() ..........middle....... ~B() .........end.......... ~B()
begin和middle之间两次单独调用构造函数的地方产生的临时对象,都在语句结束后被析构了,在middle和end之间,47行处,调用了构造函数构造出一个临时变量,然后通过赋值构造函数赋值给变量b,随即临时变量被析构,而变量b直到main函数结束才被析构掉。
总而言之,要对easylogging++做一层封装并保持其流式日志的特性,可以通过同样的方式,在析构函数上做手脚。