2019独角兽企业重金招聘Python工程师标准>>>

前几天看到一个网友的评论:“这种一般自己实现个用用就行了 没必要整第三方库”。
的确,很多个人或公司都自己实现了简单写日志函数在产品中使用即可,一般不喜欢第三方库,一来认为第三方库代码多,势必影响性能,二来带来的不可预见的代码黑盒子,影响软件整体可靠。
这是一个广泛的论点,我并不否认它的存在合理性,但还是想对传统的简单写日志函数和iLOG3函数库做个比较,分两部分:性能和代码复杂度,看完后你会发现你会对日志函数库iLOG3感兴趣的。

一、性能

一般来讲,简单写日志函数肯定要比日志函数库要快,但是我分别做了压力测试,结果发现iLOG3竟然比简单写日志函数要快近一倍!下面是测试案例

随手写的最简单的写日志函数代码(源码包test目录下的bench_tiny.c)

static int _WriteLogBase( char *c_filename , long c_fileline , int log_level , char *format , va_list valist )
{int        fd ;char        buf[ 1024 + 1 ] ;long        len ;time_t        tt ;struct tm    stime ;pid_t        pid ;unsigned long    tid ;        int        n ;/* 打开日志文件 */fd = open( "bench_tiny.log" , O_CREAT|O_APPEND|O_WRONLY , S_IRWXU|S_IRWXG|S_IRWXO ) ;if( fd == -1 )return -1;/* 组织日志内容 */time( & tt );localtime_r( & tt , & stime );pid = getpid() ;tid = (unsigned long)pthread_self() ;memset( buf , 0x00 , sizeof(buf) );len = snprintf( buf , sizeof(buf) , "%04d-%02d-%02d %02d:%02d:%02d | INFO  | %d:%lu:%s:%ld | %s\n" , stime.tm_year+1900 , stime.tm_mon+1 , stime.tm_mday , stime.tm_hour , stime.tm_min , stime.tm_sec , pid , tid , c_filename , c_fileline , "log" ) ;/* 输出日志 */n = write( fd , buf , len ) ;if( n == -1 )return -1;/* 关闭日志文件 */ /* 关键场合必须及时关闭文件,否则日志有丢失可能 */close( fd );return 0;
}#define LOG_LEVEL_DEBUG        0
#define LOG_LEVEL_INFO        1
#define LOG_LEVEL_WARN        2
#define LOG_LEVEL_ERROR        3
#define LOG_LEVEL_FATAL        4/* 以不同日志等级写行日志 */
int DebugLog( char *c_filename , long c_fileline , char *format , ... )
{...
}int InfoLog( char *c_filename , long c_fileline , char *format , ... )
{va_list        valist ;/* 暂不写日志等级过滤逻辑 *//* ... *//* 调用通用底层函数输出日志 */va_start( valist , format );_WriteLogBase( c_filename , c_fileline , LOG_LEVEL_INFO , format , valist );va_end( valist );return 0;
}int ErrorLog( char *c_filename , long c_fileline , char *format , ... )
{...
}...

在我的环境里跑出这样的成绩

$ rm -f *.log* ; sleep 1 ; time ./bench_tiny 10 10 10000 ; head -1 bench_tiny.log ; wc *.log* ; rm -f *.log*real    0m21.863s
user    0m1.688s
sys     0m19.924s
2014-02-22 12:58:25 | INFO  | 2457:3086568336:bench_tiny.c:144 | log
1000000  8000000 69000000 bench_tiny.log

嗯,10个进程,每个进程开10个线程,每个线程循环调用函数InfoLog()一万次,花费21秒左右,共写出100万行日志,约6900万字节

现在轮到iLOG3出场,用源代码包中test目录下的压测示例test_press_mpt.c。
公平起见,把按文件大小转档关掉。

// SetLogRotateMode( press , LOG_ROTATEMODE_SIZE );

开跑

$ rm -f *.log* ; sleep 1 ; time ./test_press_mpt 10 10 10000 ; head -1 test_press_mpt.log ; wc *.log* ; rm -f *.logreal    0m13.745s
user    0m1.136s
sys     0m12.444s
2014-02-22 13:02:33 | INFO  | 2893:3086392208:test_press_mpt.c:120 | log
1000000  8000000 73000000 test_press_mpt.log

