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 
", 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
};
原文地址:https://www.cnblogs.com/suntus/p/15226308.html