IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    dlog:高性能线程安全的C++日志库

    armsword发表于 2016-09-10 15:52:05
    love 0

    打算闲暇时光写点靠谱的东西比如名字服务、KV存储等基础服务类软件,所以周末就先从封装一些基础库开始。上周末写了个C++日志库,支持多线程,性能也还不错,今天就做了下测试和修改了下bug。这里说下该库的使用方法,以及实现思路以及一些性能调优方法。欢迎交流和指教。

    源码地址

    https://github.com/armsword/dlog

    使用方法

    将etc文件下的dlog.json扔到可执行文件的当前目录(当然只要能让可执行文件找到dlog.json即可),每个函数包含logger文件夹下的Log.h头文件,在主函数里调用DLOG_INIT初始化一次,之后在每个需要打印log的文件里调用DLOG_LOG即可。
    如实例所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <dlog/logger/Log.h>
    #include <unistd.h>
    using namespace dlog::logger;
    int main() {
    DLOG_INIT();
    DLOG_LOG(WARN, "test the log level using log lib!");
    DLOG_LOG(DEBUG, "Hello World, %d",2016);
    DLOG_LOG(INFO, "test Log C/C++ lib");
    DLOG_LOG(ERROR, "Hello everyone, this is my blog: http://armsword.com/; Welcome to visit it,Thank you!");
    return 0;
    }

    该日志库功能

    • 包含四种日志级别,分别为WARN、DEBUG、INFO、ERROR,日志级别大小依次递增
    • 可配置输出日志路径
    • 可配置输出日志前缀
    • 可定义输出的日志级别,默认DEBUG
    • 可定义日志文件切分大小
    • 支持多线程程序
    • 可定义日志往磁盘刷新的方式
    • 支持每天切换新的日志文件
    • 支持log文件被删除时,从新建立日志文件

    dlog.json配置

    1
    2
    3
    4
    5
    6
    7
    {
    "log_path": "./log", // 日志路径
    "log_prefix": "dlog", // 日志前缀
    "log_level": "DEBUG", // 输出日志级别,DEBUG表示大于等于DEBUG级别的日志都打印
    "max_file_size": 200, // 日志切分大小,单位m
    "async_flush": true // 日志往磁盘刷新方式,true表示异步,false表示同步,建议选择true
    }

    性能

    在测试机上测试了下机器的硬盘真实io写速度

    1
    2
    3
    4
    5
    6
    time dd if=/dev/zero of=test.dbf bs=8k count=300000 oflag=direct
    记录了300000+0 的读入
    记录了300000+0 的写出
    2457600000字节(2.5 GB)已复制,38.0385 秒,64.6 MB/秒
    注:oflag=direct 表示使用DirectIO方式,不使用文件系统的buffer等

    而我用dlog写入3.5G(1000W条数据),用时大约62s,计算下来,写速度大约57M/s,看起来性能还算不错,当然由于时间问题,我测试还不够充分,以后有机会继续优化下再测试看下。

    一些性能调优技巧

    • 为了避免锁竞争,使用了一种更为高效的线程局部存储方法,就是使用关键字thread来定义变量,thread是GCC内置的线程局部存储设施(Thread-Local Storage),凡是带有__thread的变量,每个线程都拥有该变量的一份拷贝,且互不干扰。
    • 使用likely,unlikely来提高CPU分支预测正确率来提高性能。我们定义likely、unlikely如下:
      1
      2
      #define likely(x) __builtin_expect((x), 1)
      #define unlikely(x) __builtin_expect((x), 0)

    其中__builtin_expect是gcc提供的函数。顾名思义,likely表示这件事很大概率会发生 :)

    • 使用loop线程来判断文件是否需要切分(打开新的fd),并且open、close这种费时操作,只在loop线程里完成,不阻塞log输出线程(一些小技巧保证线程安全)。

    本日志库主要是为了满足个人需求,当然即使公司业务,没特殊需求的话也足够用了,所以并没有写的像log4j或者log4cxx那么复杂,但即便如此该日志库还有一些提高空间,等我有空再继续优化下,欢迎交流和指教,谢谢。

    致谢:

    因为很久没用Cmake了(之前在阿里用ascons),基本上都忘光了。并且阿里的一些经验使我比较在意代码目录的组织方式,搜索后发现一篇文章讲Cmake非常不错,作者很用心,非常感谢。

    Cmake入门实战



沪ICP备19023445号-2号
友情链接