muduo中的日誌系統
簡單總結一下muduo中的日誌系統的功能運作方式及一些疑問。
第一個類:
template<int SIZE> class FixedBuffer
成員變數:
1. 成員函式: 一個函式型別的成員 void (*cookie_)(),用來給記憶體塊做cookie。
2. 一個緩衝區data_[SIZE], 其緩衝區大小由類模板引數決定(這裡是一個非型別模板引數)。
3. 一個指示緩衝區當前位置的指標 char* cur_;
功能:
1.緩衝區的管理,包括緩衝區的各種引數的獲取,將新內容寫入緩衝區等
voidappend(const char* buf, size_t len) { //如果 剩餘的空間還夠用 if(implicit_cast<size_t>(avail()) > len){ //這是google提供的一個型別轉換函式 有什麼不同呢 //將內容加入緩衝區 memcpy(cur_, buf, len); cur_ += len; } }
從函式程式碼可知,往緩衝區寫入資料提供的第一級的寫入函式,只接受一個char型別的緩衝區以及緩衝區長度。後續如果要寫入這個緩衝區就需要先轉換為這種格式。
2. 給記憶體塊加cookie。
加cookie的目的主要是為了當程式崩潰的時候,還沒來得及寫入的日誌可以通過dump core檔案搜尋cookie得到。
實現的形式是 在類的建構函式中將函式的地址賦給cookie變數 在解構函式中可進行同樣的操作。
總結:這個類就是實現緩衝區的生成,各種基本功能函式的。
第二個類:
class LogStream
這個類是實行流控制的類。
成員變數:
1. 一個FixedBuffer型別的類,相當於管理了一個記憶體塊。
2. 一個static 的變數, static const omt kMaxNumericSize = 32; 這個變數記錄變數記錄的最大的有效數字的位數。
功能: 主要實現的功能是 將各種型別的資料型別進行轉換使其可以非常方便的被寫入到緩衝區中。
下面挑幾個典型的函式實現進行說明:
LogStream& LogStream::operator<<(short v) { *this << static_cast<int>(v); return *this; }
這個將 short轉換成了 int 然後再呼叫int對應的<<運算子。
int對應的<<運算子:
LogStream& LogStream::operator<<(int v) { formatInteger(v); return *this; }
之前說過FixedBuffer緩衝區寫入資料的格式是特定的,以上存在一個轉換函式:formatInteger
formatInteger函式:
template<typename T> void LogStream::formatInteger(T v) { if(buffer_.avail() >= kMaxNumericSize) { size_t len = convert(buffer_.current(), v); buffer_.add(len); //改變當前位置指標標誌 } }
這裡又用到了convert函式:
template<typename T> size_t convert(char buf[], T value) { T i = value; char* p = buf; do { int lsd = static_cast<int>(i % 10); i /= 10; *p++ = zero[lsd]; }while(i != 0); if(value < 0) { *p++ = '-'; } *p = '\0'; std::reverse(buf, p); return p - buf; //返回新加入的字串的長度 } // end of convert
就是將一個數字轉化為字串型別並寫入到緩衝區中
通過上面的一串操作完善了過載操作符 << 的功能:
其它資料型別 ----》int型別-----》formatInteger函式-------------》呼叫convert 將int逐位轉換為字元型別病新增到緩衝區中
|
|----------》調整緩衝區當前位置計數器
奇怪的是以上的轉換過程並沒有呼叫之前預留好的append函式。
第三個類:
通過上面的兩個類已經能夠實現通過過載操作符的形式將資料寫入到緩衝區中,接下來的類要解決的問題是,做一個比較好的寫入介面以及將緩衝區裡面的資料寫入到檔案中去。
class Logger
這個類裡面自己定義了一個類 搞得很攪,先來看看這個內建的類:
class Impl { public: //日誌等級型別資訊 typedef Logger::LogLevel LogLevel; Impl(LogLevel level, int old_errno, const SourceFile& file, int line);void finish(); //寫入日誌緩衝區的流控制 LogStream stream_; //日誌等級 LogLevel level_; int line_; //應該是檔名 SourceFile basename_; };
這個類是在Logger中作為私有成員被定義的。 看其名字就知道這個類是Logger的大管家,核心功能都在這個類中實現。
成員變數:
1. stream_ 寫入日誌快取的流控制類
2. level_記錄日誌的等級
3. line_日誌發生在原始檔的那一行
4. 產生日誌的原始檔名稱, 這個原始檔是專門用一個類來管理的,後面再說。
功能:
在建構函式和finish函式中完成了大部分的功能:
建構函式:
寫入日誌的頭部資訊 包括執行緒的程序id 日誌級別 等資訊
finish函式:
寫入輸出日誌的程式碼的原始檔和在原始檔中的行數。like this:
14124 INFO trace - test.cc:15
下面來看看那個管理檔案的類:
class SourceFile { public: template<int N> SourceFile(const char (&arr)[N]) //代理了一個緩衝區 : data_(arr), //代理這段緩衝區 size_(N-1) { //為什麼要去掉第一個 / 之前的位置呢 ????????? const char* slash = strrchr(data_, '/'); //slash 是 最後一個 / 出現的位置 if(slash) { data_ = slash + 1; } size_ = static_cast<int>(strlen(data_)); } 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_; }; // SourceFile
成員變數:
1. 一個指向檔名的指標data_,建構函式傳入引用,然後再通過引用將首地址賦值給它。 data_ : 可訪問待操作的檔案。
2. size_檔名的大小
功能:將傳入的儲存有任意檔案路徑的 原始檔名更改為只剩下原始檔名,例如: /home/muduo/test.cc 轉化為 ---》test.cc
總結: 這就是一個功能函式,完全由它的建構函式實現了他的功能。
下面開始介紹今天的主角:
class Logger
成員變數:
1. Impl impl_; 大管家類 裡面包含了 SourceFile
功能:
建構函式: 初始化了一些成員變數,初始化了impl_ 並在流中寫入了一些基本資訊。
解構函式:將緩衝區的內容通過註冊的輸出函式輸出到對應的位置,可以是標準輸出或檔案 根據具體的功能函式決定。