mars xlog源码分析

转载请标明出处: http://blog.csdn.net/linuxfu/article/details/61915473

前言

看xlog源码的初衷是为了将xlog运用在项目中,因为项目中已经有了一些对于日志的操作逻辑,希望xlog能适应之前的逻辑,使整个日志的替换能平滑过渡,因此需要对xlog作一些修改,例如表现在如下几个方面:
–修改xlog的日志清理操作;
–修改压缩方式;
–修改xlog默认的格式;
–加密方式的增加;

看完下面的代码分析,对于上面几个方面应该可以知道怎么修改。

代码风格

sg_ 开头的变量,static global 全局static
PtrBuffer buff_ 变量名后带_,表示成员变量
bool __Reset(); 函数前面带__,表示成员函数
AutoBuffer& _buff 变量名前带_,表示局部变量

代码分析

我们先从入口开始分析,主要也就这两个native函数,具体的jni接口定义在Java2C_Xlog.cc中

public static native void appenderOpen(int level, int mode, String cacheDir, String logDir, String nameprefix);
public static native void logWrite2(int level, String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);

以logWrite2函数为例,主要是调用了xlogger_Write。appenderOpen则是调用了appender_open_with_cache

DEFINE_FIND_STATIC_METHOD(KXlog_logWrite2, KXlog, "logWrite2", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIJJLjava/lang/String;)V")
JNIEXPORT void JNICALL Java_com_tencent_mars_xlog_Xlog_logWrite2
  (JNIEnv *env, jclass, int _level, jstring _tag, jstring _filename,
          jstring _funcname, jint _line, jint _pid, jlong _tid, jlong _maintid, jstring _log) {

    if (!xlogger_IsEnabledFor((TLogLevel)_level)) {
        return;
    }

    XLoggerInfo xlog_info;
    gettimeofday(&xlog_info.timeval, NULL);
    xlog_info.level = (TLogLevel)_level;
    xlog_info.line = (int)_line;
    xlog_info.pid = (int)_pid;
    xlog_info.tid = LONGTHREADID2INT(_tid);
    xlog_info.maintid = LONGTHREADID2INT(_maintid);

    const char* tag_cstr = NULL;
    const char* filename_cstr = NULL;
    const char* funcname_cstr = NULL;
    const char* log_cstr = NULL;

    if (NULL != _tag) {
        tag_cstr = env->GetStringUTFChars(_tag, NULL);
    }

    if (NULL != _filename) {
        filename_cstr = env->GetStringUTFChars(_filename, NULL);
    }

    if (NULL != _funcname) {
        funcname_cstr = env->GetStringUTFChars(_funcname, NULL);
    }

    if (NULL != _log) {
        log_cstr = env->GetStringUTFChars(_log, NULL);
    }

    xlog_info.tag = NULL == tag_cstr ? "" : tag_cstr;
    xlog_info.filename = NULL == filename_cstr ? "" : filename_cstr;
    xlog_info.func_name = NULL == funcname_cstr ? "" : funcname_cstr;

    xlogger_Write(&xlog_info, NULL == log_cstr ? "NULL == log" : log_cstr);

    if (NULL != _tag) {
        env->ReleaseStringUTFChars(_tag, tag_cstr);
    }

    if (NULL != _filename) {
        env->ReleaseStringUTFChars(_filename, filename_cstr);
    }

    if (NULL != _funcname) {
        env->ReleaseStringUTFChars(_funcname, funcname_cstr);
    }

    if (NULL != _log) {
        env->ReleaseStringUTFChars(_log, log_cstr);
    }
}

我们先来分析下appender_open_with_cache,先删除_cachedir里面过期的日志,删除逻辑是根据日志文件的上次修改时间,现在是10天前的日志就删除。注意可能会删除文件夹里面所有文件,这儿做的不够好,应该根据后缀名删除的,更合理些;__move_old_files 将日志文件从_cachedir移到_logdir,这个函数中匹配了prefix和end,只有匹配到的文件才会move过去。

