log4z源码解析


文章目录

  • log4z源码解析
    • 1. 继承关系图
    • 2 各个类详解
      • 2.1 ILog4zManager ---- log4z日志库的抽象基类
        • 内部结构图
        • 源码注释
      • 2.2 ThreadHelper ---- log4z 线程抽象基类
        • 2.2.1 内部结构图
        • 2.2.2. 源码解析
      • 2.3 LogerManager log4z最重要的类
        • 2.3.1内部接口调用关系图
        • 2.3.2 类源码解析
        • 2.3.3 主要类接口介绍
          • run
            • 源码
            • 时序图
          • popLog(LogData *& log)
            • 源码
            • 调用关系
          • configFromStringImpl 解析配置
            • 源码解析
          • makeLogData
            • 源码
      • 2.4 其他工具类
      • 2.5 日志格式
      • 2.6 其他工具类接口
      • 2.7 默认的宏定义
      • 2.8 记录日志的宏定义
    • 3 总结

log4z源码解析 1. 继承关系图 log4z源码解析
文章图片

通过上述的类图,log4z的类之间的关系非常简单,只有一层继承关系,接下来我们将一一介绍上述类图中各个类的实现
2 各个类详解 2.1 ILog4zManager ---- log4z日志库的抽象基类
内部结构图 ILog4zManager 是一个抽象接口类,定义了log4z主要的操作
log4z源码解析
文章图片

源码注释
/*log4z日志库的基类*/ //! log4z class class ILog4zManager { public: //构造函数 ILog4zManager(){}; // 基类的析构必须声明为虚函数,原因见:c/c++ 基类析构函数为什么必须定义为虚函数? virtual ~ILog4zManager(){}; //! Log4z Singleton // ILog4zManager 是一个单例,懒汉模式 static ILog4zManager * getInstance(); inline static ILog4zManager & getRef(){return *getInstance(); } inline static ILog4zManager * getPtr(){return getInstance(); } /*下面都是纯虚函数 在派生类LogerManager 进行一一实现*///! Config or overwrite configure //! Needs to be called before ILog4zManager::Start,, OR Do not call. // 读取配置文件并覆写,需要在 日志库工作之前进行调用,或者不进行调用使用默认的配置。 virtual bool config(const char * configPath) = 0; // 从一个字符串中读取配置 virtual bool configFromString(const char * configContent) = 0; //! Create or overwrite logger. //! Needs to be called before ILog4zManager::Start, OR Do not call. // 创建一个记录器 ,最大支持创建20个 virtual LoggerId createLogger(const char* key) = 0; //! Start Log Thread. This method can only be called once by one process. // 开启日志记录的主线程,这个接口只能调用一次 virtual bool start() = 0; //! Default the method will be calling at process exit auto. //! Default no need to call and no recommended. // 结束日志记录的主线程,默认的不需要调用此接口,因此不推荐使用 virtual bool stop() = 0; //! Find logger. thread safe. // 根据 关键字进行查找记录器 线程安全 virtual LoggerId findLogger(const char* key) =0; //pre-check the log filter. if filter out return false. // 在将一条日志推入队列之前进行检查,如果有问题直接返回错误 virtual bool prePushLog(LoggerId id, int level) = 0; //! Push log, thread safe. // 将一条日志推入到队列中去,线程安全 virtual bool pushLog(LogData * pLog, const char * file = NULL, int line = 0) = 0; //! set logger's attribute, thread safe. // 设置记录器的属性,线程安全 virtual bool enableLogger(LoggerId id, bool enable) = 0; // immediately when enable, and queue up when disable. virtual bool setLoggerName(LoggerId id, const char * name) = 0; virtual bool setLoggerPath(LoggerId id, const char * path) = 0; virtual bool setLoggerLevel(LoggerId id, int nLevel) = 0; // immediately when enable, and queue up when disable. virtual bool setLoggerFileLine(LoggerId id, bool enable) = 0; virtual bool setLoggerDisplay(LoggerId id, bool enable) = 0; virtual bool setLoggerOutFile(LoggerId id, bool enable) = 0; virtual bool setLoggerLimitsize(LoggerId id, unsigned int limitsize) = 0; virtual bool setLoggerMonthdir(LoggerId id, bool enable) = 0; virtual bool setLoggerReserveTime(LoggerId id, time_t sec) = 0; //! Update logger's attribute from config file, thread safe. // 设置自动更新配置配置文件的时间间隔,线程安全 virtual bool setAutoUpdate(int interval/*per second, 0 is disable auto update*/) = 0; // 更新配置文件 virtual bool updateConfig() = 0; //! Log4z status statistics, thread safe. // log4z的状态统计 // 获取是否使能 virtual bool isLoggerEnable(LoggerId id) = 0; // 获取总的写入文件的日志的条数 virtual unsigned long long getStatusTotalWriteCount() = 0; // 获取总的写入文件的日志的字节数 virtual unsigned long long getStatusTotalWriteBytes() = 0; // 获取总的push的日志条数 入队的次数,有的日志不一定写入文件所以和上面有所区别 virtual unsigned long long getStatusTotalPushQueue() = 0; // 获取总的Pop的日志条数 出队的次数 virtual unsigned long long getStatusTotalPopQueue() = 0; // 获取活跃的记录器的个数 即:enable的记录器的个数 virtual unsigned int getStatusActiveLoggers() = 0; // 制作日志数据 即:向日志前添加 时间 线程ID 日志级别信息 添加的信息例子如下:2020-07-06 10:32:22.873 [12756] LOG_DEBUG virtual LogData * makeLogData(LoggerId id, int level) = 0; // 丢弃一条LogData,将丢弃的日志放入到 _freeLogDatas 中去 virtual void freeLogData(LogData * log) = 0; };

