异常与日志系统的从思考到实践


引语:

从albianj的v1.0开始,关于异常与日志的处理子系统虽然可以使用,但是对我来说,一直不尽如人意.近期,随着albianj再一次的大更新,我们终于找到了一个简单又有效方案来达成我们心目中异常与日志该有的样子.从可以使用的V1.0到功能增强的V2.0,再到现在集合了理论依据,运用支撑与实践证明的V3.0,异常和日志子系统的演化过程对我们来说是一部挣扎于抗争的历史,也是一部集合了无数日日夜夜,无数人员废了无数脑细胞成就的奋斗史,更是一部教训深刻,改进认知的血泪史.


异常与日志的概念

异常,何为异常?根据教科书的概念,异常:.

日志,何为日志?好像我们的教科书从来没有对于日志做过概念性的解释,在不同的阶段,对于日志我们都要不同的感受.作为学生,日志仅仅只是一个概念;作为一个初出茅庐的程序员,日志总是带着朦朦胧胧的面纱,我们就知道日志需要记,并且最好是事无巨细,但对于它的庐山真面目我们好像很少见到,对于它的价值,我们更是无处而得.直到开始直面ONLINE系统,开始处理各种问题,开始碰见各种疑难杂症,我们才发现,日志的正确与精确是多么的重要,以至于重要到无论怎么强调都不过分的地步.因为只有它,在你每每大汗淋漓心跳加速的时候,每每抓耳挠腮冥思苦想不得其解的时候,每每放弃治疗准备killself的时候,是它的挺身而出救了你这个脆弱的程序DOG.

异常与日志放在一起的原因,为什么说到异常就要提到日志.


异常与日志的使用日常

异常太多,处理麻烦 为了一了百了的保险性代码,我一般不会管异常的类型.这个原因是:通常发出异常不仅仅是被认为是异常,而是一个运行时错误,流程通常因为excp而carsh,只有及其少数的异常(1%,可能还不到1%)会被作为流程分支的一部分来加以处理.

记录麻烦:stack frame层级太多,第二个,通常干扰原因信息太多,只是对于excp而言,真正重要的信息只是where(filename,line number,funcname),why(cause) 别的都不重要.

对于异常的处理,99%的可能是:log下来,然后rethrow excp,或者吃掉原始excp,重新new一个带有新info的excp,再或者,世界msgbox.show,然后exit thread.


日志与异常子系统设计与实现

理论支撑:

  1. 异常是什么类型其实并不重要
  2. excp的where很重要
  3. 异常的cause很重要,也就是why很重要?是不是可以一眼看出来excp的why?
  4. 异常的msg很重要,但是msg是需要一定的规则进行分级的.异常的msg通常会被有意或者无意的show到log或者直接送达client,但是其实msg是有可能会带有敏感信息的,比如tablename,fieldname,db pwd,useid等等,但是这部分msg不能送达client
  5. 对于java(此条仅对区分runtime异常与非runtime异常的语言),excp是runtime还是非runtime并不重要,反正只要是excp,总要去处理.

实践支撑:

  1. catch从来不会单type单type的catch然后处理,从来都是catch一个总的excp通常是excp,更甚至是throwable,然后处理

    此条cover 上面1和5

  2. catch后基本上不会去做分支,如果需要改变分支,通常使用返回值或者RefArg来显示的处理,而不是通过excp

  3. catch后,对于excp,通常只是简单地记入log,然后进入错误流程或者直接exit thread.

设计图(UML)

说明


结语

虽然这里讲的异常与日志子系统脱胎于我们的Albianj,但是其并不仅仅只是针对Albianj的异常与日志处理.更是面向大众通用化的,可集成,可重用的一个方案.从语言角度来说,它不仅仅可以被java使用与实现(Albianj使用java实现),该方案可以适用于任何语言,任何框架的系统.特别是对于某些Framework的开发者,该方案极其适用,不仅可以节省代码量,更可以简化系统的设计与实现,又可以给使用Framework的开发者一个简单明了的异常处理接口.

这样的方案应该归功于我们不仅仅只是想建立一个特殊目的,特殊作用化异常与日志系统,我们更是从一开始就设立了通用化的目标,并且一直在寻找达成这个目标的方法和方案.