同样开10个进程,每个进程开10个线程,每个线程循环调用函数iLOG3里的InfoLog()一万次,花费13秒左右,共写出100万行日志,约7300万字节,你没看错,iLOG3日志函数库的日志输出性能比随手写的简单写日志函数快了近一倍。
为什么会这样呢?等我再对比完代码复杂度后一起总结 ^_^

二、代码复杂度

我们从iLOG3里的InfoLog作为入口一层层剥离下去,看一下写一次日志需要跑哪些路径,和随手写的简单写日志函数代码有哪些区别。

/* 写普通信息日志 */
int InfoLog( LOG *g , char *c_filename , long c_fileline , char *format , ... )
{WRITELOGBASE( g , LOG_LEVEL_INFO )return 0;
}/* 代码宏 */
#define WRITELOGBASE(_g_,_log_level_) \va_list        valist; \int        nret ; \if( (_g_) == NULL ) \return LOG_RETURN_ERROR_PARAMETER; \if( (_g_)->output == LOG_OUTPUT_FILE && (_g_)->log_pathfilename[0] == '\0' ) \return 0; \if( TEST_LOGLEVEL_NOTENOUGH( _log_level_ , (_g_) ) ) \return 0; \va_start( valist , format ); \nret = WriteLogBase( (_g_) , c_filename , c_fileline , _log_level_ , format , valist ) ; \va_end( valist ); \if( nret < 0 ) \return nret;/* 写日志基函数 */
int WriteLogBase( LOG *g , char *c_filename , long c_fileline , int log_level , char *format , va_list valist )
{long        writelen ;int        nret ;if( format == NULL )return 0;/* 初始化行日志缓冲区 */g->logbuf.buf_remain_len = g->logbuf.buf_size - 1 - 1 ;g->logbuf.bufptr = g->logbuf.bufbase ;/* 填充行日志缓冲区 */if( g->pfuncLogStyle ){nret = g->pfuncLogStyle( g , & (g->logbuf) , c_filename , c_fileline , log_level , format , valist ) ;if( nret )return nret;}/* 自定义过滤日志 */if( g->pfuncFilterLog ){nret = g->pfuncFilterLog( g , & (g->open_handle) , log_level , g->logbuf.bufbase , g->logbuf.buf_size-1-1 - g->logbuf.buf_remain_len ) ;if( nret )return nret;}/* 打开文件 */if( g->open_flag == 0 ){if( TEST_ATTRIBUTE( g->log_options , LOG_OPTION_CHANGE_TEST ) || TEST_ATTRIBUTE( g->log_options , LOG_OPTION_OPEN_ONCE ) ){if( g->pfuncOpenLogFirst ){nret = g->pfuncOpenLogFirst( g , g->log_pathfilename , & (g->open_handle) ) ;if( nret )return nret;g->open_flag = 1 ;}}else if( TEST_ATTRIBUTE( g->log_options , LOG_OPTION_OPEN_AND_CLOSE ) ){/* 打开日志文件 */if( g->pfuncOpenLog ){nret = g->pfuncOpenLog( g , g->log_pathfilename , & (g->open_handle) ) ;if( nret )return nret;g->open_flag = 1 ;}}}/* 导出日志缓冲区 */if( g->pfuncWriteLog ){nret = g->pfuncWriteLog( g , & (g->open_handle) , log_level , g->logbuf.bufbase , g->logbuf.buf_size-1-1 - g->logbuf.buf_remain_len , & writelen ) ;if( nret )return nret;}/* 关闭日志 */if( g->open_flag == 1 ){if( g->output == LOG_OUTPUT_FILE || g->output == LOG_OUTPUT_CALLBACK ){if( TEST_ATTRIBUTE( g->log_options , LOG_OPTION_CHANGE_TEST ) ){if( g->pfuncChangeTest ){nret = g->pfuncChangeTest( g , & (g->test_handle) ) ;if( nret )return nret;}}else if( TEST_ATTRIBUTE( g->log_options , LOG_OPTION_OPEN_AND_CLOSE ) ){/* 关闭日志文件 */if( g->pfuncCloseLog ){nret = g->pfuncCloseLog( g , & (g->open_handle) ) ;if( nret )return nret;g->open_flag = 0 ;}}}}/* 如果输出到文件 */if( g->output == LOG_OUTPUT_FILE ){/* 日志转档侦测 */if( g->rotate_mode == LOG_ROTATEMODE_NONE ){}else if( g->rotate_mode == LOG_ROTATEMODE_SIZE && g->log_rotate_size > 0 ){g->skip_count--;if( g->skip_count < 1 ){RotateLogFileSize( g , writelen );}}else if( g->rotate_mode == LOG_ROTATEMODE_PER_DAY ){RotateLogFilePerDate( g );}else if( g->rotate_mode == LOG_ROTATEMODE_PER_HOUR ){RotateLogFilePerHour( g );}}/* 清空一级缓存 */g->cache1_tv.tv_sec = 0 ;g->cache1_stime.tm_mday = 0 ;return 0;
}

