log4z源码解析
文章目录
log4z源码解析
1. 继承关系图
通过上述的类图,log4z的类之间的关系非常简单,只有一层继承关系,接下来我们将一一介绍上述类图中各个类的实现
2 各个类详解
2.1 ILog4zManager ---- log4z日志库的抽象基类
内部结构图
ILog4zManager 是一个抽象接口类,定义了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 内部结构图
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内部接口调用关系图
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);
}
}
}
时序图
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;
}
调用关系
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日志信息统计与画图
下一篇: 苹果放弃ZFS开源文件系统计划