協程庫項目—日志模塊

日志模塊程序結構圖

sylarLog
├── LogLevel(日志級別封裝類)
│   ├── 提供“從日志級別枚舉值轉換到字符串”、“從字符串轉換相應的日志級別枚舉值”等方法
├── LogEvent(日志事件類)
│   ├── 封裝日志事件的屬性,例如時間、線程id、日志等級、內容等等,并對外提供訪問方法
│   └── 日志事件的構造在使用上會通過宏定義來簡化
├── LogEventWrap(日志事件包裝類)
│   ├── 內含日志事件 LogEvent
│   └── 日志事件在析構時由日志器進行輸出
├── LogFormatter(日志格式類)
│   ├── 通過傳遞日志樣式字符串給該類,該類對傳入的字符串進行解析,例如 %d%t%p%m%n 表示 時間、線程號、日志等級、內容、換行
│   ├── 內含一個虛基類-日志內容格式化項 FormatItem
│   ├── 有13個子類,消息-MessageFormatItem、日志級別-LevelFormatItem、累計毫秒數-ElapseFormatItem、日志名稱-NameFormatItem、線程id-ThreadIdFormatItem、換行-NewLineFormatItem、時間-DateTimeFormatItem、文件名-FilenameFormatItem、行號-LineFormatItem、Tab-TabFormatItem、協程id-FiberIdFormatItem、線程名稱-ThreadNameFormatItem、直接打印字符串-StringFormatItem
│   └── 整個日志模塊最復雜的邏輯就是該類解析日志樣式的函數 init()
├── LogAppender(日志輸出目的地類)
│   ├── LogAppender 為虛基類,有純虛函數,留給子類去各自實現
│   ├── 實現的子類如 StdoutLogAppender 和 FileLogAppender
│   └── Appender 自帶一個默認的 LogFormatter,以默認方式輸出
├── StdoutLogAppender(標準化輸出類)
│   └── 日志輸出到控制臺
├── FileLogAppender(文件輸出類)
│   └── 日志輸出到相應的文件中
├── Logger(日志器類)
│   ├── 設置日志名稱、設置日志等級 LogLevel、設置日志輸出位置 LogAppender、設置日志格式、根據日志級別控制日志輸出等
├── LoggerManager(日志管理器類)
│   ├── 利用 map 存放各個 Logger 實例,其中 key 是日志器的名稱,value 是日志器的智能指針
│   └── 還內含一個主日志器 root
└── 其他說明├── 每個類都 typedef std::shared_ptr ptr,方便外界使用其智能指針└── 普遍使用 Spinlock 實現互斥,保證線程安全。Spinlock 比 普通的 Mutex 效率高,但耗CPU。

數據流轉

  1. 首先通過SYLAR_LOG_NAME(name)宏從LoggerMgr中獲取對應的Logger對象,然后通過SYLAR_LOG_DEBUG(logger)->SYLAR_LOG_LEVEL(logger,level)宏創建一個新的LogEvent對象,并將其傳遞給LogEventWrap臨時對象。接著,通過std::stringstream將日志內容存入其中。
  2. 當LogEventWrap臨時對象析構時,會調用Logger的log方法,遍歷其所有的LogAppender,并調用每個LogAppender的log方法(傳入event參數)。
  3. 這里以FileLogAppender為例,LogAppender的log方法會加上自己的std::ostream參數(如果是輸出到控制臺,則是std::cout),然后調用LogFormatter的format方法(傳入ostream、event參數)。
  4. LogFormatter的format方法會遍歷自己緩存的所有FormatItem(繼承了FormatItem的各種子類智能指針),將日志內容格式化(例如加上時間日期、線程id等)。
  5. 調用的是每個FormatItem的format方法(傳入ostream、event參數)。最后,每個FormatItem的format方法會將格式化后的內容以流式方式存入std::ostream。如果是輸出到控制臺,那么這里就直接輸出了。如果是文件,因為std::ostream關聯了文件,因此會對文件進行緩存寫(非實時寫)。

LogFormatter類的init方法

LogFormatter類的init方法,用于解析日志格式字符串。主要功能如下:

