同步異步日志系統
- 一、日志系統框架設計
- 1.1模塊劃分
- 1.1.1 日志等級模塊
- 1.1.2 日志消息模塊
- 1.1.3 日志消息格式化模塊
- 1.1.4 日志落地模塊(日志落地的方向是工廠模式)
- 1.1.5 日志器模塊(日志器的生成是建造者模式)
- 1.1.6 異步線程模塊(日志的輸出是用宏完成的代理模式)
- 1.1.7 單例的日志器管理模塊(單例模式實現對日志器的管理)
- 1.2 模塊關系圖
- 二、代碼設計
- 2.1 實用類設計
日志系統:
作用:將一條消息,進行格式化為指定格式的字符串后,寫入到指定位置
- 日志要寫入指定位置(標準輸出,指定文件, 滾動文件等等是可擴展得)
日志系統需要支持將日志消息落地到不同的位置—多落地方向- 日志寫入指定位置,支持不同的寫入方式(同步,異步)
同步:業務線程自己負責日志的寫入(流程簡單,但是有可能會因為阻塞導致效率降低) 異步:業務線程將日志放入緩沖區內存,讓其他異步線程負責將日志寫入指定位置- 日志輸出以日志器為單位,支持多日志器(不同的項目組有不同的輸出策略)
一、日志系統框架設計
本項?實現的是?個多?志器?志系統,主要實現的功能是讓程序員能夠輕松的將程序運??志信息落地到指定的位置,且?持同步與異步兩種?式的?志落地?式。
1.1模塊劃分
1.1.1 日志等級模塊
枚舉出日志分為多少個等級—對不同的日志有不同等級標記–以便于控制輸出
- OFF:關閉
- DEBUG:調試,調試時的關鍵信息輸出。
- INFO:提示,普通的提?型?志信息。
- WARN:警告,不影響運?,但是需要注意?下的?志。
- ERROR:錯誤,程序運?出現錯誤的?志。
- FATAL:致命,?般是代碼異常導致程序?法繼續推進運?的?志。
1.1.2 日志消息模塊
封裝一條日志所需的各種要素(時間,線程ID,文件名,行號,日志等級,消息主體…)
- 時間:描述本條?志的輸出時間。
- 線程ID:描述本條?志是哪個線程輸出的。
- ?志等級:描述本條?志的等級。
- ?志數據:本條?志的有效載荷數據。
- ?志?件名:描述本條?志在哪個源碼?件中輸出的。
- ?志?號:描述本條?志在源碼?件的哪??輸出的。
1.1.3 日志消息格式化模塊
按照指定的格式,對于日志消息中關鍵要素進行組織,最終得到一個指定格式的字符串
系統默認的輸出格式: [%d{%H:%M:%S}]%T[%t]%T[%p]%T[%c]%T%f:%l%T%m%n
[12:38:45] [12345678] [FATAL] [root] main.c:178 套接字創建失敗…\n
- %d{%H:%M:%S}:表??期時間,花括號中的內容表示日期時間的格式。
- %T:表?制表符縮進。
- %t:表?線程ID。%p:表??志級別。
- %c:表??志器名稱,不同的開發組可以創建??的?志器進??志輸出,?組之間互不影響。
- %f:表??志輸出時的源代碼?件名。
- %l:表??志輸出時的源代碼?號。
- %m:表?給與的?志有效載荷數據 。
- %n:表?換行。
- 設計思想:設計不同的?類,不同的?類從?志消息中取出不同的數據進?處理。
1.1.4 日志落地模塊(日志落地的方向是工廠模式)
決定了?志的落地?向,以什么方式輸出。
- 標準輸出:表?將?志進?標準輸出的打印。
- ?志?件輸出:表?將?志寫?指定的?件末尾。
- 滾動?件輸出:當前以?件??進?控制,當?個?志?件??達到指定??,則切換下?個?件進?輸出 。
- 后期,也可以擴展遠程?志輸出,創建客?端,將?志消息發送給遠程的?志分析服務器。
- 設計思想:設計不同的?類,不同的?類控制不同的?志落地?向。
1.1.5 日志器模塊(日志器的生成是建造者模式)
對上邊幾個模塊的整合,??通過?志器進??志的輸出,有效降低??的使? 難度。
?志消息落地模塊對象,?志消息格式化模塊對象,?志輸出等級
- 同步日志器模塊—完成日志的同步輸出功能。
- 異步日志器模塊—完成日志的異步輸出功能(將日志消息發送到日志緩沖器內存)
1.1.6 異步線程模塊(日志的輸出是用宏完成的代理模式)
負責異步日志的實際落地輸出功能
- 實現對?志的異步輸出功能,??只需要將輸出?志任務放?任務池,異步線程負責?志的落地輸出功能,提供了更加?效的?阻塞的?志輸出。
1.1.7 單例的日志器管理模塊(單例模式實現對日志器的管理)
為了方便管理,可以在程序的任意位置使用日志器,我設置一個單例對象。對日志進行全局的管理,以便于能夠在項目的任何位置獲取指定的日志器進行日志輸出。
- 為了降低項?開發的?志耦合,不同的項?組可以有??的?志器來控制輸出格式以及落地?向,因此本項?是?個多?志器的?志系統。
- 管理模塊就是對創建的所有?志器進?統?管理。并提供?個默認?志器,提供標準輸出的?志輸出。
1.2 模塊關系圖
通過模塊關系圖,能夠簡單快速的了解模塊之間的關系。
二、代碼設計
2.1 實用類設計
提前完成一些功能和接口。
- 首先需要把架子搭起來,功能先聲明好。
// 通?功能類,與業務?關的功能實現
// 1. 獲取系統時間
// 2. 判斷文件是否存在
// 3. 獲取?件所在?錄的路徑
// 4. 創建?錄
//靜態接口可以直接使用,不需要實例化對象
#include <iostream>
namespace logsLearn {namespace util{//日期類class Data{public://獲取系統時間static time_t now();};//文件類class File{public://判斷文件是否存在static bool exists(const std::string &pathname);//獲取?件所在?錄的路徑static std::string path(const std::string &pathname);//創建?錄static void createDirectory(const std::string &pathname);};}
}
- 其次在實現各個功能。
// 條件編譯,防止頭文件重復包含
#ifndef __M_UTIL_H__
#define __M_UTIL_H__
// 通?功能類,與業務?關的功能實現
// 1. 獲取系統時間
// 2. 判斷文件是否存在
// 3. 獲取?件所在?錄的路徑
// 4. 創建?錄
#include <iostream>
#include <ctime>
#include <sys/stat.h>
#include <sys/types.h>
namespace logsLearn
{namespace util{// 日期類class Data{public:// 獲取系統時間static time_t now(){// 返回當前系統時間return time(nullptr);}};// 文件類class File{public:// 判斷文件是否存在static bool exists(const std::string &pathname){ // 定義了一個stat的結構體變量struct stat st;// 獲取文件信息不成功,進入條件if (stat(pathname.c_str(), &st) < 0){return false;}return true;}// 獲取?件所在?錄的路徑static std::string path(const std::string &pathname){ if (pathname.empty()) return ".";// pos是當前查找的最后“/\\”的位置size_t pos = pathname.find_last_of("/\\");// 沒有找到"/\\"if (pos == std::string::npos)return ".";// 找到了,返回路徑,左閉右開原則,要想包括pos位置,需要加1return pathname.substr(0, pos + 1);}// 創建?錄static void createDirectory(const std::string &pathname){ // pos找到的位置,idx是存的位置size_t pos = 0, idx = 0;while (idx < pathname.size()){// 遍歷路徑每找到最后一個"/\\"創建文件夾size_t pos = pathname.find_first_of("/\\", idx);if (pos == std::string::npos){ // 創建文件夾,pathname.c_str()表示路徑名,0777表示權限mkdir(pathname.c_str(), 0777);}// 前面的路徑std::string parent_dir = pathname.substr(0, pos + 1);// 文件是否存在if (exists(parent_dir) == true){idx = pos + 1;continue;}// 文件不存在,創建文件mkdir(parent_dir.c_str(), 0777);idx = pos + 1;}}};}
}
#endif
- 我們每次編寫完一個模塊后,要對其進行單元測試,確保程序的準確性。
//測試代碼
#include "util.hpp"
int main()
{//工具類測試std::cout<<logsLearn::util::Data::now()<<std::endl;std::string pathname="./abc/bcd/a.txt";std::cout<<pathname;logsLearn::util::File::createDirectory(logsLearn::util::File::path(pathname));return 0;
}
- 界面展示
make運行之前
make運行之后
補充:
1.stat函數
stat函數是用于獲取文件信息,比如文件權限,文件類型信息等等。
函數的聲明:
int stat(const char *pathname, struct stat *buf);
參數說明:
pathname:表示文件路徑名
buf:用于保存獲取到的文件屬性信息(這是一個傳出參數)
返回值說明:成功返回0,失敗返回-1并設置errno
2.mkdir函數
mkdir函數用于創建一個新的目錄。如果指定的目錄已經存在,并且沒有設置相應的標志來允許覆蓋或忽略已存在的目錄,則函數會失敗;它允許用戶為新創建的目錄設置權限,這些權限決定了誰可以訪問該目錄。
函數聲明:
int mkdir(const char * pathname , mode_t mode);
參數說明:
pathname(const char * ):指向以null結尾的字符串的指針,該字符串指定了要創建的目錄的路徑。路徑可以是相對路徑或絕對路徑。
mode(mode_t):指定新目錄的權限。這些權限位使用八進制數表示(例如,0755),并且會受進程的文件模式創建掩碼(umask)的影響。最終權限是請求權限與umask的補碼的邏輯與結果。通常,mode參數包括文件所有者(user)、組(group)和其他人(other)的讀(r)、寫(w)和執行(x)權限的組合。
返回值說明:成功時,返回0;失敗時,返回-1,并設置全局變量errno以指示錯誤類型(如EACCES表示權限不足,EEXIST表示目錄已存在等)。