以上可以看出两者路径基本一致,iLOG3的WriteLogBase也是格式化行日志缓冲区、打开日志文件、输出日志、关闭日志文件、日志转档处理,无非都是抽象成日志控制框架,即通过回调函数来挂接实现而没有直接调用具体功能函数,这样设计的好处是用户可以编写自己的打开输出关闭日志文件等函数来替代iLOG3默认挂接的来实现更灵活的日志控制。

好,现在我来解释一下为什么第三方的日志函数库iLOG3比自己随手写的简单写日志函数速度快近一倍的原因。

首先,日志函数库iLOG3的原型就是简单写日志函数扩充而来,所以它们的处理路径和基本逻辑差不多,但是iLOG3把作为函数库的很多耗时的配置解析等工作尽量都放在写日志前就预先处理好,真正写日志时跑的函数调用关系、运行逻辑路径等和简单写日志函数跑过的基本一致,可以理解成iLOG3写日志时调用的其实就是简单写日志函数,到此它们的性能就差不多了,然后,iLOG3拥有日志句柄这个数据结构,为缓存层等设计提供了方便,iLOG3内部做了大量严谨、精巧的性能优化,其中就包括时间缓存(保证缓存不会串日志)、字符串转换缓存、文件大小转档步进算法等,恰当受控的优化的效果是显而易见的,最终性能就比随手写的简单写日志函数要快了。

最后,再次展现一下我开发iLOG3的设计准则:
·作为软件的重要基础模块,日志函数库应尽量实现的轻便、简易和稳定,在保证高可靠性的前提下,高性能低影响是首要设计目标。提供多层API以及可选的外部配置文件方式使用。
·基本功能必不可少,如日志等级、行日志和十六进制块日志等,对性能和易用性影响较大的高级功能可不加尽量不加。(要不要实现日志转档是一个很纠结的问题,我一直坚持认为用运维shell来实现会更好,但迫于同类日志库的压力,我还是实现了)
·日志模块至少应分为两部分:核心和外部配置,中间用API胶合起来,便于扩展和灵活替换
·线程安全

是不是越看越心动了?那就赶紧下载来玩玩吧
首页传送门 : http://git.oschina.net/calvinwilliams/iLOG3
源代码包doc目录中包含了用户指南和参考手册,里面有更详尽的说明

声明:文本的写作目的并不是想说服大家改用第三方日志函数库来替代自己写的简单写日志函数,而是想带领大家深入探讨诸如写日志这类在某些场景中软件必备模块的一些思考,至于用不用iLOG3则需要你更多更全面甚至是非技术层面的考量。

转载于:https://my.oschina.net/u/988092/blog/202240

