<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      日志模塊

      介紹

      在學(xué)習(xí)了sylar的C++高性能分布式服務(wù)器框架后,想把自己在學(xué)習(xí)過(guò)程中的感想記錄下來(lái)。當(dāng)然主要原因還是sylar的B站視頻過(guò)于難以理解了,也是想加強(qiáng)一下自己對(duì)這個(gè)框架的理解。很多內(nèi)容也是借鑒了其他大佬的博文,比如找人找不到北zhongluqiang

      日志模塊概述

      日志模塊的目的:

      • 用于格式化輸出程序日志,方便從日志中定位程序運(yùn)行過(guò)程中出現(xiàn)的問(wèn)題。
      • 同時(shí)應(yīng)該包括文件名/行號(hào),時(shí)間戳,線程/協(xié)程號(hào),模塊名稱(chēng),日志級(jí)別等額外信息。
      • 在打印致命的日志時(shí),還應(yīng)該附加程序的棧回溯信息,以便于分析和排查問(wèn)題。

      從設(shè)計(jì)上看,一個(gè)完整的日志模塊應(yīng)該具備以下功能:

      1. 區(qū)分不同的級(jí)別,比如常的DEBUG/INFO/WARN/ERROR等級(jí)別。

      2. 區(qū)分不同的輸出地。不同的日志可以輸出到不同的位置,比如可以輸出到標(biāo)準(zhǔn)輸出,輸出到文件,輸出到syslog,輸出到網(wǎng)絡(luò)上的日志服務(wù)器等,甚至同一條日志可以同時(shí)輸出到多個(gè)輸出地。

      3. 區(qū)分不同的類(lèi)別。日志可以分類(lèi)并命名,一個(gè)程序的各個(gè)模塊可以使用不同的名稱(chēng)來(lái)輸出日志,這樣可以很方便地判斷出當(dāng)前日志是哪個(gè)程序模塊輸出的。

      4. 日志格式可靈活配置。可以按需指定每條日志是否包含文件名/行號(hào)、時(shí)間戳、線程/協(xié)程號(hào)、日志級(jí)別、啟動(dòng)時(shí)間等內(nèi)容。

      5. 可通過(guò)配置文件的方式配置以上功能。

      日志模塊設(shè)計(jì)

      類(lèi)似于log4cpp,日志模塊擁有以下幾個(gè)主要類(lèi):

      • class LogLevel:定義日志級(jí)別。并提供將日志級(jí)別與文本之間的互相轉(zhuǎn)化
      • class Logger:日志器。定義日志級(jí)別,設(shè)置輸出地,設(shè)置日志格式。
      • class LogEvent:記錄日志事件。主要記錄一下信息
      • class LogEventWarp:日志事件包裝器。將logEvent打包,可以直接通過(guò)使用該類(lèi)完成對(duì)日志的定義。
      • class LogFormatter:日志格式化。
      • class LogAppender:日志輸出目標(biāo)。有兩個(gè)子類(lèi) class StdoutLogAppender 和 class FileLogAppender,可以分別輸出到控制臺(tái)和文件
      • class LoggerManager:日志管理器。單例模式

      具體實(shí)現(xiàn)

      日志級(jí)別 class LogLevel

      enum Level {
          /// 致命情況,系統(tǒng)不可用
          FATAL  = 0,
          /// 高優(yōu)先級(jí)情況,例如數(shù)據(jù)庫(kù)系統(tǒng)崩潰
          ALERT  = 100,
          /// 嚴(yán)重錯(cuò)誤,例如硬盤(pán)錯(cuò)誤
          CRIT   = 200,
          /// 錯(cuò)誤
          ERROR  = 300,
          /// 警告
          WARN   = 400,
          /// 正常但值得注意
          NOTICE = 500,
          /// 一般信息
          INFO   = 600,
          /// 調(diào)試信息
          DEBUG  = 700,
          /// 未設(shè)置
          NOTSET = 800,
      };
      
      ToString(提供從日志級(jí)別 TO 文本的轉(zhuǎn)換)

      通過(guò)X宏(X Macros)將不同的級(jí)別放入switch case語(yǔ)句中。X宏的基本思想是將重復(fù)的代碼片段定義為一個(gè)宏,然后在需要使用這些代碼的地方多次調(diào)用這個(gè)宏。這樣可以避免手動(dòng)編寫(xiě)和維護(hù)大量重復(fù)代碼。

      const char* LogLevel::ToString(LogLevel::Level level){
          switch (level){ 
      #define XX(name) \
              case LogLevel::name: \
                  return #name; \
                  break;
      ?
              XX(DEBUG);
              XX(INFO);
              XX(WARN);
              XX(ERROR);
              XX(FATAL);
      #undef XX
      ?
              default:
                  return "UNKNOW";
              }
              return "UNKNOW";
      }
      

      swtich (level):對(duì)傳入的 level 參數(shù)進(jìn)行 switch 分支判斷。

      //#define XX(name) ... #undef XX:定義了一個(gè)名為 XX 的宏,用于減少重復(fù)代碼。宏 XX 接受一個(gè)參數(shù) name,并生成對(duì)應(yīng)的 case 語(yǔ)句。宏會(huì)展開(kāi)成:

      case LogLevel::DEBUG:
          return "DEBUG";
          break;
      case LogLevel::INFO:
          return "INFO";
          break;
      // 依次類(lèi)推
      

      注1:常見(jiàn)的X宏使用場(chǎng)景:
      枚舉與字符串映射:如將枚舉值轉(zhuǎn)換為字符串。
      多次聲明相似的代碼結(jié)構(gòu):如函數(shù)聲明、結(jié)構(gòu)體初始化等。
      生成重復(fù)的測(cè)試代碼:如生成一系列測(cè)試用例。

      FromString(提供從文本 To 日志級(jí)別的轉(zhuǎn)換)

      同樣通過(guò)宏定義處理多種情況。轉(zhuǎn)換時(shí)不針對(duì)大小寫(xiě),DEBUG和debug都可以完成對(duì)應(yīng)的轉(zhuǎn)化

      LogLevel::Level LogLevel::FromString(const std::string &str) {
      #define XX(level, v)    \
          if(str == #v) { \
              return LogLevel::level; \
          }
          XX(DEBUG, debug);
          XX(INFO, info);
          XX(WARN, warn);
          XX(ERROR, error);
          XX(FATAL, fatal);
      ?
          XX(DEBUG, DEBUG);
          XX(INFO, INFO);
          XX(WARN, WARN);
          XX(ERROR, ERROR);
          XX(FATAL, FATAL);
      ?
          return LogLevel::UNKNOW;
      #undef XX
      }
      

      日志事件 class LogEvent

      用于記錄日志現(xiàn)場(chǎng),比如該日志的級(jí)別,文件名/行號(hào),日志消息,線程/協(xié)程號(hào),所屬日志器名稱(chēng)等。

      成員變量
          const char* m_file = nullptr;   //文件名
          int32_t m_line = 0;             //行號(hào)
          uint32_t m_elapse = 0;          //程序啟動(dòng)開(kāi)始到現(xiàn)在的毫秒數(shù)
          uint32_t m_thieadId = 0;        //線程id
          uint32_t m_fiberId = 0;         //協(xié)程id
          uint64_t m_time;                //時(shí)間戳
          std::string m_threadName;       //線程名稱(chēng)
          std::stringstream m_ss;         //日志內(nèi)容流
          std::shared_ptr<Logger> m_logger;   //日志器
          LogLevel::Level m_level;        //日志等級(jí)
      
      成員函數(shù)
           /**
           * @brief 構(gòu)造函數(shù)
           * 
           * @param[in] logger 日志器
           * @param[in] level 日志級(jí)別
           * @param[in] file 文件名
           * @param[in] line 文件行號(hào)
           * @param[in] elapse 程序啟動(dòng)依賴(lài)的耗時(shí)(毫秒)
           * @param[in] thread_id 線程id
           * @param[in] fiber_id 協(xié)程id
           * @param[in] time 日志時(shí)間
           * @param[in] thread_name 線程名稱(chēng)
           */
      LogEvent::LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
                  , const char* file, int32_t line, uint32_t elapse
                  , uint32_t thread_id, uint32_t fiber_id, uint64_t time
                  , const std::string& thread_name)
          :m_file(file)
          ,m_line(line)
          ,m_elapse(elapse)
          ,m_thieadId(thread_id)
          ,m_fiberId(fiber_id)
          ,m_time(time)
          ,m_threadName(thread_name)
          ,m_logger(logger)
          ,m_level(level) {
          }
      
      void LogEvent::format(const char* fmt, ...) {
          va_list al;  		//1)
      	va_start(al, fmt);	//2)
      	format(fmt, al);	//3)
      	va_end(al);			//6)
      }
      
      void LogEvent::format(const char* fmt, va_list al){
      	char *buf = nullptr;
          // len返回寫(xiě)入buf的長(zhǎng)度
      	int len = vasprintf(&buf, fmt, al);	//4)
      	if(len != -1) {
      		m_ss << std::string(buf, len);	//5)
      		free(buf);
      	}
      }
      

      日志事件包裝器 class LogEventWarp

      日志事件包裝類(lèi),其實(shí)就是將日志事件和日志器包裝到一起,因?yàn)橐粭l日志只會(huì)在一個(gè)日志器上進(jìn)行輸出。將日志事件和日志器包裝到一起后,方便通過(guò)宏定義來(lái)簡(jiǎn)化日志模塊的使用。另外,LogEventWrap還負(fù)責(zé)在構(gòu)建時(shí)指定日志事件和日志器,在析構(gòu)時(shí)調(diào)用日志器的log方法將日志事件進(jìn)行輸出。

      // 日志事件
      LogEvent::ptr m_event;
      
      // 構(gòu)造函數(shù)
      LogEventWarp::LogEventWarp(LogEvent::ptr e)
      	:m_event(e){
      }
      
      // 析構(gòu)函數(shù)
      LogEventWarp::~LogEventWarp() {
      	m_event->getLogger()->log(m_event->getLevel(), m_event);
      }
      

      在此說(shuō)一下使用日志的宏,這里定義了SYLAR_LOG_LEVEL宏,用來(lái)輸出Level級(jí)別的LogEvent,并將LogEvent寫(xiě)入到Logger中。

      #define SYLAR_LOG_LEVEL(logger, level) \
      	if (logger->getLevel() <= level) \
      		sylar::LogEventWarp(sylar::LogEvent::ptr (new sylar::LogEvent(logger, level, \
      				__FILE__, __LINE__, 0, sylar::GetThreadId(), \
      			sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getSS()
      
      #define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::DEBUG)
      

      日志格式

      日志內(nèi)容格式化 class FormatItem

      該類(lèi)為L(zhǎng)ogFormatter的public內(nèi)部類(lèi)成員,通過(guò)該類(lèi)得到解析后的格式。

      此類(lèi)為抽象類(lèi),不同事件的子類(lèi)繼承該類(lèi),并且重寫(xiě)純虛函數(shù)format將日志格式轉(zhuǎn)化到流

      格式化日志到流

      // 消息format
      class MessageFormatItem : public LogFormatter::FormatItem{
      public:
      	MessageFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << event->getContent(); 
      	}
      };
      
      // 日志級(jí)別format
      class LevelFormatItem : public LogFormatter::FormatItem{
      public:
      	LevelFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << LogLevel::ToString(level);
      	}
      };
      
      // 執(zhí)行時(shí)間format
      class ElapseFormatItem : public LogFormatter::FormatItem{
      public:
      	ElapseFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << event->getElapse(); 
      	}
      };
      
      // 日志器名稱(chēng)format
      class NameFormatItem : public LogFormatter::FormatItem{
      public:
      	NameFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << event->getLogger()->getName(); 
      	}
      };
      
      // 線程id format
      class ThreadIdFormatItem : public LogFormatter::FormatItem{
      public:
      	ThreadIdFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << event->getThieadId(); 
      	}
      };
      
      // 協(xié)程id format
      class FiberIdFormatItem : public LogFormatter::FormatItem{
      public:
      	FiberIdFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << event->getFiberId(); 
      	}
      };
      
      // 線程名稱(chēng)format
      class ThreadNameFormatItem : public LogFormatter::FormatItem{
      public:
      	ThreadNameFormatItem(const std::string& str = "") {}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		os << event->getThreadName(); 
      	}
      };
      
      // 時(shí)間format
      class DateTimeFormatItem : public LogFormatter::FormatItem{
      public:
      	DateTimeFormatItem(const std::string& format = "%Y-%m-%d %H:%M:%S")
      		:m_format(format) {
      			if(m_format.empty()) {
      				m_format = "%Y-%m-%d %H:%M:%S"; 
      			}
      		}
      	void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
      		struct tm tm;
      		time_t time = event->getTime();	//創(chuàng)建event時(shí)默認(rèn)給的 time(0) 當(dāng)前時(shí)間戳
      		localtime_r(&time, &tm);	//將給定時(shí)間戳轉(zhuǎn)換為本地時(shí)間,并將結(jié)果存儲(chǔ)在tm中
      		char buf[64];
      		strftime(buf, sizeof(buf), m_format.c_str(), &tm);	//將tm格式化為m_format格式,并存儲(chǔ)到buf中
      		os << buf; 
      	}
      private:
      	std::string m_format;
      };
      
      日志格式器 class LogFormatter

      與log4cpp的PatternLayout對(duì)應(yīng),用于格式化一個(gè)日志事件。該類(lèi)構(gòu)建時(shí)可以指定pattern,表示如何進(jìn)行格式化。提供format方法,用于將日志事件格式化成字符串。

      // 成員變量
      // 日志格式模板
      std::string m_pattern;
      // 日志格式解析后格式
      std::vector<FormatItem::ptr> m_items;
      // 判斷日志格式錯(cuò)誤
      bool m_error = false;
      
      // 構(gòu)造函數(shù)
      LogFormatter::LogFormatter(const std::string& pattern)
      	:m_pattern(pattern) {
      		init();
      }
      
      // 將解析后的日志信息輸出到流中
      std::string LogFormatter::format (std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event){
      	std::stringstream ss;
      	for(auto& i : m_items) {
      		i->format(ss, logger, level, event);
      	}
      	return ss.str();
      }
      
      // init(解析格式)
      // 得到相應(yīng)FormatItem放入m_items
      // 默認(rèn)格式模板為:"%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
      // e.g.Y-M-D H:M:S threadId threadName fiberId [Level] [logName] FILE:LINE message
      
      //%xxx %xxx{xxx} %%
      // m_pattern "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
      void LogFormatter::init(){ 
      	//string, format, type
      	std::vector<std::tuple<std::string, std::string, int>> vec;
      	std::string nstr;	// 存放 [ ] :
      	for(size_t i = 0; i < m_pattern.size(); ++i) {
      		if (m_pattern[i] != '%')	//若解析的不是'%'
      		{
      			nstr.append(1, m_pattern[i]);	//在nstr后面添加一個(gè)該字符
      			continue;
      		}
      
      		if((i + 1) < m_pattern.size()) {	//保證m_pattern不越界
      			if (m_pattern[i + 1] == '%') {	//解析 "%%"
      				nstr.append(1, '%');		//在nstr后面加上%
      				continue;
      			}
              
      												
      		size_t n = i + 1;		//遇到'%'往下  (e.g.) n = 1, m_pattern[1] = 'd'
      		int fmt_status = 0;		//狀態(tài)1: 解析時(shí)間{%Y-%m-%d %H:%M:%S} 狀態(tài)0:解析之后的
      		size_t fmt_begin = 0;	//開(kāi)始位置 為{
      
      		std::string str;		//d T t N等格式
      		std::string fmt;		//保存時(shí)間格式 %Y-%m-%d %H:%M:%S	
      
      		while(n < m_pattern.size()){
                  // fmt_status != 0, m_attern[n]不是字母,m_pattern[n]不是'{', m_pattern[n]不是'}'
                  // (e.g.) %T%  (i -> %, n -> T, while循環(huán) n -> % 此時(shí)解析完一個(gè)T, break
                  // (e.g.) 遇到 [ ] break,取出[%p]中的p
      			if(!fmt_status && (!isalpha(m_pattern[n]) && m_pattern[n] != '{' //返回0表示該字符不是字母字符。
      					&& m_pattern[n] != '}')) {
      				str = m_pattern.substr(i + 1, n - i - 1);
      				break;
      			}
      			if(fmt_status == 0){	//開(kāi)始解析時(shí)間格式
      				if(m_pattern[n] == '{'){
      					str = m_pattern.substr(i + 1, n - i - 1);	//str = "d"
      					fmt_status = 1;	
      					fmt_begin = n;
      					++n;
      					continue;
      				}
      			} else if(fmt_status == 1) {	//結(jié)束解析時(shí)間格式
      				if(m_pattern[n] == '}') {
                          // fmt = %Y-%m-%d %H:%M:%S
      					fmt = m_pattern.substr(fmt_begin + 1, n - fmt_begin - 1);
      					fmt_status = 0;
      					++n;
      					break;		//解析時(shí)間結(jié)束break
      				}
      			}
      			++n;
      			if (n == m_pattern.size()) {	//最后一個(gè)字符
      				if (str.empty()) {
      					str = m_pattern.substr(i + 1);
      				}
      			}
      		}
      		if(fmt_status == 0){
      			if(!nstr.empty()){	// nstr: [ :
      				vec.push_back(std::make_tuple(nstr, std::string(), 0));	// 將[ ]放入, type為0
      				nstr.clear();
      			}
      			vec.push_back(std::make_tuple(str, fmt, 1));	//(e.g.) ("d", %Y-%m-%d %H:%M:%S, 1) type為1
      			i = n - 1;	 //跳過(guò)已解析的字符,讓i指向當(dāng)前處理的字符,下個(gè)for循環(huán)會(huì)++i處理下個(gè)字符
      		} else if(fmt_status == 1) {
      			std::cout << "Pattern parde error: " << m_pattern << " - " << m_pattern.substr(i) << std::endl;
      			m_error = true;
      			vec.push_back(std::make_tuple("<<pattern_error>>", fmt, 0));
      		} 
      	}
      
      	if(!nstr.empty()) {
      		vec.push_back(std::make_tuple(nstr, "", 0));	//(e.g.) 最后一個(gè)字符為[ ] :
      	}
      	
          // map類(lèi)型為<string, cb>, string為相應(yīng)的日志格式, cb返回相應(yīng)的FormatItem智能指針
      	static std::map<std::string, std::function<FormatItem::ptr(const std::string& fmt)> > 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:日志級(jí)別
              XX(r, ElapseFormatItem),            //r:累計(jì)毫秒數(shù)
              XX(c, NameFormatItem),              //c:日志名稱(chēng)
              XX(t, ThreadIdFormatItem),          //t:線程id
              XX(n, NewLineFormatItem),           //n:換行
              XX(d, DateTimeFormatItem),          //d:時(shí)間
              XX(f, FilenameFormatItem),          //f:文件名
              XX(l, LineFormatItem),              //l:行號(hào)
              XX(T, TabFormatItem),               //T:Tab
              XX(F, FiberIdFormatItem),           //F:協(xié)程id
      		XX(N, ThreadNameFormatItem),		//N:線程名稱(chēng)
      
      #undef XX
      	};
      
      	for (auto& i : vec){
      		if (std::get<2>(i) == 0) {	//若type為0
                  //將解析出的FormatItem放到m_items中 [ ] :
      			m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
      		} else {	//type為1
      			auto it = s_format_items.find(std::get<0>(i));	//從map中找到相應(yīng)的FormatItem
      			if(it == s_format_items.end()) {	//若沒(méi)有找到則用StringFormatItem顯示錯(cuò)誤信息 并設(shè)置錯(cuò)誤標(biāo)志位
      				m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(i) + ">>")));
      				m_error = true;
      			} else {	//返回相應(yīng)格式的FormatItem,其中std::get<1>(i)作為cb的參數(shù)
      				m_items.push_back(it->second(std::get<1>(i)));
      			}
      		}
      	}
      }
      

      日志輸出

      日志輸出器 class LogAppender

      class LogAppender是抽象類(lèi),有兩個(gè)子類(lèi),分別為StdoutLogAppender和FileLogAppender,分別實(shí)現(xiàn)控制臺(tái)和文件的輸出。兩個(gè)類(lèi)都重寫(xiě)純虛函數(shù)log方法實(shí)現(xiàn)寫(xiě)入日志,重寫(xiě)純虛函數(shù)toYamlString方法實(shí)現(xiàn)將日志轉(zhuǎn)化為YAML格式的字符串

      成員變量

      //日志級(jí)別
      LogLevel::Level m_level = LogLevel::DEBUG;
      //日志格式器
      LogFormatter::ptr m_formatter;
      // 互斥鎖
      MutexType m_mutex;
      // 是否有formatter
      bool m_hasFormatter = false;
      

      成員函數(shù)

      // setFormatter(更改日志格式器)
      void LogAppender::setFormatter(LogFormatter::ptr val) {
      	MutexType::Lock lock(m_mutex);
      	m_formatter = val;
      	if (m_formatter) {
      		m_hasFormatter = true;
      	} else {
      		m_hasFormatter = false;
      	}
      }
      
      // getFormatter(獲得日志格式器)
      LogFormatter::ptr LogAppender::getFormatter() {
      	MutexType::Lock lock(m_mutex);
      	return m_formatter;
      }
      
      class StdoutLogAppender(輸出到控制臺(tái)的Appender)
      class StdoutLogAppender : public LogAppender {
      public:
      	typedef std::shared_ptr<StdoutLogAppender> ptr;
      	void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event){
              if(level >= m_level) {
                  MutexType::Lock lock(m_mutex);
                  std::cout << m_formatter->format(logger, level, event);	//這里調(diào)用Logformat的format,它會(huì)遍歷m_items調(diào)用相應(yīng)的format輸出到流
              }
          }
      	std::string toYamlString(){
              MutexType::Lock lock(m_mutex);
              YAML::Node node;
              node["type"] = "StdoutLogAppender";
              if(m_level != LogLevel::UNKNOW) {
                  node["level"] = LogLevel::ToString(m_level);
              }
              if(m_hasFormatter && m_formatter) {
                  node["formatter"] = m_formatter->getPattern();
              }
              std::stringstream ss;
              ss << node;
              return ss.str();
          }
      };
      
      class FileLogAppender(輸出到文件的Appender)

      mumber(成員變量)

      // 文件路徑
      std::string m_filename;
      // 文件流
      std::ofstream m_filestream;
      // 每秒reopen一次,判斷文件有沒(méi)有被刪
      uint64_t m_lastTime = 0;
      

      成員函數(shù)

      // 構(gòu)造函數(shù)
      FileLogAppender::FileLogAppender(const std::string& filename)
      	:m_filename(filename){
      		reopen();
      }
      
      // reopen(寫(xiě)入文件)
      bool FileLogAppender::reopen(){
      	MutexType::Lock lock(m_mutex);
      	if (m_filestream){
      		m_filestream.close();
      	}
      
      	m_filestream.open(m_filename, std::ios::app);	//以追加的方式寫(xiě)入文件中
      	return !!m_filestream;
      }
      // log(輸出到文件)
      // 重寫(xiě)log方法,輸出到文件
      void FileLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event){
      	if (level >= m_level){
      		uint64_t now = time(0);
      		if (now != m_lastTime) {	//每秒重新reopen
      			reopen();
      			m_lastTime = now;
      		}
      		MutexType::Lock lock(m_mutex);
      		if (!(m_filestream << m_formatter->format(logger, level, event))) {		//寫(xiě)到m_filestream流中
      			std::cout << "error" << std::endl;
      		}
      	}
      }
      
      // toYamlString(轉(zhuǎn)化為YAML格式字符串)
      // 重寫(xiě)toYamlString方法,轉(zhuǎn)化為YAML格式字符串
      std::string FileLogAppender::toYamlString() {
      	MutexType::Lock lock(m_mutex);
      	YAML::Node node;
      	node["type"] = "FileLogAppender";
      	node["file"] = m_filename;
      	if(m_level != LogLevel::UNKNOW) {
      		node["level"] = LogLevel::ToString(m_level);
      	}
      	if(m_hasFormatter && m_formatter) {
      		node["formatter"] = m_formatter->getPattern();
      	}
      	std::stringstream ss;
      	ss << node;
      	return ss.str();
      }
      

      日志器 class Logger

      負(fù)責(zé)進(jìn)行日志輸出。一個(gè)Logger包含多個(gè)LogAppender和一個(gè)日志級(jí)別,提供log方法,傳入日志事件,判斷該日志事件的級(jí)別高于日志器本身的級(jí)別之后調(diào)用LogAppender將日志進(jìn)行輸出,否則該日志被拋棄。

      成員變量
      //日志名稱(chēng)
      std::string m_name;
      //日志級(jí)別
      LogLevel::Level m_level;
      // 互斥鎖
      MutexType m_mutex;
      // 日志目標(biāo)集合
      std::list<LogAppender::ptr> m_appenders;
      //日志格式器
      LogFormatter::ptr m_formatter;
      // root Log
      Logger::ptr m_root;
      
      成員函數(shù)
      // Logger(構(gòu)造函數(shù))
      // 名稱(chēng),def = root
      // 日志級(jí)別, def = DEBUG
      // 日志格式, def = "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
      Logger::Logger(const std::string& name)
      	:m_name(name) 
      	,m_level(LogLevel::DEBUG){
      		m_formatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
      }
      
      // log(寫(xiě)不同Level日志到日志目標(biāo))
      // m_appenders為日志目標(biāo)地,將當(dāng)前l(fā)ogger輸出到相應(yīng)的appender,因?yàn)锳ppender的log要傳入logger的智能指針,所以使用shared_from_this()獲得當(dāng)前l(fā)ogger的智能指針
      void Logger::log(LogLevel::Level level, LogEvent::ptr event){
      	if (level >= m_level){
      		auto self = shared_from_this();
      		MutexType::Lock lock(m_mutex);
      		if (!m_appenders.empty()) {
      			for(auto& i : m_appenders){
      				i->log(self, level, event);
      			}
      		} else if(m_root) {		//當(dāng)logger的appenders為空時(shí),使用root寫(xiě)logger
      			m_root->log(level, event);
      		}
      	}	
      }
      
      // addAppender(添加日志目標(biāo))
      // 若appender沒(méi)有formatter的話(huà)就將默認(rèn)formatter賦給他,若有formatter則直接添加到m_appenders隊(duì)列中
      void Logger::addAppender(LogAppender::ptr appender){
      		MutexType::Lock lock(m_mutex);
      		if (!appender->getFormatter()) {
      			MutexType::Lock ll(appender->m_mutex);
      			appender->m_formatter = m_formatter;
      		}
      	m_appenders.push_back(appender);
      }
      
      // delAppender(刪除日志目標(biāo))
      // 在m_appenders中找到要?jiǎng)h除的appender,erase掉
      void Logger::delAppender(LogAppender::ptr appender){
      	MutexType::Lock lock(m_mutex);
      	for (auto it = m_appenders.begin();
      		it != m_appenders.end(); ++it) {
      	     if(*it == appender) {
      	     	m_appenders.erase(it);
      		break;
      	     }
      	}
      }
      
      // setFormatter(通過(guò)智能指針 )
      // 將新的formatter賦給m_formatter,若appender沒(méi)有formatter,則將appender的formatter更新。
      void Logger::setFormatter(LogFormatter::ptr val){
      	MutexType::Lock lock(m_mutex);
      	m_formatter = val;
      
      	for (auto& i : m_appenders) {
      		MutexType::Lock ll(i->m_mutex);
      		if (!i->m_hasFormatter) {
      			i->m_formatter = m_formatter;
      		}
      	}
      }
      
      // setFormatter(通過(guò)字符串)
      // new一個(gè)新的formatter,若格式?jīng)]錯(cuò),調(diào)用上面的setFormatter設(shè)置Formatter。
      void Logger::setFormatter(const std::string &val){
      	sylar::LogFormatter::ptr new_val(new sylar::LogFormatter(val));
      	if (new_val->isError()) {
      		 std::cout << "Logger setFormatter name = " << m_name
      				   << "value = " << val << "invalid formatter"
      				   << std::endl;
      		 return;
      	}
      	// m_formatter = new_val;
      	setFormatter(new_val);
      }
      
      // toYamlString(轉(zhuǎn)換為YAML格式輸出)
      // 將當(dāng)前l(fā)ogger name,level,formatter,appenders YAML格式按流輸出
      std::string Logger::toYamlString() {
      	MutexType::Lock lock(m_mutex);
      	YAML::Node node;
      	node["name"] = m_name;
      	if(m_level != LogLevel::UNKNOW) {
      		node["level"] = LogLevel::ToString(m_level);
      	}
      	if (m_formatter) {
      		node["formatter"] = m_formatter->getPattern();
      	}
      	for (auto& i : m_appenders) {
      		node["appenders"].push_back(YAML::Load(i->toYamlString()));
      	}
      	std::stringstream ss;
      	ss << node;
      	return ss.str();
      }
      

      日志管理器 class LoggerManager

      單例模式,用于統(tǒng)一管理所有的日志器,提供日志器的創(chuàng)建與獲取方法。LogManager自帶一個(gè)root Logger,用于為日志模塊提供一個(gè)初始可用的日志器。

      typedef sylar::Singleton<LoggerManager> LoggerMgr;
      
      成員變量
      // 互斥鎖
      MutexType m_mutex;
      // 日志器容器
      std::map<std::string, Logger::ptr> m_loggers;
      // 主日志器
      Logger::ptr m_root;
      
      成員函數(shù)
      // LoggerManager(構(gòu)造函數(shù))
      LoggerManager::LoggerManager() {
      	m_root.reset(new Logger);
      	m_root->addAppender(LogAppender::ptr(new StdoutLogAppender));
      
      	m_loggers[m_root->m_name] = m_root;
      }
      
      // getLogger(獲取日志器)
      // 在map中找到相應(yīng)的logger就返回他,若沒(méi)有就創(chuàng)建一個(gè)logger并將他放到日志器容器m_loggers中,再返回他
      Logger::ptr LoggerManager::getLogger(const std::string& name) {
      	MutexType::Lock lock(m_mutex);
      	auto it = m_loggers.find(name);
      	if (it != m_loggers.end()) {
      		return it->second;
      	}
      	Logger::ptr logger(new Logger(name));
      	logger->m_root = m_root;	//將logger的root賦值,當(dāng)沒(méi)有appender時(shí),使用root寫(xiě)logger
      	m_loggers[name] = logger;
      	return logger;
      }
      
      // toYamlString(將日志格式轉(zhuǎn)化為YAML字符串)
      std::string LoggerManager::toYamlString() {
      	MutexType::Lock lock(m_mutex);
      	YAML::Node node;
      	for (auto& i : m_loggers) {
      		node.push_back(YAML::Load(i.second->toYamlString()));
      	}
      	std::stringstream ss;
      	ss << node;
      	return ss.str();
      }
      

      宏定義

      使用流的方式,將不同日志級(jí)別的事件寫(xiě)入logger中
      #define SYLAR_LOG_LEVEL(logger, level) \
      	if (logger->getLevel() <= level) \
      		sylar::LogEventWarp(sylar::LogEvent::ptr (new sylar::LogEvent(logger, level, \
      				__FILE__, __LINE__, 0, sylar::GetThreadId(), \
      			sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getSS()
      
      #define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::DEBUG)
      #define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::INFO)
      #define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::WARN)
      #define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::ERROR)
      #define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::FATAL)
      
      使用格式化方式, 將不同日志級(jí)別的事件寫(xiě)入logger中
      #define SYLARY_LOG_FMT_LEVEL(logger, level, fmt, ...) \
      	if (logger->getLevel() <= level) \
      		sylar::LogEventWarp(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
      							__FILE__, __LINE__, 0, sylar::GetThreadId(), \
      					sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getEvent()->format(fmt, __VA_ARGS__)
      #define SYLARY_LOG_FMT_DEBUG(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VA_ARGS__)
      #define SYLARY_LOG_FMT_INFO(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)
      #define SYLARY_LOG_FMT_WARN(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)
      #define SYLARY_LOG_FMT_ERROR(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)
      #define SYLARY_LOG_FMT_FATAL(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)
      
      獲得主日志器
      #define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
      
      獲得相應(yīng)名字的日志器
      #define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)
      

      總結(jié)

      總結(jié)一下日志模塊的工作流程:

      1. 初始化LogFormatter,LogAppender, Logger。

      2. 通過(guò)宏定義提供流式風(fēng)格和格式化風(fēng)格的日志接口。每次寫(xiě)日志時(shí),通過(guò)宏自動(dòng)生成對(duì)應(yīng)的日志事件LogEvent,并且將日志事件和日志器Logger包裝到一起,生成一個(gè)LogEventWrap對(duì)象。

      3. 日志接口執(zhí)行結(jié)束后,LogEventWrap對(duì)象析構(gòu),在析構(gòu)函數(shù)里調(diào)用Logger的log方法將日志事件進(jìn)行輸出。

      待補(bǔ)充與完善

      目前來(lái)看,sylar日志模塊已經(jīng)實(shí)現(xiàn)了一個(gè)完整的日志框架,并且配合后面的配置模塊,可用性很高,待補(bǔ)充與完善的地方主要存在于LogAppender,目前只提供了輸出到終端與輸出到文件兩類(lèi)LogAppender,但從實(shí)際項(xiàng)目來(lái)看,以下幾種類(lèi)型的LogAppender都是非常有必要的:

      1. Rolling File Appender,循環(huán)覆蓋寫(xiě)文件
      2. Rolling Memory Appender,循環(huán)覆蓋寫(xiě)內(nèi)存緩沖區(qū)
      3. 支持日志文件按大小分片或是按日期分片
      4. 支持網(wǎng)絡(luò)日志服務(wù)器,比如syslog
      posted @ 2024-05-27 14:26  機(jī)械心  閱讀(130)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲一区二区色情苍井空| 久久久一本精品99久久精品36| 在线无码免费的毛片视频| 日韩中文字幕免费在线观看| 国产精品va在线观看无码不卡| 99久久激情国产精品| 国产婷婷综合在线视频| 欧美成人精品手机在线| 国产偷自一区二区三区在线| 久久人人97超碰爱香蕉| 2021亚洲国产精品无码| 久久三级国内外久久三级| 中文字幕精品亚洲字幕成| 少妇内射高潮福利炮| bt天堂新版中文在线| 国产微拍一区二区三区四区| 国产午夜福利在线视频| 久九九精品免费视频| 亚洲人成线无码7777| 国产av一区二区三区综合| 性夜夜春夜夜爽夜夜免费视频| 吕梁市| 国产精品久久久久久久久鸭| 亚洲激情一区二区三区在线| 少妇人妻88久久中文字幕| 亚洲中文字幕国产综合| 免费人成再在线观看视频| 九九热在线视频观看这里只有精品| 元朗区| 农民人伦一区二区三区| 国产不卡一区二区四区| 99久久精品费精品国产| 亚洲成av人片天堂网无码| 久久国产一区二区日韩av| 亚洲经典在线中文字幕| 国产丰满乱子伦无码专区| 98精品全国免费观看视频| 国产伦精品一区二区三区妓女 | 漂亮人妻被中出中文字幕| 国模雨珍浓密毛大尺度150p| 国产午夜精品理论大片|