void appender_open_with_cache(TAppenderMode _mode, const std::string& _cachedir, const std::string& _logdir, const char* _nameprefix) {
    assert(!_cachedir.empty());
    assert(!_logdir.empty());
    assert(_nameprefix);

    sg_logdir = _logdir;

    if (!_cachedir.empty()) {
        sg_cache_logdir = _cachedir;
        boost::filesystem::create_directories(_cachedir);
        __del_timeout_file(_cachedir);   1.删除_cachedir里面过期的日志
        // "_nameprefix" must explicitly convert to "std::string", or when the thread is ready to run, "_nameprefix" has been released.
        Thread(boost::bind(&__move_old_files, _cachedir, _logdir, std::string(_nameprefix))).start_after(3 * 60 * 1000);//2.开启线程move 老文件
    }

    appender_open(_mode, _logdir.c_str(), _nameprefix);

}

static void __del_timeout_file(const std::string& _log_path) {
    time_t now_time = time(NULL);

    boost::filesystem::path path(_log_path);

    if (boost::filesystem::exists(path) && boost::filesystem::is_directory(path)){
        boost::filesystem::directory_iterator end_iter;
        for (boost::filesystem::directory_iterator iter(path); iter != end_iter; ++iter) {
            time_t fileModifyTime = boost::filesystem::last_write_time(iter->path());

            if (now_time > fileModifyTime && now_time - fileModifyTime > kMaxLogAliveTime) {//1.超过kMaxLogAliveTime就删除
                if (boost::filesystem::is_regular_file(iter->status())) {
                    boost::filesystem::remove(iter->path());
                }
                else if (boost::filesystem::is_directory(iter->status())) {//2.注意可能会删除文件夹里面所有文件
                    __del_files(iter->path().string());
                }
            }
        }
    }
}

static void __del_files(const std::string& _forder_path) {

    boost::filesystem::path path(_forder_path);
    if (!boost::filesystem::is_directory(path)) {
        return;
    }

    boost::filesystem::directory_iterator end_iter;
    for (boost::filesystem::directory_iterator iter(path); iter != end_iter; ++iter) {
        if (boost::filesystem::is_regular_file(iter->status()))
        {
            boost::filesystem::remove(iter->path());
        }
    }
}   

mark
然后再看看appender_open,在每次open的时候,都会添加一些信息,如上图。

void appender_open(TAppenderMode _mode, const char* _dir, const char* _nameprefix) {
    assert(_dir);
    assert(_nameprefix);

    if (!sg_log_close) {
        __writetips2file("appender has already been opened. _dir:%s _nameprefix:%s", _dir, _nameprefix);
        return;
    }

    xlogger_SetAppender(&xlogger_appender);

    //mkdir(_dir, S_IRWXU|S_IRWXG|S_IRWXO);
    boost::filesystem::create_directories(_dir);
    tickcount_t tick;
    tick.gettickcount();
    __del_timeout_file(_dir);//1.删除过期文件

    tickcountdiff_t del_timeout_file_time = tickcount_t().gettickcount() - tick;

    tick.gettickcount();

    char mmap_file_path[512] = {0};
    snprintf(mmap_file_path, sizeof(mmap_file_path), "%s/%s.mmap2", sg_cache_logdir.empty()?_dir:sg_cache_logdir.c_str(), _nameprefix);

    bool use_mmap = false;
    if (OpenMmapFile(mmap_file_path, kBufferBlockLength, sg_mmmap_file))  {//2.创建mmap文件
        sg_log_buff = new LogBuffer(sg_mmmap_file.data(), kBufferBlockLength, false);//3.并将文件映射到内存
        use_mmap = true;
    } else {
        char* buffer = new char[kBufferBlockLength];                //4.mmap文件创建不成功,则会选择用一个普通的buf
        sg_log_buff = new LogBuffer(buffer, kBufferBlockLength, false);
        use_mmap = false;
    }

    if (NULL == sg_log_buff->GetData().Ptr()) {
        if (use_mmap && sg_mmmap_file.is_open())  CloseMmapFile(sg_mmmap_file);
        return;
    }


    AutoBuffer buffer;
    sg_log_buff->Flush(buffer);//5.如果之前mmap中存在内容,则将内容冲到buffer中。如果发生crash了,buf中内容会保存在mmap中,下次启动会将mmap中的内容冲洗出来

    ScopedLock lock(sg_mutex_log_file);
    sg_logdir = _dir;
    sg_logfileprefix = _nameprefix;
    sg_log_close = false;
    appender_setmode(_mode);
    lock.unlock();

    char mark_info[512] = {0};
    get_mark_info(mark_info, sizeof(mark_info));

    if (buffer.Ptr()) {
        __writetips2file("~~~~~ begin of mmap ~~~~~\n");
        __log2file(buffer.Ptr(), buffer.Length());// 6.将buffer中的内容写到日志
        __writetips2file("~~~~~ end of mmap ~~~~~%s\n", mark_info);
    }

    tickcountdiff_t get_mmap_time = tickcount_t().gettickcount() - tick;
    //7.如上图,会写一些开始的内容
    char appender_info[728] = {0};
    snprintf(appender_info, sizeof(appender_info), "^^^^^^^^^^" __DATE__ "^^^" __TIME__ "^^^^^^^^^^%s", mark_info);

    xlogger_appender(NULL, appender_info);
    char logmsg[64] = {0};
    snprintf(logmsg, sizeof(logmsg), "del time out files time: %" PRIu64, (int64_t)del_timeout_file_time);
    xlogger_appender(NULL, logmsg);

    snprintf(logmsg, sizeof(logmsg), "get mmap time: %" PRIu64, (int64_t)get_mmap_time);
    xlogger_appender(NULL, logmsg);

    xlogger_appender(NULL, "MARS_URL: " MARS_URL);
    xlogger_appender(NULL, "MARS_PATH: " MARS_PATH);
    xlogger_appender(NULL, "MARS_REVISION: " MARS_REVISION);
    xlogger_appender(NULL, "MARS_BUILD_TIME: " MARS_BUILD_TIME);
    xlogger_appender(NULL, "MARS_BUILD_JOB: " MARS_TAG);

    snprintf(logmsg, sizeof(logmsg), "log appender mode:%d, use mmap:%d", (int)_mode, use_mmap);
    xlogger_appender(NULL, logmsg);

    BOOT_RUN_EXIT(appender_close);

}