开源纯C日志函数库iLOG3快速入门(五、与随手写的简单写日志函数的比较)相关推荐

  1. 开源纯C日志函数库iLOG3快速入门(八、如果你喜欢简单日志函数甚于日志函数库)...

    2019独角兽企业重金招聘Python工程师标准>>> 开源纯C日志函数库iLOG3快速入门(八.如果你喜欢简单日志函数甚于日志函数库) 很多网友来信坚持表达了在项目中应使用简单日志 ...

  2. ibm ilog mysql_开源纯C日志函数库iLOG3快速入门(八、如果你喜欢简单日志函数甚于日志函数库)...

    开源纯C日志函数库iLOG3快速入门(八.如果你喜欢简单日志函数甚于日志函数库) 很多网友来信坚持表达了在项目中应使用简单日志函数,而不喜欢日志函数库,我与之反复争论无果,不过话说回来,我也喜欢短小轻 ...

  3. python符号格式化设置区间_Python 数值区间处理_对interval 库的快速入门详解

    使用 Python 进行数据处理的时候,常常会遇到判断一个数是否在一个区间内的操作.我们可以使用 if else 进行判断,但是,既然使用了 Python,那我们当然是想找一下有没有现成的轮子可以用. ...

  4. 【Python随记】:curses 库的快速入门

    文章目录 curses 简介 Python curses 模块 curses 库安装方法 Windows 下安装 Linux 下安装 curses 简介 curses 是一个在Linux/Unix下广 ...

  5. STL快速入门学习教程之map的简单使用

    STL快速入门学习教程之map的简单使用 map是STL中的一个关联容器,它以一对一的数据进行整理(第一个数值称为关键字,且这个关键字只能在map中出现一次,第二个数值称为前关键字的值),正是由于这种 ...

  6. 【完结】12大深度学习开源框架(caffe,tf,pytorch,mxnet等)快速入门项目

    这是一篇总结文,给大家来捋清楚12大深度学习开源框架的快速入门,这是有三AI的GitHub项目,欢迎大家star/fork. https://github.com/longpeng2008/yousa ...

  7. Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 4. 函数

    什么样的程序员才是优秀的程序员?咪博士认为"慵懒"的程序员才是真正优秀的程序员.听起来不合逻辑?真正优秀的程序员知道如何高效地工作,而不是用不止境的加班来完成工作任务.函数便是程序 ...

  8. java 写日志太快_Log4j2 快速入门 —— 定期/定大小生成日志文件(三)

    Log4j2 对于生成日志文件时,可以通过对中的进行设置,来完整日志文件各种生成方案 定期生成日志文件 log4j2.xml user/logs filePattern="${LOG_HOM ...

  9. Python快速入门到实战(三)逻辑控制语句,函数与类

    目录 一.逻辑控制语句 条件控制语句 if-else for 循环语句 while 循环 break 语句 continue 语句 Pass 语句 二.函数 函数的定义与调用 参数传递 函数的参数类型 ...

最新文章

  1. 疑难杂症——bash: /dev/null: Permission denied
  2. mysql查询显示柱形图_Grafana配置mysql展示自定义分组柱状图(Mac)
  3. 美甲帮:数加平台打造大数据架构
  4. minio分布式搭建_分布式存储Minio集群环境搭建
  5. TKDragView_TKCalendarView:页面curl的动画日历
  6. matlab求方程实根,简单迭代法求方程根的MATLAB程序
  7. MD5加密,Base64加密/解密,AES加密/解密
  8. Scala深入浅出实战经典---001-Scala开发环境搭建和HelloWorld解析
  9. 卡巴斯基病毒库备份小程序
  10. WoShop跨境电商无货源供应商全开源无加密商城源码
  11. 如何关闭访达窗口_UG NX软件基础操作,如何自定义软件快捷键
  12. java blazeds_Flex+Java+Blazeds
  13. 超像素分割SLIC与SLIC0(SLIC Zero)算法的区别
  14. 然而,随着下属人数的增多,下属各自 开始形成自己的权力主体。
  15. GPON(计算机网络相关)
  16. 靶机渗透----bulldog2
  17. 40岁销售被裁员后抑郁了,学Python是他最后的希望
  18. 关于耳机与电脑连接断断续续问题的解决办法
  19. Web 安全工具篇:Burp Suite 使用指南
  20. EfficientNetV2 Smaller Models and Faster Training

热门文章

  1. 使用Hive SQL查询Iceberg表的正确姿势
  2. 【Linux】文件解压缩、解打包命令解析(zip、unzip、tar)
  3. 机器学习的VC维度(机器学习基石)
  4. 【无标题】洛必塔法则的使用条件
  5. 开发方向校招准备的正确姿势,机会留给有准备的人
  6. 如何利用NDI Analysis工具分析NDI流类型
  7. C++ STL模板库用法查询及一些常见面试题(自用)
  8. 大国博弈扑朔迷离 黄金理财打造纷争下的盈利棋局
  9. 嵌入式面试:vivo 2020春 校
  10. coarse-to-fine(6) - DML