首先,定義了一個patterns向量,用于存儲解析到的模式項。每個模式項包括一個整數類型和一個字符串,類型為0表示該模式是常規字符串,為1表示該模式需要轉義。
定義了一個臨時變量tmp,用于存儲常規字符串。
定義了一個日期格式字符串dateformat,默認把位于%d后面的大括號對里的全部字符都當作格式字符,不校驗格式是否合法。
定義了一個布爾變量error,用于標記解析過程中是否出錯。
定義了兩個布爾變量parsing_string和parsing_pattern,分別表示是否正在解析常規字符和模板字符。初始時,parsing_string為true。
使用一個循環遍歷m_pattern字符串中的每個字符,根據不同的情況進行解析。
如果當前字符是"%“,則根據parsing_string的值進行不同的處理。如果正在解析常規字符,則將之前的常規字符串添加到patterns中,并將parsing_string設置為false;如果正在解析模板字符,則將當前的”%“作為模板字符添加到patterns中,并將parsing_string設置為true。
如果當前字符不是”%“,則根據parsing_string的值進行不同的處理。如果正在解析常規字符,則將當前字符添加到tmp中;如果正在解析模板字符,則將當前字符作為模板字符添加到patterns中,并根據不同情況進行特殊處理。
在解析模板字符的過程中,如果遇到”%d",則需要進一步解析日期格式字符串。通過遍歷后續字符,直到找到閉合的大括號,將其中的字符添加到dateformat中。
如果在解析過程中出現錯誤,將m_error設置為true并返回。
最后,根據解析得到的模式項創建相應的格式化項對象,并將其添加到m_items中。
如果解析過程中出現錯誤,將m_error設置為true并返回。