然后xlogger_appender中,这个里面会根据同步或者异步分别调用__appender_sync、__appender_async。这个函数中还有s_recursion_str,不知道干啥的,不影响主流程的分析也没深究,熟悉的大神可告知;

void xlogger_appender(const XLoggerInfo* _info, const char* _log) {
    if (sg_log_close) return;

    SCOPE_ERRNO();

    DEFINE_SCOPERECURSIONLIMIT(recursion);
    static Tss s_recursion_str(free);

    if (sg_consolelog_open) ConsoleLog(_info,  _log); //1.如果开启了,android平台使用logcat输出日志

    if (2 <= (int)recursion.Get() && NULL == s_recursion_str.get()) {
        if ((int)recursion.Get() > 10) return;
        char* strrecursion = (char*)calloc(16 * 1024, 1);
        s_recursion_str.set((void*)(strrecursion));

        XLoggerInfo info = *_info;
        info.level = kLevelFatal;

        char recursive_log[256] = {0};
        snprintf(recursive_log, sizeof(recursive_log), "ERROR!!! xlogger_appender Recursive calls!!!, count:%d", (int)recursion.Get());

        PtrBuffer tmp(strrecursion, 0, 16*1024);
        log_formater(&info, recursive_log, tmp);

        strncat(strrecursion, _log, 4096);
        strrecursion[4095] = '\0';

        ConsoleLog(&info,  strrecursion);
    } else {
        if (NULL != s_recursion_str.get()) {
            char* strrecursion = (char*)s_recursion_str.get();
            s_recursion_str.set(NULL);

            __writetips2file(strrecursion);
            free(strrecursion);
        }

        if (kAppednerSync == sg_mode)
            __appender_sync(_info, _log);//2.同步写日志
        else
            __appender_async(_info, _log);//3.异步写日志
    }
}

同步模式下,__appender_sync会先格式化,然后加密,然后直接写到文件中

