vlambda博客
学习文章列表

CBrother原生支持日志系统

之前研究了其他语言的日志系统,基本上都不是原生支持的,python原生支持有个logging,也只支持脚本层面写日志,用C语言去扩展第三方库也是没有统一的日志接口,如果扩展库要反馈错误给用户需要用到异常机制。java也是如此,所以java用C语言扩展第三方库很少,一般都直接用Java代码重新实现第三库。所以我一直陷入了一个困惑,难道一门语言原生支持日志系统属于多管闲事?经过一个漫长的思维斗争,最终想清楚了这其中的细微区别,于是决定:CBrother原生支持日志系统。

其他语言的错误反馈局限性

在讨论这个问题之前首先我们要限定一个范围,所有的编译型语言不在讨论范围内,因为编译型语言其最终会生产机器码,其代码最终保持一种形态,故原生日志系统对其没有实质意义。
我们讨论的是如python、ruby、java、php这类跑虚拟机的语言,其代码分为两部分,一部分是语言的脚本代码,另一部分是其他语言的扩展(动态库 *.dll *.so等)。这些语言自身的脚本代码可以实现一套日志库来解决日志的统一性,但是扩展库都没有这样的机制,扩展库内部反馈错误的途径无非是抛出异常、状态性错误码, 下来看一下这两种错误反馈的局限性。
异常机制,如果扩展库内部没有开启线程,用户调用接口事情干完后直接反馈结果,那么异常机制能很好的告知用户错误的各种原因。但是如果扩展库内开启了100个辅助线程,而辅助线程里又做了大量的工作,其工作跟用户的线程是并行的,这个时候辅助线程内部的异常就无法通过异常机制告知用户,这种情况下就需要用到状态错误码机制。


状态错误码,可以在辅助线程出现问题时记录一个全局状态,用户通过获取状态码来知道当前系统产生的问题,但是在一种场景下,比如说100个辅助线程中20个报出告警,其余80个正常,但是系统不应该停止,经常会造成最后一次错误码覆盖了前一次的错误码,使得某些错误被隐藏。

CBrother原生支持日志系统


为了避免这两种错误的局限性,我研究了一些其他语言的部分扩展库,一但扩展库系统复杂度较高,一般都需要封装各种各样的io抽象类提供给用户,然后用用户在自己的线程里调用一个方法(大部分情况下叫做run),虽然解决了问题,但是用户在使用的时候会产生很多困惑,自己new了这么多多对象,完全不知道这些对象对自己有何意义,总之就是例子这么写我就复制过来这么用,一套组合拳搞定就行,这种封装导致最终的接口晦涩难懂,用户如果不知道其内部原理根本无法理解其接口。甚至某些语言因为没有多线程能力,其扩展库里直接隐藏掉了一部分错误,这导致系统运行不出错还好,但凡出错用户根本摸不到头脑,连定位问题的方法都没有。
我发现很多复杂度较高的扩展库为了解决以上问题,也提供了自己独有的日志系统,这一点看来,到了一定的系统复杂度,日志是错误反馈的一个有效且最直接途径,但是当用到多个这样的库时候,比如说同时用了http服务模块和tcp服务模块,两个模块因为是不同人写的,日志打印格式输出路径均不统一,对于开发者来说,也不是一个很好的体验。

CBrother原生支持日志系统


CBrother原生日志的优势

首先,CBrother在做扩展库时,我们不希望接口晦涩难懂,我们更希望通过简单的接口完成非常复杂的操作,用户只需要简单的学习错误排除方法就可以。


CBrother的日志系统就是为了实现如上所说的错误反馈机制,其借鉴了log4j的接口原型,对脚本和扩展库都保留了统一的日志接口,无论扩展库书写的日志还是脚本书写的日志,最终都统一通过开发者定制的规则,按照开发者设置的格式输出到开发者设置的路径,并且可以拆分文件个数。

如此,后续扩展库只需要使用CBrother提供的接口输出详细日志,并在扩展库文档中给出日志排错说明书即可。扩展库开发中不再需要为了错误反馈而做大量设计,对于接口的保留可以怎么简单好用怎么来,不但降低扩展库使用难度,同时也降低了扩展库开发难度。

CBrother升级到v2.4.5

  • 原生支持日志系统,详细用法文档中查看《日志模块》章节

  • 增加了@logger标签符来声明日志配置函数

  • 类内this与super增加特殊语义,可用于控制多层继承时的父子类同名函数调用

日志的使用

@logger  //声明loggerConfig为日志配置函数,其运行时机早于一切模块加载function loggerConfig(){ var consoleLogger = new logger::LoggerOutputConsole(); var fileLogger = new logger::LoggerOutputFile(GetRoot(),"http-80");   logger::addLoggerOutput("cbrother.httpserver",consoleLogger,"[%d] %m"); //给http日志添加控制台输出  logger::addLoggerOutput("cbrother.httpserver",fileLogger,"[%d] %m"); //给http日志添加日志输出  logger::addLoggerOutput("test",consoleLogger); //给test日志添加一个控制台输出}
function main(parm){  var httpServer = new HttpServer();  //模块默认日志名为cbrother.httpserver httpServer.setRoot(GetRoot()); httpServer.startServer(80);   var logger = logger::getLogger("test");  logger.debug("test logger");    GetInput();}

说在最后

其实做这一点决定我也挣扎了很久,这个原生支持复杂的日志系统也没有什么其他语言可以借鉴的地方,也许过几年发现当初支持日志系统的想法就是彻头彻尾的错误,会给CBrother带来什么样的影响,确实还是无法估量。但是我还是相信自己上述的分析,应该是利大于弊。



↓↓↓点阅读原文查看CBrother官网