void LogFormatter::init() {// 按順序存儲解析到的pattern項// 每個pattern包括一個整數類型和一個字符串,類型為0表示該pattern是常規字符串,為1表示該pattern需要轉義// 日期格式單獨用下面的dataformat存儲std::vector<std::pair<int, std::string>> patterns;// 臨時存儲常規字符串std::string tmp;// 日期格式字符串,默認把位于%d后面的大括號對里的全部字符都當作格式字符,不校驗格式是否合法std::string dateformat;// 是否解析出錯bool error = false;// 是否正在解析常規字符,初始時為truebool parsing_string = true;// 是否正在解析模板字符,%后面的是模板字符// bool parsing_pattern = false;size_t i = 0;while(i < m_pattern.size()) {std::string c = std::string(1, m_pattern[i]);if(c == "%") {if(parsing_string) {if(!tmp.empty()) {patterns.push_back(std::make_pair(0, tmp));}tmp.clear();parsing_string = false; // 在解析常規字符時遇到%,表示開始解析模板字符// parsing_pattern = true;i++;continue;} else {patterns.push_back(std::make_pair(1, c));parsing_string = true; // 在解析模板字符時遇到%,表示這里是一個%轉義// parsing_pattern = false;i++;continue;}} else { // not %if(parsing_string) { // 持續解析常規字符直到遇到%,解析出的字符串作為一個常規字符串加入patternstmp += c;i++;continue;} else { // 模板字符,直接添加到patterns中,添加完成后,狀態變為解析常規字符,%d特殊處理patterns.push_back(std::make_pair(1, c));parsing_string = true; // parsing_pattern = false;// 后面是對%d的特殊處理,如果%d后面直接跟了一對大括號,那么把大括號里面的內容提取出來作為dateformatif(c != "d") {i++;continue;}i++;if(i < m_pattern.size() && m_pattern[i] != '{') {continue;}i++;while( i < m_pattern.size() && m_pattern[i] != '}') {dateformat.push_back(m_pattern[i]);i++;}if(m_pattern[i] != '}') {// %d后面的大括號沒有閉合,直接報錯std::cout << "[ERROR] LogFormatter::init() " << "pattern: [" << m_pattern << "] '{' not closed" << std::endl;error = true;break;}i++;continue;}}} // end while(i < m_pattern.size())if(error) {m_error = true;return;}// 模板解析結束之后剩余的常規字符也要算進去if(!tmp.empty()) {patterns.push_back(std::make_pair(0, tmp));tmp.clear();}// for debug // std::cout << "patterns:" << std::endl;// for(auto &v : patterns) {//     std::cout << "type = " << v.first << ", value = " << v.second << std::endl;// }// std::cout << "dataformat = " << dateformat << std::endl;static std::map<std::string, std::function<FormatItem::ptr(const std::string& str)> > s_format_items = {
#define XX(str, C)  {#str, [](const std::string& fmt) { return FormatItem::ptr(new C(fmt));} }XX(m, MessageFormatItem),           // m:消息XX(p, LevelFormatItem),             // p:日志級別XX(c, LoggerNameFormatItem),        // c:日志器名稱
//        XX(d, DateTimeFormatItem),          // d:日期時間XX(r, ElapseFormatItem),            // r:累計毫秒數XX(f, FileNameFormatItem),          // f:文件名XX(l, LineFormatItem),              // l:行號XX(t, ThreadIdFormatItem),          // t:編程號XX(F, FiberIdFormatItem),           // F:協程號XX(N, ThreadNameFormatItem),        // N:線程名稱XX(%, PercentSignFormatItem),       // %:百分號XX(T, TabFormatItem),               // T:制表符XX(n, NewLineFormatItem),           // n:換行符
#undef XX};//根據解析得到的模式項創建相應的格式化項對象,并將其添加到m_items中。for(auto &v : patterns) {if(v.first == 0) {m_items.push_back(FormatItem::ptr(new StringFormatItem(v.second)));} else if( v.second =="d") {m_items.push_back(FormatItem::ptr(new DateTimeFormatItem(dateformat)));} else {auto it = s_format_items.find(v.second);if(it == s_format_items.end()) {std::cout << "[ERROR] LogFormatter::init() " << "pattern: [" << m_pattern << "] " << "unknown format item: " << v.second << std::endl;error = true;break;} else {m_items.push_back(it->second(v.second));}}}if(error) {m_error = true;return;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/715412.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/715412.shtml
英文地址,請注明出處:http://en.pswp.cn/news/715412.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Unity(第二十一部)動畫的基礎了解(感覺不了解其實也行)

1、動畫組件老的是Animations 動畫視頻Play Automatically 是否自動播放Animate Physics 驅動方式&#xff0c;勾選后是物理驅動Culling Type 剔除方式 默認總是動畫化就會一直執行下去&#xff0c;第二個是基于渲染播放&#xff08;離開鏡頭后不執行&#xff09;&#xff0c; …

高性能服務系列【二】CPU和內存

現代計算機的系統架構十分復雜。在服務器中&#xff0c;雙路處理器已經十分常見。最近Arm處理器實現雙路共384核心&#xff0c;要知道目前Linux內核最高只支持256核&#xff0c;這就有點尷尬。 多路處理器將越來越普遍&#xff0c;對性能的影響和傳統架構有不小的差別&#xf…

MySQL中json類型的字段

有些很復雜的信息&#xff0c;我們一般會用擴展字段傳一個json串&#xff0c;字段一般用text類型存在數據庫。mysql5.7以后支持json類型的字段&#xff0c;還可以進行sql查詢與修改json內的某個字段的能力。 1.json字段定義 ip_info json DEFAULT NULL COMMENT ip信息, 2.按…

GO基本類型一些記錄

基本類型一些記錄 1.直接粘貼文本進println(" ")2.中文字符串長度別用len( )3.byte本質是uint8 1.直接粘貼文本進println(" ") GoLand會自動補充轉義符 2.中文字符串長度別用len( ) 用相應編碼庫的方法&#xff0c;一般utf8即可 utf8.RuneCountInStrin…

PySide6實現word轉化pdf

目錄 一:實現思路 二:實現代碼 三:完整代碼和界面 一:實現思路 利用PySide6創建兩個按鈕和一個顯示區域,一個選擇文件按鈕,一個轉化按鈕和信息展示,操作文件按鈕選擇一個待轉化的word文檔。并且展示文件路徑到信息展示區,操作轉化按鈕,讀取選擇的文件轉化為pdf。并…

ThreadLocal介紹

文章目錄 ThreadLocal源碼分析&#xff1a;set方法get方法remove方法 ThreadLocal內存泄漏問題 ThreadLocal ThreadLocal提供了線程局部變量&#xff0c;每個線程都可以通過set和get方法來對這個變量進行操作&#xff0c;但不會和其他線程的局部變量沖突&#xff0c;實現了線程…

Doris實戰——拈花云科的數據中臺實踐

目錄 前言 一、業務背景 二、數據中臺1.0—Lambda 三、新架構的設計目標 四、數據中臺2.0—Apache Doris 4.1 新架構數據流轉 4.2 新架構收益 五、新架構的落地實踐 5.1 模型選擇 5.1.1 Unique模型 5.1.2 Aggregate模型 5.2 資源管理 5.3 批量建表 5.4 計算實現…

Stable Diffusion 模型分享:Realistic Stock Photo(真實的庫存照片)

本文收錄于《AI繪畫從入門到精通》專欄&#xff0c;專欄總目錄&#xff1a;點這里。 文章目錄 模型介紹生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下載地址 模型介紹 條目內容類型大模型基礎模型SDXL 1.0來源CIVITAI作者PromptSharingSamaritan文件名稱reali…

原生GO開發的博客系統

Go博客實戰教程&#xff0c;是一個練手級項目教程&#xff0c;使用原生Go開發&#xff0c;未使用任何框架。 如何使用原生Go開發一個web項目 循序漸進&#xff0c;掌握編程思維和思路 初步具有工程思維&#xff0c;能適應一般的開發工作 1. 搭建項目 package mainimport (&q…

Vue3_2024_1天【Vue3創建和響應式,對比Vue2】

前言&#xff1a; Vue3對比Vue2版本&#xff0c;它在性能、功能、易用性和可維護性方面都有顯著的提升和改進。 性能優化&#xff1a;模板編譯器的優化、對Proxy的支持以及使用了更加高效的Virtual DOM算法等。這使得Vue3的打包大小減少了41%&#xff0c;初次渲染提速55%&#…

【MATLAB源碼-第153期】基于matlab的OFDM系統插入導頻和訓練符號兩種信道估計方式誤碼率對比仿真。

操作環境&#xff1a; MATLAB 2022a 1、算法描述 OFDM&#xff08;Orthogonal Frequency Division Multiplexing&#xff0c;正交頻分復用&#xff09;是一種高效的無線信號傳輸技術&#xff0c;廣泛應用于現代通信系統&#xff0c;如Wi-Fi、LTE和5G。OFDM通過將寬帶信道劃分…

使用docker方式測試部署django項目(客戶催)

需求 1&#xff1a;已有django項目–weidanyewu 2&#xff1a;希望在服務器上測試部署–客戶催 3&#xff1a;沒完善django的啟動 4&#xff1a;使用臨時數據庫進行演示 5&#xff1a;使用python3.10版本鏡像 6&#xff1a;展示端口80 7&#xff1a;后臺執行django程序 8&#…

【C語言】熟悉文件順序讀寫函數

前言 本篇詳細介紹了 文件順序讀寫常用函數&#xff0c;快來看看吧~ 歡迎關注個人主頁&#xff1a;逸狼 創造不易&#xff0c;可以點點贊嗎~ 如有錯誤&#xff0c;歡迎指出~ 目錄 前言 ?編輯 文件順序讀寫函數 fgetc函數 示例 fputc函數 逐個字符寫入 寫入26個字母 文…

手寫模擬器,解放雙手!效果炸裂的生產工具

手寫模擬器是一款基于Handright的仿手寫圖片生成軟件&#xff0c;可以讓你的電腦和手機也能寫出漂亮的手寫字&#xff0c;你只需要輸入你想要寫的內容&#xff0c;選擇你喜歡的字體和背景&#xff0c;就可以生成一張高仿真的手寫圖片&#xff0c;用于各種場合&#xff0c;比如做…

uniapp中canvas的基礎使用

canvas簡介 canvas是uniapp中提供的一個組件,用于生成自定義的圖形界面。通過canvas,我們可以通過JavaScript代碼在頁面上繪制各種圖形和圖像。 使用canvas 在頁面中添加canvas 首先需要在頁面的template中添加一個canvas組件: <template><view><canvas ca…

linux:iostat 用法詳解

文章目錄 描述語法參數例子 描述 iostat 是一個在類Unix操作系統中常用的系統監控工具&#xff0c;尤其是Linux系統中&#xff0c;它主要用于收集和報告中央處理器(CPU)使用情況以及磁盤輸入/輸出(I/O)統計數據。以下是 iostat 命令的基本用法及其參數詳解&#xff1a; 語法 …

代碼隨想錄三刷 day11 | 棧與隊列之 20. 有效的括號 1047. 刪除字符串中的所有相鄰重復項 150. 逆波蘭表達式求值

三刷day11 20. 有效的括號1047. 刪除字符串中的所有相鄰重復項150. 逆波蘭表達式求值 20. 有效的括號 題目鏈接 解題思路&#xff1a; 有三種不匹配的情況&#xff1a; 第一種情況&#xff0c;字符串里左方向的括號多余了 。 第二種情況&#xff0c;括號沒有多余&#xff0c;…

[伴學筆記]01-操作系統概述 [南京大學2024操作系統]

文章目錄 前言jyy:01-操作系統概述 [南京大學2024操作系統]為什么要學操作系統?學習操作系統能得到什么? 什么是操作系統?想要明白什么是操作系統:時間線:1940s1950s-1960s1960-1970s年代. 信息來源: 前言 督促自己,同時分享所得,閱讀完本篇大約需要10分鐘,希望為朋友的技術…

編碼規則轉換

思考&#xff1a; 如何將一個機內碼轉換為區內碼&#xff1f; 只要將機內碼減去 A0A0 就可以啦 如果只讓我們用加法器來解決呢&#xff1f; 注意我們的數據占用了 32 位&#xff0c;如果想用補碼進行減法運算的話&#xff0c;符號位怎么辦&#xff1f;&#xff1f;&#xf…

《探索數據結構之美:如何高效實現哈希表》

摘要&#xff1a;哈希表是一種基于鍵值對的數據結構&#xff0c;它通過哈希函數將鍵映射到表中一個位置&#xff0c;以實現快速的插入、刪除和查找操作。在本期播客中&#xff0c;我們將深入剖析哈希表的數據結構&#xff0c;分享如何用Python語言實現一個哈希表項目。此外&…