static void __appender_sync(const XLoggerInfo* _info, const char* _log) {

    char temp[16 * 1024] = {0};     // tell perry,ray if you want modify size.
    PtrBuffer log(temp, 0, sizeof(temp));
    log_formater(_info, _log, log);//1.格式化log

    char buffer_crypt[16 * 1024] = {0};
    size_t len = 16 * 1024;
    if (!LogBuffer::Write(log.Ptr(), log.Length(), buffer_crypt, len))   return;//2.加密日志

    __log2file(buffer_crypt, len);//3.写日志文件
}
bool LogBuffer::Write(const void* _data, size_t _inputlen, void* _output, size_t& _len) {
    if (NULL == _data || NULL == _output || 0 == _inputlen || _len <= (size_t) s_log_crypt->GetHeaderLen()) {
        return false;
    }

    s_log_crypt->CryptSyncLog((char*)_data, _inputlen, (char*)_output, _len);//目前没有加密,只是单纯的拷贝了下buf。加密逻辑需要自己添加

    return true;
}

异步模式,和同步模式类似,不过是只写到buf中,超过总长1/3才会通知写日志

static void __appender_async(const XLoggerInfo* _info, const char* _log) {
    ScopedLock lock(sg_mutex_buffer_async);
    if (NULL == sg_log_buff) return;

    char temp[16*1024] = {0};       //tell perry,ray if you want modify size.
    PtrBuffer log_buff(temp, 0, sizeof(temp));
    log_formater(_info, _log, log_buff);     //1.格式化log

    if (sg_log_buff->GetData().Length() >= kBufferBlockLength*4/5) {//2.这个应该算是一种异常情况,所以将其记录了下来
       int ret = snprintf(temp, sizeof(temp), "[F][ sg_buffer_async.Length() >= BUFFER_BLOCK_LENTH*4/5, len: %d\n", (int)sg_log_buff->GetData().Length());
       log_buff.Length(ret, ret);
    }

    if (!sg_log_buff->Write(log_buff.Ptr(), (unsigned int)log_buff.Length())) return; //3.将log_buff写到sg_log_buff

    if (sg_log_buff->GetData().Length() >= kBufferBlockLength*1/3 || (NULL!=_info && kLevelFatal == _info->level)) {//4.在buf长度>=1/3总长,就通知写日志
       sg_cond_buffer_async.notifyAll();
    }

}

bool LogBuffer::Write(const void* _data, size_t _length) {
    if (NULL == _data || 0 == _length) {
        return false;
    }

    if (buff_.Length() == 0) {
        if (!__Reset()) return false;
    }

    size_t before_len = buff_.Length();
    size_t write_len = _length;

    if (is_compress_) {   //1.压缩,每次压缩一条日志
        cstream_.avail_in = (uInt)_length;
        cstream_.next_in = (Bytef*)_data;

        uInt avail_out = (uInt)(buff_.MaxLength() - buff_.Length());
        cstream_.next_out = (Bytef*)buff_.PosPtr();
        cstream_.avail_out = avail_out;

        if (Z_OK != deflate(&cstream_, Z_SYNC_FLUSH)) {
            return false;
        }

        write_len = avail_out - cstream_.avail_out;
    } else {
        buff_.Write(_data, _length);    //2.不压缩
    }


    char crypt_buffer[4096] = {0};
    size_t crypt_buffer_len = sizeof(crypt_buffer);


    s_log_crypt->CryptAsyncLog((char*)buff_.Ptr() + before_len, write_len, crypt_buffer, crypt_buffer_len);//3.加密

    uint16_t single_log_len = crypt_buffer_len;    
    buff_.Write(&single_log_len, sizeof(single_log_len), before_len);//4.加密后日志长度变化了

    before_len += sizeof(single_log_len);                    
    buff_.Write(crypt_buffer, crypt_buffer_len, before_len); //5.然后将加密后日志内容填在后面

    before_len += crypt_buffer_len;
    buff_.Length(before_len, before_len);                    //6.更新buff_长度

    s_log_crypt->UpdateLogLen((char*)buff_.Ptr(), (uint32_t)crypt_buffer_len + sizeof(single_log_len));//7.更新sg_log_buff中的长度

    return true;
}

通知了sg_cond_buffer_async,xlog中会有个线程专门用来写日志,也可以看到,如果超过15min,也是会自动写日志到文件的;

