欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

log4z源码解析

程序员文章站 2022-05-12 20:17:39
...

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<std::string, LoggerId> _ids; 
    // the last used id of _loggers  最后使用的记录器ID
    LoggerId    _lastId; 
    //记录器的信息
    LoggerInfo _loggers[LOG4Z_LOGGER_MAX];

	/*
	log4z 三种队列的关系图 
	_logs 日志的队列,通过 pushLog 或者 hotChange 接口向 std::deque<LogData *> _logs; 队列中添加日志数据添加到该队列中
	_logsCache 日志缓存队列,在popLog时,如果 _logsCache 不为空,优先处理 _logsCache 队列的日志,如果为空,
				则将 _logs 队列中的日志转移到 _logsCache,采用 std::deque中 swap接口,这个接口处理起来比较快,详细的资料可以自行查阅相关文档。
    
	_freeLogDatas  空闲队列,类似与回收站功能 ,遇到不合法的日志将丢弃在这个队列里面,如果有新的日志,则优先查看这个队列,
					从里面pop出来,利用其空间,重新进行赋值,然后再push进 _logs队列

	*/


    
    //! log queue
    char _chunk1[256];
    LockHelper    _logLock;  // 该队列的锁
    std::deque<LogData *> _logs;
    unsigned long long _ullStatusTotalPushLog;

    char _chunk2[256];
    LockHelper    _freeLock;
    std::vector<LogData*> _freeLogDatas;

    char _chunk3[256];
    //show color lock
    LockHelper _scLock;
    //status statistics
    //write file
    char _chunk4[256];
    std::deque<LogData *> _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 <curLogger._level  )
            {
                freeLogData(pLog);
                continue;
            }

			// 根据记录器的属性判断是否终端打印
            if (curLogger._display)
            {
                showColorText(pLog->_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<std::string, LoggerInfo> 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<std::pair<time_t, std::string> > _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<std::string, LoggerInfo> loggerMap;
    if (!parseConfigFromString(content, loggerMap))
    {
        printf(" !!! !!! !!! !!!\r\n");
		printf(" !!! !!! log4z load config file error \r\n");
		printf(" !!! !!! !!! !!!\r\n");
        return false;
    }
    for (std::map<std::string, LoggerInfo>::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<LogData*> _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<std::string, std::string> 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<LogData *> _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)