muduo网络库学习(九)日志类Logger和LogStream,将日志信息打印到屏幕
每一個成熟的項目都有大大小小的日志系統,在關鍵的地方打印日志信息,常用來跟蹤程序運行,查找錯誤原因等,可以節省大量的debug時間
muduo的日志信息有5個級別
- TRACE,細粒度最高的日志信息,打印的最詳細
- DEBUG,細粒度級別上對調試有幫助的日志信息
- INFO,粗粒度級別上強調程序的運行信息
- WARN,程序能正常運行,但存在潛在風險的信息
- ERROR,執行出錯,但不影響程序繼續執行的錯誤信息
- FATAL,將導致程序退出的嚴重信息
muduo的日志格式為
在muduo的很多源文件中,多存在著輸出日志信息的語句,例如
LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();LOG_DEBUG << "EventLoop created " << this << " in thread " << threadId_;LOG_INFO << "TcpServer::newConnection [" << name_<< "] - new connection [" << connName<< "] from " << peerAddr.toIpPort();LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP";LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";LOG_FATAL << "Another EventLoop " << t_loopInThisThread<< " exists in this thread " << threadId_;這些形如LOG_*的調用實際上是宏定義
/* * __FILE__:返回所在文件名* __LINE__:返回所在行數* __func__:返回所在函數名* * 這些都是無名對象,當使用LOG_* << "***"時,* 1.構造Logger類型的臨時對象,返回LogStream類型變量* 2.調用LogStream重載的operator<<操作符,將數據寫入到LogStream的Buffer中* 3.當前語句結束,Logger臨時對象析構,調用Logger析構函數,將LogStream中的數據輸出*/ #define LOG_TRACE if (muduo::Logger::logLevel() <= muduo::Logger::TRACE) \muduo::Logger(__FILE__, __LINE__, muduo::Logger::TRACE, __func__).stream() #define LOG_DEBUG if (muduo::Logger::logLevel() <= muduo::Logger::DEBUG) \muduo::Logger(__FILE__, __LINE__, muduo::Logger::DEBUG, __func__).stream() #define LOG_INFO if (muduo::Logger::logLevel() <= muduo::Logger::INFO) \muduo::Logger(__FILE__, __LINE__).stream() #define LOG_WARN muduo::Logger(__FILE__, __LINE__, muduo::Logger::WARN).stream() #define LOG_ERROR muduo::Logger(__FILE__, __LINE__, muduo::Logger::ERROR).stream() #define LOG_FATAL muduo::Logger(__FILE__, __LINE__, muduo::Logger::FATAL).stream() #define LOG_SYSERR muduo::Logger(__FILE__, __LINE__, false).stream() #define LOG_SYSFATAL muduo::Logger(__FILE__, __LINE__, true).stream()當使用LOG_*時,在編譯期會被宏定義后面的語句替換。而實際上是創建了Logger的臨時對象,創建后調用stream函數,可以猜測,stream函數返回的對象重載了operator << 函數,可以應對各種日志信息的輸出,函數定義如下,實際返回的是LogStream對象
/* 返回Impl的LogStream對象 */LogStream& stream() { return impl_.stream_; }LogStream對象重載了各種operator << 函數,可以處理多種類型的信息。而在LogStream中,存在著一個緩沖區Buffer用于保存這些日志信息
/* 使用FixedBuffer,默認緩沖區大小為kSmallBuffer,重載各種operator<<操作 */ class LogStream : noncopyable {typedef LogStream self;public:/* 緩沖區的類型,是個固定大小的緩沖區,由字符數組實現 */typedef detail::FixedBuffer<detail::kSmallBuffer> Buffer;/* 重載的operator<<函數,將日志信息存放在緩沖區中 */self& operator<<(const char* str){if (str){buffer_.append(str, strlen(str));}else{buffer_.append("(null)", 6);}return *this;}/* ... */private:/* 用于存儲日志信息的緩沖區 */Buffer buffer_;static const int kMaxNumericSize = 32; };對于臨時對象,所在語句結束后就被析構了,所以對于日志信息的輸出,肯定都交給Logger對象的析構函數處理了
Logger的定義如下,Logger定義6個日志級別,分別對應不同的LOG_*。同時使用了Impl技法將對象和數據分離,Impl保存著所有Logger需要的數據。SourceFile保存著調用LOG_*語句所在的源文件名和行號,也是一個內部類。
Impl的功能不只局限在保存Logger的數據,同時也在被創建時為日志信息添加前綴,通常是日期,時間,線程號,級別等。注意,在創建Logger對象時,會調用Impl的構造函數,在構造函數中就已經將日志前綴寫入LogStream中,而調用stream函數返回LogStream對象時,日志正文信息就只會寫在后面,這也是想要的效果。
Impl構造函數如下,用于寫入各種前綴信息
由于是臨時對象,所以析構函數接管了所有日志輸出的任務
打印函數如下,默認打印到屏幕上,可以使用Logger::setOuput和Logger::setFlush設置自定義打印和刷新函數,將日志信息重定向到文件等,這就涉及muduo另幾個日志類,用于寫入文件和滾動文件。
/** 默認輸出和刷新位置,將信息打印到屏幕上*/ void defaultOutput(const char* msg, int len) {size_t n = fwrite(msg, 1, len, stdout);//FIXME check n(void)n; }void defaultFlush() {fflush(stdout); }至此日志信息會打印到屏幕上,Logger臨時對象也被析構,程序繼續執行(如果不是FATAL的話)
總結
以上是生活随笔為你收集整理的muduo网络库学习(九)日志类Logger和LogStream,将日志信息打印到屏幕的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每天一道LeetCode-----给定字
- 下一篇: 每天一道LeetCode-----重新实