static void __async_log_thread() {
        while (true) {

            ScopedLock lock_buffer(sg_mutex_buffer_async);

            if (NULL == sg_log_buff) break;

            AutoBuffer tmp;
            sg_log_buff->Flush(tmp);//1.将sg_log_buff中的内容冲到tmp中
            lock_buffer.unlock();

            if (NULL != tmp.Ptr())  __log2file(tmp.Ptr(), tmp.Length());//2.写日志

            if (sg_log_close) break;

            sg_cond_buffer_async.wait(15 * 60 *1000);
        }
    }

这个sg_log_buff->Flush(tmp)冲洗的过程可以看下;

void LogBuffer::Flush(AutoBuffer& _buff) {

    if (Z_NULL != cstream_.state) {
        deflateEnd(&cstream_);
    }

    if (s_log_crypt->GetLogLen((char*)buff_.Ptr(), buff_.Length()) == 0){
        __Clear();
        return;
    }

    __Flush();//冲洗
    _buff.Write(buff_.Ptr(), buff_.Length());
    __Clear();
}
void LogBuffer::__Flush() {//之前做加密时有一些对应的格式,解析这些格式;
    assert(buff_.Length() >= s_log_crypt->GetHeaderLen());

    s_log_crypt->UpdateLogHour((char*)buff_.Ptr());
    s_log_crypt->SetTailerInfo((char*)buff_.Ptr() + buff_.Length());
    buff_.Length(buff_.Length() + s_log_crypt->GetTailerLen(), buff_.Length() + s_log_crypt->GetTailerLen());

}

__log2file,异步和同步最终是通过这个写到文件中。注意这个里面的逻辑,是存在两种文件:正式的日志文件(logfilepath),cache日志文件(logcachefilepath)。写的时候,1.先写入cache日志文件中,2.再写入正式日志文件中,3.再将cache日志文件删除。如果1失败了,则直接跳到2写正式日志文件,如果正式也写不成功,则又写到cache中。等到后面,再将cache中的内容转入到正式文件中。
做这么多,无非是防丢日志,尽量确保日志不会丢失;

static void __log2file(const void* _data, size_t _len) {
        if (NULL == _data || 0 == _len || sg_logdir.empty()) {
            return;
        }

        ScopedLock lock_file(sg_mutex_log_file);

        if (sg_cache_logdir.empty()) { //1.如果sg_cache_logdir是空的,则写到sg_logdir
            if (__openlogfile(sg_logdir)) {
                __writefile(_data, _len, sg_logfile);
                if (kAppednerAsync == sg_mode) {
                    __closelogfile();
                }
            }
            return;
        }

        struct timeval tv;
        gettimeofday(&tv, NULL);
        char logcachefilepath[1024] = {0};

        __make_logfilename(tv, sg_cache_logdir, sg_logfileprefix.c_str(), LOG_EXT, logcachefilepath , 1024);//2.指定logcachefilepath

        if (boost::filesystem::exists(logcachefilepath) && __openlogfile(sg_cache_logdir)) {//3.如果logcachefilepath文件存在,则打开文件并写入
            __writefile(_data, _len, sg_logfile);   //4.写入到cache文件中
            if (kAppednerAsync == sg_mode) {
                __closelogfile();
            }


            char logfilepath[1024] = {0};
            __make_logfilename(tv, sg_logdir, sg_logfileprefix.c_str(), LOG_EXT, logfilepath , 1024);
            if (__append_file(logcachefilepath, logfilepath)) {//5.同上,写入的日志文件中
                if (kAppednerSync == sg_mode) {
                    __closelogfile();
                }
                remove(logcachefilepath);          //6.然后删除cache日志文件
            }
        } else {  //7、如果打开cache文件失败,则直接写到日志文件
            bool write_sucess = false;
            bool open_success = __openlogfile(sg_logdir);
            if (open_success) {
                write_sucess = __writefile(_data, _len, sg_logfile);
                if (kAppednerAsync == sg_mode) {
                    __closelogfile();
                }
            }

            if (!write_sucess) {//8.如果没日志写成功,则先关了,再打开cache日志文件,写入到cache日志文件中
                if (open_success && kAppednerSync == sg_mode) {
                    __closelogfile();
                }

                if (__openlogfile(sg_cache_logdir)) {
                    __writefile(_data, _len, sg_logfile);
                    if (kAppednerAsync == sg_mode) {
                        __closelogfile();
                    }
                }
            }
        }

    }