2.2 ThreadHelper ---- log4z 线程抽象基类
该基类用于线程的管理,log4z日志库中存在一个线程,该线程负责将日志从队列中取出并输出屏幕或者文件,该类定义了线程的创建,开始,结束等接口,具体实现在LogerManager 中。
2.2.1 内部结构图 log4z源码解析
文章图片

2.2.2. 源码解析
// log4z线程类 class ThreadHelper { public: ThreadHelper(){_hThreadID = 0; } virtual ~ThreadHelper(){} public: //创建线程并进行启动 bool start(); // 线程等待 bool wait(); // 线程运行的主函数 virtual void run() = 0; private: // 线程ID unsigned long long _hThreadID; #ifndef WIN32 pthread_t _phtreadID; #endif };

2.3 LogerManager log4z最重要的类
这个派生类,将ILogerManager,ThreadHelper 抽象类 进行实现。
2.3.1内部接口调用关系图 log4z源码解析
文章图片

2.3.2 类源码解析
class LogerManager : public ThreadHelper, public ILog4zManager { public: LogerManager(); virtual ~LogerManager(); bool configFromStringImpl(std::string content, bool isUpdate); //! 读取配置文件并覆写 virtual bool config(const char* configPath); virtual bool configFromString(const char* configContent); //! 覆写式创建 virtual LoggerId createLogger(const char* key); virtual bool start(); virtual bool stop(); virtual bool prePushLog(LoggerId id, int level); virtual bool pushLog(LogData * pLog, const char * file, int line); //! 查找ID virtual LoggerId findLogger(const char*key); bool hotChange(LoggerId id, LogDataType ldt, int num, const std::string & text); virtual bool enableLogger(LoggerId id, bool enable); virtual bool setLoggerName(LoggerId id, const char * name); virtual bool setLoggerPath(LoggerId id, const char * path); virtual bool setLoggerLevel(LoggerId id, int nLevel); virtual bool setLoggerFileLine(LoggerId id, bool enable); virtual bool setLoggerDisplay(LoggerId id, bool enable); virtual bool setLoggerOutFile(LoggerId id, bool enable); virtual bool setLoggerLimitsize(LoggerId id, unsigned int limitsize); virtual bool setLoggerMonthdir(LoggerId id, bool enable); virtual bool setLoggerReserveTime(LoggerId id, time_t sec); virtual bool setAutoUpdate(int interval); virtual bool updateConfig(); virtual bool isLoggerEnable(LoggerId id); virtual unsigned long long getStatusTotalWriteCount(){return _ullStatusTotalWriteFileCount; } virtual unsigned long long getStatusTotalWriteBytes() { return _ullStatusTotalWriteFileBytes; } virtual unsigned long long getStatusTotalPushQueue() { return _ullStatusTotalPushLog; } virtual unsigned long long getStatusTotalPopQueue() { return _ullStatusTotalPopLog; } virtual unsigned int getStatusActiveLoggers(); protected: virtual LogData * makeLogData(LoggerId id, int level); virtual void freeLogData(LogData * log); // 显示文本的颜色 void showColorText(const char *text, int level = LOG_LEVEL_DEBUG); // 改变 LoggerInfo 属性 bool onHotChange(LoggerId id, LogDataType ldt, int num, const std::string & text); // 打开记录器 bool openLogger(LogData * log); // 关闭记录器 bool closeLogger(LoggerId id); // 弹出一条日志 bool popLog(LogData *& log); virtual void run(); private://! thread status. 运行状态 bool_runing; //! wait thread started. 信号量 等待线程运行 SemHelper_semaphore; //! hot change name or path for one logger 热改变一个记录器的路径或者名字的时间间隔 int _hotUpdateInterval; // 配置文件的校验和,根据此项查看配置文件是否改变 unsigned int _checksum; //! the process info. 进程信息 // 进程号 std::string _pid; // 程序名 std::string _proName; //! config file name 配置文件路径名字 std::string _configFile; //! logger id manager, [logger name]:[logger id].记录器map logger name -> logger id std::map _ids; // the last used id of _loggers最后使用的记录器ID LoggerId_lastId; //记录器的信息 LoggerInfo _loggers[LOG4Z_LOGGER_MAX]; /* log4z 三种队列的关系图 _logs 日志的队列,通过 pushLog 或者 hotChange 接口向 std::deque _logs; 队列中添加日志数据添加到该队列中 _logsCache 日志缓存队列,在popLog时,如果 _logsCache 不为空,优先处理 _logsCache 队列的日志,如果为空, 则将 _logs 队列中的日志转移到 _logsCache,采用 std::deque中 swap接口,这个接口处理起来比较快,详细的资料可以自行查阅相关文档。 _freeLogDatas空闲队列,类似与回收站功能 ,遇到不合法的日志将丢弃在这个队列里面,如果有新的日志,则优先查看这个队列, 从里面pop出来,利用其空间,重新进行赋值,然后再push进 _logs队列 *///! log queue char _chunk1[256]; LockHelper_logLock; // 该队列的锁 std::deque _logs; unsigned long long _ullStatusTotalPushLog; char _chunk2[256]; LockHelper_freeLock; std::vector _freeLogDatas; char _chunk3[256]; //show color lock LockHelper _scLock; //status statistics //write file char _chunk4[256]; std::deque _logsCache; // log4z日志库库信息统计 unsigned long long _ullStatusTotalPopLog; //弹出的总的日志条数 unsigned long long _ullStatusTotalWriteFileCount; // 写入文件的条数 unsigned long long _ullStatusTotalWriteFileBytes; // 写入文件的总的字节数 };

2.3.3 主要类接口介绍 run 源码
void LogerManager::run() { _runing = true; LOGA("-----------------log4z thread started!----------------------------"); for (int i = 0; i <= _lastId; i++) { if (_loggers[i]._enable) { LOGA("logger id=" << i << " key=" << _loggers[i]._key << " name=" << _loggers[i]._name << " path=" << _loggers[i]._path << " level=" << _loggers[i]._level << " display=" << _loggers[i]._display); } } // post 一个信号量,让start 函数进行返回 _semaphore.post(); LogData * pLog = NULL; int needFlush[LOG4Z_LOGGER_MAX] = {0}; time_t lastCheckUpdate = time(NULL); // 进入主循环 while (true) { // 从缓存队列中弹出一条日志进行处理 while(popLog(pLog)) { // 合法性判断 if (pLog->_id <0 || pLog->_id > _lastId) { freeLogData(pLog); continue; } // 找到该条日志对应的记录器 LoggerInfo & curLogger = _loggers[pLog->_id]; if (pLog->_type != LDT_GENERAL) { onHotChange(pLog->_id, (LogDataType)pLog->_type, pLog->_typeval, std::string(pLog->_content, pLog->_contentLen)); curLogger._handle.close(); freeLogData(pLog); continue; }// 更改弹出的计数 _ullStatusTotalPopLog ++; //discard 如果记录器没有使能或者 输出级别控制,则直接丢弃if (!curLogger._enable || pLog->_level _content, pLog->_level); } if (LOG4Z_ALL_DEBUGOUTPUT_DISPLAY ) { #ifdef WIN32 OutputDebugStringA(pLog->_content); #endif }// 写入日志文件 if (curLogger._outfile ) { //判断记录器是否打开 if (!openLogger(pLog)) { freeLogData(pLog); continue; } // 写入文件并更新统计信息 curLogger._handle.write(pLog->_content, pLog->_contentLen); curLogger._curWriteLen += (unsigned int)pLog->_contentLen; needFlush[pLog->_id] ++; _ullStatusTotalWriteFileCount++; _ullStatusTotalWriteFileBytes += pLog->_contentLen; } else { _ullStatusTotalWriteFileCount++; _ullStatusTotalWriteFileBytes += pLog->_contentLen; }freeLogData(pLog); } // 更新所有记录器,将文件缓冲区中内容刷进磁盘 for (int i=0; i<=_lastId; i++) { if (_loggers[i]._enable && needFlush[i] > 0) { _loggers[i]._handle.flush(); needFlush[i] = 0; } if(!_loggers[i]._enable && _loggers[i]._handle.isOpen()) { _loggers[i]._handle.close(); } }//! delay. 延时50ms sleepMillisecond(50); //! quit 如果有结束标志或者日志为空,退出 if (!_runing && _logs.empty()) { break; } // 定时查看配置文件是否改变 if (_hotUpdateInterval != 0 && time(NULL) - lastCheckUpdate > _hotUpdateInterval) { updateConfig(); lastCheckUpdate = time(NULL); }} // 关闭所有记录器 for (int i=0; i <= _lastId; i++) { if (_loggers[i]._enable) { _loggers[i]._enable = false; closeLogger(i); } }}

时序图 log4z源码解析
文章图片

popLog(LogData *& log) 弹出一条日志
源码
bool LogerManager::popLog(LogData *& log) { /*如果 _logsCache 不为空,优先处理 _logsCache 队列的日志,如果为空, 则将 _logs 队列中的日志转移到 _logsCache,*/ if (_logsCache.empty()) { if (!_logs.empty()) { AutoLock l(_logLock); if (_logs.empty()) { return false; } _logsCache.swap(_logs); } } if (!_logsCache.empty()) { log = _logsCache.front(); _logsCache.pop_front(); return true; } return false; }

调用关系 log4z源码解析
文章图片

log4z源码解析
文章图片

configFromStringImpl 解析配置 源码解析
bool LogerManager::configFromStringImpl(std::string content, bool isUpdate) { // 通过计算校验和方式来判断配置文件是否发生了变化,如果没有发生变化直接返回 unsigned int sum = 0; for (std::string::iterator iter = content.begin(); iter != content.end(); ++iter) { sum += (unsigned char)*iter; } if (sum == _checksum) { return true; } _checksum = sum; /************************************************************************************ 配置文件内容 [Main] #path=./MainLog/ #level = ALL #display = true #monthdir = false #fileline = false #enable = false #outfile = false[fromfile] path = ./fromfile #level=DEBUG #display=true #monthdir = false #enable = false[mysql] path = ./stress display=false limitsize=10[network] path = ./stress display=false limitsize=10 [moniter] path = ./stress display=false limitsize=10将配置文件解析成一个 map std::map loggerMap; 下面将一一介绍该结构 struct LoggerInfo { //! attribute属性 std::string _key; // 记录器的关键字 logger key std::string _name; // 记录器的名字 one logger one name. std::string _path; // 该记录器日志文件的路径 path for log file. int_level; // 记录器的过滤级别filter level bool _display; //是否显示到屏幕 display to screen bool _outfile; //是否输出单文件 output to file bool _monthdir; //是否每个月穿件目录 create directory per month unsigned int _limitsize; //文件大小限制 单位为 MB limit file's size, unit Million byte. bool _enable; // 记录器使能标志 logger is enable bool _fileLine; //是否在每行添加文件名,行号 enable/disable the log's suffix.(file name:line number) time_t _logReserveTime; //日志文件保留的时间 单位 秒 log file reserve time. unit is time second. //! runtime info 运行时的信息 time_t _curFileCreateTime; //文件创建的事件file create time time_t _curFileCreateDay; //创建文件的日期 file create day time unsigned int _curFileIndex; //当前文件的序号 rolling file index unsigned int _curWriteLen; //当前文件的长度 current file length Log4zFileHandler_handle; //日志文件的文件句柄 file handle. //!history 历史日志 std::list > _historyLogs; LoggerInfo() { _enable = false; _path = LOG4Z_DEFAULT_PATH; _level = LOG4Z_DEFAULT_LEVEL; _display = LOG4Z_DEFAULT_DISPLAY; _outfile = LOG4Z_DEFAULT_OUTFILE; _monthdir = LOG4Z_DEFAULT_MONTHDIR; _limitsize = LOG4Z_DEFAULT_LIMITSIZE; _fileLine = LOG4Z_DEFAULT_SHOWSUFFIX; _curFileCreateTime = 0; _curFileCreateDay = 0; _curFileIndex = 0; _curWriteLen = 0; _logReserveTime = 0; } }; (gdb) p loggerMap $1 = std::map with 5 elements = { ["Main"] = {_key = "Main", _name = "Main", _path = "./log/", _level = 1, _display = true, _outfile = true, _monthdir = false, _limitsize = 100, _enable = true, _fileLine = true, _logReserveTime = 0, _curFileCreateTime = 0, _curFileCreateDay = 0, _curFileIndex = 0, _curWriteLen = 0, _handle = {_file = 0x0}, _historyLogs = empty std::__cxx11::list}, ["fromfile"] = {_key = "fromfile", _name = "fromfile", _path = "./fromfile", _level = 1, _display = true, _outfile = true, _monthdir = false, _limitsize = 100, _enable = true, _fileLine = true, _logReserveTime = 0, _curFileCreateTime = 0, _curFileCreateDay = 0, _curFileIndex = 0, _curWriteLen = 0, _handle = { _file = 0x0}, _historyLogs = empty std::__cxx11::list}, ["moniter"] = {_key = "moniter", _name = "moniter", _path = "./stress", _level = 1, _display = false, _outfile = true, _monthdir = false, _limitsize = 10, _enable = true, _fileLine = true, _logReserveTime = 0, _curFileCreateTime = 0, _curFileCreateDay = 0, _curFileIndex = 0, _curWriteLen = 0, _handle = {_file = 0x0}, _historyLogs = empty std::__cxx11::list}, ["mysql"] = {_key = "mysql", _name = "mysql", _path = "./stress", _level = 1, _display = false, _outfile = true, _monthdir = false, _limitsize = 10, _enable = true, _fileLine = true, _logReserveTime = 0, _curFileCreateTime = 0, _curFileCreateDay = 0, _curFileIndex = 0, _curWriteLen = 0, _handle = {_file = 0x0}, _historyLogs = empty std::__cxx11::list}, ["network"] = {_key = "network", _name = "network", _path = "./stress", _level = 1, _display = false, _outfile = true, _monthdir = false, _limitsize = 10, _enable = true, _fileLine = true, _logReserveTime = 0, _curFileCreateTime = 0, _curFileCreateDay = 0, _curFileIndex = 0, _curWriteLen = 0, _handle = {_file = 0x0}, _historyLogs = empty std::__cxx11::list} } *************************************************************************************/ // 将字符串形式的配置转化为 一个map std::map loggerMap; if (!parseConfigFromString(content, loggerMap)) { printf(" !!! !!! !!! !!!\r\n"); printf(" !!! !!! log4z load config file error \r\n"); printf(" !!! !!! !!! !!!\r\n"); return false; } for (std::map::iterator iter = loggerMap.begin(); iter != loggerMap.end(); ++iter) { LoggerId id = LOG4Z_INVALID_LOGGER_ID; // 查找 该记录是是否存在,如果不存在 就创建一个 id = findLogger(iter->second._key.c_str()); if (id == LOG4Z_INVALID_LOGGER_ID) { if (isUpdate) { continue; } else { id = createLogger(iter->second._key.c_str()); if (id == LOG4Z_INVALID_LOGGER_ID) { continue; } } } /* 设置该记录器各个属性,不一一列举。设置的是类中的变量 LoggerInfo _loggers[LOG4Z_LOGGER_MAX] 是一个数组,最大 支持20个 记录器 */ enableLogger(id, iter->second._enable); setLoggerName(id, iter->second._name.c_str()); setLoggerPath(id, iter->second._path.c_str()); setLoggerLevel(id, iter->second._level); setLoggerFileLine(id, iter->second._fileLine); setLoggerDisplay(id, iter->second._display); setLoggerOutFile(id, iter->second._outfile); setLoggerLimitsize(id, iter->second._limitsize); setLoggerMonthdir(id, iter->second._monthdir); } return true; }

makeLogData 制作日志数据 即:向日志前添加 时间 线程ID 日志级别信息 添加的信息例子如下:2020-07-06 10:32:22.873 [12756] LOG_DEBUG
源码
LogData * LogerManager::makeLogData(LoggerId id, int level) { LogData * pLog = NULL; // 首先查看向量 std::vector _freeLogDatas; 这个相当于回收站,不合法的日志将其放入_freeLogDatas ,在此利用其空间赋予别的值 if (true) { if (!_freeLogDatas.empty()) { AutoLock l(_freeLock); if (!_freeLogDatas.empty()) { pLog = _freeLogDatas.back(); _freeLogDatas.pop_back(); } } if (pLog == NULL) { pLog = new(malloc(sizeof(LogData) + LOG4Z_LOG_BUF_SIZE-1))LogData(); } } //append precise time to log // 添加精确的时间,线程ID 到 log中去; if (true) { pLog->_id = id; pLog->_level = level; pLog->_type = LDT_GENERAL; pLog->_typeval = 0; pLog->_threadID = 0; pLog->_contentLen = 0; #ifdef WIN32 FILETIME ft; GetSystemTimeAsFileTime(&ft); unsigned long long now = ft.dwHighDateTime; now <<= 32; now |= ft.dwLowDateTime; now /= 10; now -= 11644473600000000ULL; now /= 1000; pLog->_time = now / 1000; pLog->_precise = (unsigned int)(now % 1000); #else struct timeval tm; gettimeofday(&tm, NULL); pLog->_time = tm.tv_sec; pLog->_precise = tm.tv_usec / 1000; #endif #ifdef WIN32 pLog->_threadID = GetCurrentThreadId(); #elif defined(__APPLE__) unsigned long long tid = 0; pthread_threadid_np(NULL, &tid); pLog->_threadID = (unsigned int) tid; #else pLog->_threadID = (unsigned int)syscall(SYS_gettid); #endif }//format log // 将时间 线程ID进行格式化处理 最终处理成这种形式 2020-07-06 10:32:20.722 [12756] LOG_FATAL if (true) { #ifdef WIN32 static __declspec(thread) tm g_tt = { 0 }; static __declspec(thread) time_t g_curDayTime =0 ; #else static __thread tm g_tt = { 0 }; static __thread time_t g_curDayTime = 0; #endif // WIN32 if (pLog->_time < g_curDayTime || pLog->_time >= g_curDayTime + 24*3600) { g_tt = timeToTm(pLog->_time); g_tt.tm_hour = 0; g_tt.tm_min = 0; g_tt.tm_sec = 0; g_curDayTime = mktime(&g_tt); } time_t sec = pLog->_time - g_curDayTime; Log4zStream ls(pLog->_content, LOG4Z_LOG_BUF_SIZE); ls.writeULongLong(g_tt.tm_year + 1900, 4); ls.writeChar('-'); ls.writeULongLong(g_tt.tm_mon + 1, 2); ls.writeChar('-'); ls.writeULongLong(g_tt.tm_mday, 2); ls.writeChar(' '); ls.writeULongLong(sec/3600, 2); ls.writeChar(':'); ls.writeULongLong((sec % 3600)/60 , 2); ls.writeChar(':'); ls.writeULongLong(sec % 60, 2); ls.writeChar('.'); ls.writeULongLong(pLog->_precise, 3); ls.writeChar(' '); ls.writeChar('['); ls.writeULongLong(pLog->_threadID, 4); ls.writeChar(']'); ls.writeChar(' '); ls.writeString(LOG_STRING[pLog->_level], LOG_STRING_LEN[pLog->_level]); ls.writeChar(' '); pLog->_contentLen = ls.getCurrentLen(); } return pLog; }

2.4 其他工具类
// //! LockHelper // // 互斥锁 class LockHelper { public: LockHelper(); virtual ~LockHelper(); public: void lock(); void unLock(); private: #ifdef WIN32 CRITICAL_SECTION _crit; #else pthread_mutex_t_crit; #endif }; // //! AutoLock // /* 自动锁 这个锁只需定义一个变量无需进行手动解锁 使用示例: AutoLock l(_freeLock); if (!_freeLogDatas.empty()) { pLog = _freeLogDatas.back(); _freeLogDatas.pop_back(); } */ class AutoLock { public: // explicit 关键字的使用 explicit AutoLock(LockHelper & lk):_lock(lk){_lock.lock(); } ~AutoLock(){_lock.unLock(); } private: LockHelper & _lock; }; // //! SemHelper // /* log4z信号量 操作,用于日志主线程时的同步 */ class SemHelper { public: SemHelper(); virtual ~SemHelper(); public: bool create(int initcount); bool wait(int timeout = 0); bool post(); private: #ifdef WIN32 HANDLE _hSem; #elif defined(__APPLE__) dispatch_semaphore_t _semid; #else sem_t _semid; bool_isCreate; #endif};

2.5 日志格式
// 二进制 class Log4zBinary { public: Log4zBinary(const void * buf, size_t len) { this->buf = (const char *)buf; this->len = len; } const char * buf; size_tlen; }; // 字符串 class Log4zString { public: Log4zString(const char * buf, size_t len) { this->buf = (const char *)buf; this->len = len; } const char * buf; size_tlen; }; // 格式化处理 class Log4zStream { // 查看源码比较简单 }

2.6 其他工具类接口
static void fixPath(std::string &path);
// 调整日志的配置
static void trimLogConfig(std::string &str, std::string extIgnore = std::string());
// 分割字符串
static std::pair splitPairString(const std::string & str, const std::string & delimiter);
// 判断是否是一个目录
static bool isDirectory(std::string path);
// 创建递归目录
static bool createRecursionDir(std::string path);
// 获取进程ID
static std::string getProcessID();
// 获取进程的名字
static std::string getProcessName();
2.7 默认的宏定义
//! LOG Level // 日志的级别 enum ENUM_LOG_LEVEL { LOG_LEVEL_TRACE = 0, // 追踪 LOG_LEVEL_DEBUG,// 调试 LOG_LEVEL_INFO,// 信息 LOG_LEVEL_WARN,// 警告 LOG_LEVEL_ERROR,// 错误 LOG_LEVEL_ALARM,// 报警 LOG_LEVEL_FATAL,// 致命错误 }; // //! -----------------default logger config, can change on this.----------- // 默认记录器的配置,想更改默认配置可以更改一下配置 // //! the max logger count.记录器最大支持的个数 默认 20 const int LOG4Z_LOGGER_MAX = 20; //! the max log content length.单条日志 记录的最大程度 const int LOG4Z_LOG_BUF_SIZE = 1024 * 8; //! the max stl container depth.STL容器最大深度 const int LOG4Z_LOG_CONTAINER_DEPTH = 5; //! the log queue length limit size. std::deque _logs; 队列的最大深度 const int LOG4Z_LOG_QUEUE_LIMIT_SIZE = 20000; //! all logger synchronous output or not所有的记录器是否同步输出 const bool LOG4Z_ALL_SYNCHRONOUS_OUTPUT = false; //! all logger synchronous display to the windows debug output所有记录器是否同步输出到屏幕 const bool LOG4Z_ALL_DEBUGOUTPUT_DISPLAY = false; //! default logger output file. 记录器默认文件夹 const char* const LOG4Z_DEFAULT_PATH = "./log/"; //! default log filter level日志记录器默认的 级别 const int LOG4Z_DEFAULT_LEVEL = LOG_LEVEL_DEBUG; //! default logger display默认是否显示 const bool LOG4Z_DEFAULT_DISPLAY = true; //! default logger output to file 默认是否到文件 const bool LOG4Z_DEFAULT_OUTFILE = true; //! default logger month dir used status默认是否使用月度文件夹 const bool LOG4Z_DEFAULT_MONTHDIR = false; //! default logger output file limit size, unit M byte.默认的输出文件的大小 单位 MB 100MB const int LOG4Z_DEFAULT_LIMITSIZE = 100; //! default logger show suffix (file name and line number)默认在每条日志添加 文件名和行号 const bool LOG4Z_DEFAULT_SHOWSUFFIX = true; //! support ANSI->OEM console conversion on Windows #undef LOG4Z_OEM_CONSOLE //! default logger force reserve log file count. const size_t LOG4Z_FORCE_RESERVE_FILE_COUNT = 7;

2.8 记录日志的宏定义
调用关系 XX代表日志级别
LOGFXX( fmt, ...) -> FMT_XX(LOG4Z_MAIN_LOGGER_ID, fmt,##__VA_ARGS__) -> LOG_FORMAT

#define LOG_FORMAT(id, level, file, line, logformat, ...) \ do{ \ if (zsummer::log4z::ILog4zManager::getPtr()->prePushLog(id,level)) \ {\ zsummer::log4z::LogData * __pLog = zsummer::log4z::ILog4zManager::getPtr()->makeLogData(id, level); \ int __logLen = snprintf(__pLog->_content + __pLog->_contentLen, LOG4Z_LOG_BUF_SIZE - __pLog->_contentLen,logformat, ##__VA_ARGS__); \ if (__logLen < 0) __logLen = 0; \ if (__logLen > LOG4Z_LOG_BUF_SIZE - __pLog->_contentLen) __logLen = LOG4Z_LOG_BUF_SIZE - __pLog->_contentLen; \ __pLog->_contentLen += __logLen; \ zsummer::log4z::ILog4zManager::getPtr()->pushLog(__pLog, file, line); \ } \ }while(0)

过程调用非常清晰,不再进行解释。
3 总结 【log4z源码解析】通过分析log4z源码可以了解一个典型日志库的实现方式,麻雀虽小,五脏俱全,log4z日志库最核心的模式就是生产者 -- 消费者模式.
LOG_FORMAT 不断将日志推送到队列中去,而 run函数进行取队列,输出到文件或者屏幕,后期可以根据源码进行加工和改进。

    推荐阅读