日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

muduo网络库学习(九)日志类Logger和LogStream,将日志信息打印到屏幕

發布時間:2024/4/19 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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_*語句所在的源文件名和行號,也是一個內部類。

class TimeZone;class Logger {public:/* 日志級別 */enum LogLevel{TRACE, /* 細粒度最高的信息 */DEBUG, /* 對調試有幫助的事件信息 */INFO, /* 粗粒度級別上強調程序的運行信息 */WARN, /* 程序能正常運行,但是有潛在危險的信息 */ERROR, /* 程序出錯,但是不影響系統運行的信息 */FATAL, /* 將導致程序停止運行的嚴重信息 */NUM_LOG_LEVELS, /* 日志級別個數 */};// compile time calculation of basename of source file/* 內部類,保存調用LOG_WARN<<之類的那條語句所在文件名 */class SourceFile{public:template<int N>inline SourceFile(const char (&arr)[N]): data_(arr),size_(N-1){const char* slash = strrchr(data_, '/'); // builtin functionif (slash){data_ = slash + 1;size_ -= static_cast<int>(data_ - arr);}}explicit SourceFile(const char* filename): data_(filename){const char* slash = strrchr(filename, '/');if (slash){data_ = slash + 1;}size_ = static_cast<int>(strlen(data_));}const char* data_;int size_;};Logger(SourceFile file, int line);Logger(SourceFile file, int line, LogLevel level);Logger(SourceFile file, int line, LogLevel level, const char* func);Logger(SourceFile file, int line, bool toAbort);~Logger();/* 返回Impl的LogStream對象 */LogStream& stream() { return impl_.stream_; }/* 返回日志級別及設置日志級別 */static LogLevel logLevel();static void setLogLevel(LogLevel level);/* 輸出函數,將日志信息輸出 */typedef void (*OutputFunc)(const char* msg, int len);/* 刷新緩沖區 */typedef void (*FlushFunc)();static void setOutput(OutputFunc);static void setFlush(FlushFunc);static void setTimeZone(const TimeZone& tz);private:/* * Impl技法,數據和對象分離* 通常應該是在類定義中聲明class Impl;* 然后創建Impl對象,Impl impl_;* 最后在.cpp文件中實現Logger::Impl的定義* * Impl對象存儲的就是Logger所需要的所有數據*/ class Impl {public:typedef Logger::LogLevel LogLevel;Impl(LogLevel level, int old_errno, const SourceFile& file, int line);void formatTime();void finish();/* UTC時間,記錄寫入日志的時間 */Timestamp time_;/* 將日志信息存在緩沖區中,使用LOG_WARN是會返回Logger().stream(),就是返回這個LogStream */LogStream stream_;/* 日志級別,TRACE, DEBUG, WARN... */LogLevel level_;int line_;/* * SourceFile也是Logger的內部類 why ?* 保存調用LOG_WARN<<語句的源文件名*/SourceFile basename_; };Impl impl_;};

Impl的功能不只局限在保存Logger的數據,同時也在被創建時為日志信息添加前綴,通常是日期,時間,線程號,級別等。注意,在創建Logger對象時,會調用Impl的構造函數,在構造函數中就已經將日志前綴寫入LogStream中,而調用stream函數返回LogStream對象時,日志正文信息就只會寫在后面,這也是想要的效果。
Impl構造函數如下,用于寫入各種前綴信息

/* Impl的構造函數,Impl主要負責日志的格式化 */ Logger::Impl::Impl(LogLevel level, int savedErrno, const SourceFile& file, int line): time_(Timestamp::now()), /* 當前時間 */stream_(), /* LogStream流 */level_(level), /* 日志級別 */line_(line), /* 調用LOG_* << 所在行,由__LINE__獲取 */basename_(file) /* 調用LOG_* << 所在文件名,由__FILE__獲取 */ {/* 格式化當前時間,寫入LogStream中 */formatTime();/* 緩存線程id到成員變量中,當獲取時直接返回 */CurrentThread::tid();/* 將線程id和日志級別寫入LogStream */stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());stream_ << T(LogLevelName[level], 6);/* 如果有錯誤,寫入錯誤信息 */if (savedErrno != 0){stream_ << strerror_tl(savedErrno) << " (errno=" << savedErrno << ") ";} }

由于是臨時對象,所以析構函數接管了所有日志輸出的任務

  • 為日志信息添加后綴,通常是源文件名和所在行號
  • 從LogStream的緩沖區中回去所有日志信息,包括前綴和后綴
  • 調用輸出函數,默認將日志信息打印到屏幕上
  • 如果日志級別是FATAL,那么會立即刷新緩沖區,同時發送abort終止程序運行
  • /* * Logger析構函數,將LogStream中的數據打印出來* 根據日志級別決定要不要立刻刷新*/ Logger::~Logger() {/* 當前日志輸出結束,添加后綴(所在文件名和行號) */impl_.finish();/* 從LogStream的Buffer中拿出日志信息 */const LogStream::Buffer& buf(stream().buffer());/* 將信息輸出到屏幕上 */g_output(buf.data(), buf.length());/* 如果當前日志級別是FATAL,表示是個終止程序的嚴重錯誤,將輸出緩沖區的信息刷新到屏幕上,結束程序 */if (impl_.level_ == FATAL){g_flush();abort();} }

    打印函數如下,默認打印到屏幕上,可以使用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,将日志信息打印到屏幕的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。