log4z
1.基本介绍
- 地址: https://github.com/zsummer/log4z
- 支持 windows/linux/mac/android/iOS
- 一个头文件,一个cpp
- 日志滚动, 可以按月分文件夹
- 线程安全
- 彩色输出
- 格式化或流形式的记录
- 热更新配置
- 多日志分流,可以输出到多个位置(logger)
- MIT协议,基本没限制
2. 配置项
放到单独的配置文件中: log4z.cfg
[Main] # logger名字,日志分流
path = ./log/ # 日志目录
level = trace # trace, debug, info, warn/warning, error, alarm, fatal
display = false/0 # 不输出到屏幕
outfile = false/0 # 不输出到文件
monthdir = false/0 # 不使用月文件夹滚动
limitsize = 10 # MB, 日志文件滚动大小
fileline = false/0 # 是否输出源文件行,便于排查函数
enable = false/0 # 是否启用
reserve = 60 # s, 日志保留时间
3. 接口
3.1. ILog4zManager
是个总控,基础控制接口,下边的stream基于这个接口
getRef();
config(const char * configPath); // 加载配置
// 有个默认的logger: LOG4Z_MAIN_LOGGER_ID, 可以直接使用
LoggerId createLogger(const char* key); // 创建logger分流
LoggerId findLogger(const char* key); // 获取
bool enableLogger(LoggerId id, bool enable) = 0; // immediately when enable, and queue up when disable.
bool setLoggerName(LoggerId id, const char * name) = 0;
bool setLoggerPath(LoggerId id, const char * path) = 0;
bool setLoggerLevel(LoggerId id, int nLevel) = 0; // immediately when enable, and queue up when disable.
bool setLoggerFileLine(LoggerId id, bool enable) = 0;
bool setLoggerDisplay(LoggerId id, bool enable) = 0;
bool setLoggerOutFile(LoggerId id, bool enable) = 0;
bool setLoggerLimitsize(LoggerId id, unsigned int limitsize) = 0;
bool setLoggerMonthdir(LoggerId id, bool enable) = 0;
bool setLoggerReserveTime(LoggerId id, time_t sec) = 0;
bool start(); // 开启日志线程
bool stop();
3.2. Log4zStream
支持流式输入
类似 stringstream
用各种重载<<
操作符,来支持各个数据类型
比如流式输出
LOGD("char:" << 'c'
<< ", unsigned char:" << (unsigned char) 'c'
<< ", short:" << (short)-1
<< ", unsigned short:" << (unsigned short)-1
<< ", int:" << (int)-1
<< ", unsigned int:" << (unsigned int)-1
<< ", long:" << (long)-1
<< ", unsigned long:" << (unsigned long)-1
<< ", long long:" << (long long)-1
<< ", unsigned long long:" << (unsigned long long) - 1
<< ", float:" << (float)-1.234567
<< ", double:" << (double)-2.34566
<< ", double:" << pow(2, 52) - 1.0
<< ", double:" << pow(2, 52) * -1000
<< ", double:" << pow(2, 52) / 1000
<< ", double:" << pow(2, 52) / -1000
<< ", double:" << pow(2, -58)
<< ", double:" << pow(2, -16)*-1
<< ", std::string:" << std::string("fffff")
<< ", int *:" << (int *)argv
<< ", const int *:" << (const int *)argv
<< ", constant:" << 1000
<< ", constant:" << 100.12345678
<< ", bool:" << true
<< ", show hex data:" << Log4zBinary("1234567890abcdefghigklmnopqrstuvwxyz_zyw_zsummer_log4z", 50)
);
遇到 << 1000
, 会去找
inline Log4zStream & operator <<(int t){return writeLongLong(t);}
inline Log4zStream & Log4zStream::writeLongLong(long long t, int width, int dec)
{
if (t < 0 )
{
t = -t;
writeChar('-');
}
writeULongLong((unsigned long long)t, width, dec);
return *this;
}
inline Log4zStream & zsummer::log4z::Log4zStream::writeChar(char ch)
{
if (_end - _cur > 1)
{
_cur[0] = ch; // 这里是实际写到一段内存,最后 logger 会将这段内存整体输出
_cur++;
}
return *this;
}
其他格式也是这样输出的。
3.3. 方便使用的宏
支持流式输出的宏
//! fast macro
#define LOG_TRACE(id, log) LOG_STREAM(id, LOG_LEVEL_TRACE, __FILE__, __LINE__, log)
#define LOG_DEBUG(id, log) LOG_STREAM(id, LOG_LEVEL_DEBUG, __FILE__, __LINE__, log)
#define LOG_INFO(id, log) LOG_STREAM(id, LOG_LEVEL_INFO, __FILE__, __LINE__, log)
#define LOG_WARN(id, log) LOG_STREAM(id, LOG_LEVEL_WARN, __FILE__, __LINE__, log)
#define LOG_ERROR(id, log) LOG_STREAM(id, LOG_LEVEL_ERROR, __FILE__, __LINE__, log)
#define LOG_ALARM(id, log) LOG_STREAM(id, LOG_LEVEL_ALARM, __FILE__, __LINE__, log)
#define LOG_FATAL(id, log) LOG_STREAM(id, LOG_LEVEL_FATAL, __FILE__, __LINE__, log)
//! super macro.
#define LOGT( log ) LOG_TRACE(LOG4Z_MAIN_LOGGER_ID, log )
#define LOGD( log ) LOG_DEBUG(LOG4Z_MAIN_LOGGER_ID, log )
#define LOGI( log ) LOG_INFO(LOG4Z_MAIN_LOGGER_ID, log )
#define LOGW( log ) LOG_WARN(LOG4Z_MAIN_LOGGER_ID, log )
#define LOGE( log ) LOG_ERROR(LOG4Z_MAIN_LOGGER_ID, log )
#define LOGA( log ) LOG_ALARM(LOG4Z_MAIN_LOGGER_ID, log )
#define LOGF( log ) LOG_FATAL(LOG4Z_MAIN_LOGGER_ID, log )
支持格式化的宏
//!format string
#define LOGFMT_TRACE(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMT_DEBUG(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMT_INFO(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_INFO, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMT_WARN(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_WARN, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMT_ERROR(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMT_ALARM(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_ALARM, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMT_FATAL(id, fmt, ...) LOG_FORMAT(id, LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define LOGFMTT( fmt, ...) LOGFMT_TRACE(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
#define LOGFMTD( fmt, ...) LOGFMT_DEBUG(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
#define LOGFMTI( fmt, ...) LOGFMT_INFO(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
#define LOGFMTW( fmt, ...) LOGFMT_WARN(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
#define LOGFMTE( fmt, ...) LOGFMT_ERROR(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
#define LOGFMTA( fmt, ...) LOGFMT_ALARM(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
#define LOGFMTF( fmt, ...) LOGFMT_FATAL(LOG4Z_MAIN_LOGGER_ID, fmt, ##__VA_ARGS__)
4. 线程模型
一个后台线程持续往日志文件写; 各种接口线程安全。
bool LogerManager::start()
{
if (_runing) // 只会启动一个日志线程
{
showColorText("log4z already start \r\n", LOG_LEVEL_FATAL);
return false;
}
_semaphore.create(0);
bool ret = ThreadHelper::start(); // 启动日志线程
return ret && _semaphore.wait(3000); // 这里用信号量同步一下线程,等到日志线程启动后,这里再退出
}
ThreadHelper::start()
进而调用 int ret = pthread_create(&_phtreadID, NULL, threadProc, (void*)this);
启动 threadProc()
,最后会调用void LogerManager::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);
}
}
_semaphore.post(); // 通知用户线程LogerManager::start() 函数可以返回了
...
}
对线程和信号量都做了封装,以跨平台。
// 线程封装
class ThreadHelper
{
public:
ThreadHelper(){_hThreadID = 0;}
virtual ~ThreadHelper(){}
public:
bool start();
bool wait();
virtual void run() = 0;
private:
unsigned long long _hThreadID;
#ifndef WIN32
pthread_t _phtreadID;
#endif
};
// 信号量封装
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
};