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源码解析_第1张图片

通过上述的类图,log4z的类之间的关系非常简单,只有一层继承关系,接下来我们将一一介绍上述类图中各个类的实现

2 各个类详解

2.1 ILog4zManager ---- log4z日志库的抽象基类

内部结构图

ILog4zManager 是一个抽象接口类,定义了log4z主要的操作

log4z源码解析_第2张图片

源码注释

/*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源码解析_第3张图片

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源码解析_第4张图片

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源码解析_第5张图片

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源码解析_第6张图片

log4z源码解析_第7张图片

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_t  len;
};
// 字符串
class Log4zString
{
public:
    Log4zString(const char * buf, size_t len)
    {
        this->buf = (const char *)buf;
        this->len = len;
    }
    const char * buf;
    size_t  len;
};
// 格式化处理
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日志库最核心的模式就是生产者 -- 消费者模式.
LOG_FORMAT 不断将日志推送到队列中去,而 run函数进行取队列,输出到文件或者屏幕,后期可以根据源码进行加工和改进。

你可能感兴趣的:(日志库(Log))