同时冲洗缓存也存在异步和同步两种,和前面的非常类似

//同步模式冲洗缓存
void appender_flush_sync() {
if (kAppednerSync == sg_mode) {
    return;
}

ScopedLock lock_buffer(sg_mutex_buffer_async);//加锁

if (NULL == sg_log_buff) return;

AutoBuffer tmp;
sg_log_buff->Flush(tmp);//将sg_log_buff里面的空间flush到tmp上

lock_buffer.unlock();

if (tmp.Ptr())  __log2file(tmp.Ptr(), tmp.Length());//真正保存在__log2file中

}

//异步模式冲洗缓存
void appender_flush() {
    sg_cond_buffer_async.notifyAll();
}

log_formater 格式化日志,涉及到一些格式的处理。将_logbody format到_log中,例如日志如下,包括:日志级别、时间、pid、tid(如果是主线程,则有*指示)、TAG、filename、funcname、行号。后面则是具体日志内容
[D][2017-04-01 +8.0 16:23:11.888][21639, 1][test][filename, funcname, 121][hello wpr

void log_formater(const XLoggerInfo* _info, const char* _logbody, PtrBuffer& _log) {
    static const char* levelStrings[] = {
        "V",
        "D",  // debug
        "I",  // info
        "W",  // warn
        "E",  // error
        "F"  // fatal
    };

    assert((unsigned int)_log.Pos() == _log.Length());

    static int error_count = 0;
    static int error_size = 0;

    if (_log.MaxLength() <= _log.Length() + 5 * 1024) {  // allowd len(_log) <= 11K(16K - 5K)
        ++error_count;
        error_size = (int)strnlen(_logbody, 1024 * 1024);

        if (_log.MaxLength() >= _log.Length() + 128) {
            int ret = snprintf((char*)_log.PosPtr(), 1024, "[F]log_size <= 5*1024, err(%d, %d)\n", error_count, error_size);  // **CPPLINT SKIP**
            _log.Length(_log.Pos() + ret, _log.Length() + ret);
            _log.Write("");

            error_count = 0;
            error_size = 0;
        }

        assert(false);
        return;
    }

    if (NULL != _info) {//1.日志头部
        const char* filename = ExtractFileName(_info->filename);
        char strFuncName [128] = {0};
        ExtractFunctionName(_info->func_name, strFuncName, sizeof(strFuncName));

        char temp_time[64] = {0};

        if (0 != _info->timeval.tv_sec) {
            time_t sec = _info->timeval.tv_sec;
            tm tm = *localtime((const time_t*)&sec);
#ifdef ANDROID
            snprintf(temp_time, sizeof(temp_time), "%d-%02d-%02d %+.1f %02d:%02d:%02d.%.3ld", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
                     tm.tm_gmtoff / 3600.0, tm.tm_hour, tm.tm_min, tm.tm_sec, _info->timeval.tv_usec / 1000);
#elif _WIN32
            snprintf(temp_time, sizeof(temp_time), "%d-%02d-%02d %+.1f %02d:%02d:%02d.%.3d", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
                     (-_timezone) / 3600.0, tm.tm_hour, tm.tm_min, tm.tm_sec, _info->timeval.tv_usec / 1000);
#else
            snprintf(temp_time, sizeof(temp_time), "%d-%02d-%02d %+.1f %02d:%02d:%02d.%.3d", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
                     tm.tm_gmtoff / 3600.0, tm.tm_hour, tm.tm_min, tm.tm_sec, _info->timeval.tv_usec / 1000);
#endif
        }

        // _log.AllocWrite(30*1024, false);
        int ret = snprintf((char*)_log.PosPtr(), 1024, "[%s][%s][%" PRIdMAX ", %" PRIdMAX "%s][%s][%s, %s, %d][",  // **CPPLINT SKIP**
                           _logbody ? levelStrings[_info->level] : levelStrings[kLevelFatal], temp_time,
                           _info->pid, _info->tid, _info->tid == _info->maintid ? "*" : "", _info->tag ? _info->tag : "",
                           filename, strFuncName, _info->line);

        assert(0 <= ret);
        _log.Length(_log.Pos() + ret, _log.Length() + ret);
        //      memcpy((char*)_log.PosPtr() + 1, "\0", 1);

        assert((unsigned int)_log.Pos() == _log.Length());
    }

    if (NULL != _logbody) {//2.日志具体内容
        // in android 64bit, in strnlen memchr,  const unsigned char*  end = p + n;  > 4G!!!!! in stack array

        size_t bodylen =  _log.MaxLength() - _log.Length() > 130 ? _log.MaxLength() - _log.Length() - 130 : 0;
        bodylen = bodylen > 0xFFFFU ? 0xFFFFU : bodylen;
        bodylen = strnlen(_logbody, bodylen);
        bodylen = bodylen > 0xFFFFU ? 0xFFFFU : bodylen;
        _log.Write(_logbody, bodylen);
    } else {
        _log.Write("error!! NULL==_logbody");
    }

    char nextline = '\n';

    if (*((char*)_log.PosPtr() - 1) != nextline) _log.Write(&nextline, 1);//最后写个换行符
}

前面讲到了现在没有加密过程,只是单纯将buf拷贝下。而且,是有一定的格式的,如下,xlog也是按照这样的格式拼装数据
|magic start(char)|seq(uint16_t)|begin hour(char)|end hour(char)|length(uint32_t)|crypt key(uint32_t)|

void LogCrypt::CryptSyncLog(const char* const _log_data, size_t _input_len, char* _output, size_t& _output_len) {
    uint16_t seq = __GetSeq(false);
    uint32_t len = std::min(_input_len, _output_len - GetHeaderLen() - GetTailerLen());

    memcpy(_output + GetHeaderLen(), _log_data, len);   //copy log内容
    _output[GetHeaderLen() + len] = kMagicEnd;          //填充tail
    _output[0] = kMagicSyncStart;                      //填充头

    memcpy(_output + 1, &seq, sizeof(seq));           //seq

    struct timeval tv;
    gettimeofday(&tv, 0);
    time_t sec = tv.tv_sec;
    tm tm_tmp = *localtime((const time_t*)&sec);

    char hour = (char)tm_tmp.tm_hour;

    memcpy(_output+3, &hour, sizeof(hour));          //begin hour
    memcpy(_output+4, &hour, sizeof(hour));          //end hour
    memcpy(_output+5, &len, sizeof(len));            //length

    _output_len = GetHeaderLen() + GetTailerLen() + len;    
}

加密的格式,head和tail

/*
 * |magic start(char)|seq(uint16_t)|begin hour(char)|end hour(char)|length(uint32_t)|crypt key(uint32_t)|
 */

uint32_t LogCrypt::GetHeaderLen() {
    return sizeof(char) * 3 + sizeof(uint16_t) + sizeof(uint32_t) * 2;
}

uint32_t LogCrypt::GetTailerLen() {
    return sizeof(kMagicEnd);
}
static const char kMagicEnd  = '\0';

在reset里面初始化buf

bool LogBuffer::__Reset() {

    __Clear();

    if (is_compress_) {
        cstream_.zalloc = Z_NULL;
        cstream_.zfree = Z_NULL;
        cstream_.opaque = Z_NULL;

        if (Z_OK != deflateInit2(&cstream_, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY)) {
            return false;
        }

    }

    s_log_crypt->SetHeaderInfo((char*)buff_.Ptr(), is_compress_); //在每次reset时,初始化head
    buff_.Length(s_log_crypt->GetHeaderLen(), s_log_crypt->GetHeaderLen());

    return true;
}

读完的一些感受:
1.存在有很多特殊场景的逻辑,一不小心也许没有仔细读过源码的使用者也许会踩到坑里,比如前面appender_open_with_cache对于过期日志的处理,应该给出个默认的处理,也提供一个操作接口给用户;
2.有一些static global的变量,这种全局变量的写法会打破函数的复用性,使得逻辑容易冗余;

总的来说还是很棒的,稍微看懂后,改一改就成了一个很强大的日志组件,实乃搬砖之利器;
参考资料
http://www.tuicool.com/articles/NJbUbiU

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值