当前位置: 首页 > 科技观察

普通程序员如何理解日志系统

时间:2023-03-18 15:01:21 科技观察

我们在做系统开发的时候,日志系统是一个绕不开的话题。作为日志系统的最终用户,我们会接触到不同的日志系统,比如log4j、logback、slf4j等,也会接触到日志系统的各种概念,比如Formatter、Appender、Priority。这些日志系统有什么区别,应该如何理解这些概念?今天我们就来说说普通程序员,也就是日志系统的最终用户,是如何理解日志系统的。Logging系统的雏形,让我们回到了计算机世界的远古时代,或者刚刚接触计算机世界的那个时期。当时我们调试程序有两种方式:1)单步调试,一步步跟踪,查看代码中的变量。价值。2)是printf的一个大方法——在特定的地方打印日志,通过日志的输出帮助快速定位。单步调试方法费时费力,但可以准确定位问题。printf方法简单粗暴,需要尝试一下。在大多数情况下,可以很快找到问题。单步调试和printf方法一起使用,相得益彰。但单步调试止步于gdb等调试工具,printf方式最终发展出一系列的日志系统。原因是单步调试只能在程序员调试中使用,而printf方法在调试和生产线上都可以使用,输出的日志被各行各业的人使用和解读。什么时候打印日志是个问题——Levelprintf大法很粗糙。在调试过程中,日志可能非常细粒度。比如每条数据的第三个字段都打印出来了,但是在实际运行中必须删除这些细粒度的日志。我们下次调试的时候,需要知道每条数据的第三个字段是什么。为此,我们希望日志打印是智能的:调试时或者线上出现问题时,打印出各种细粒度的日志,正常运行时,输出一些最简单的信息就可以了。为了解决这个问题,日志系统引入了日志级别(Level)的解决方案。引入了日志级别的概念之后,我们在编程的时候打印一条日志的时候,就需要指明这条日志的级别。由于日志级别是最重要的参数,目前的日志系统直接通过不同的函数来指定级别,包括logger.TRACE、logger.DEGUB、logger.INFO、logger.WARN、logger.ERROR、logger.FATAL。级别比较为TRACElogging.INFOandnothasattr(record,'is_custom'):record.msg="[%s,%s,%s]%s"%(record.filename,record.lineno,record.funcName,record.msg)record.is_custom=Truereturnsuper(AltCustomFormatter,self).format(record)引入了Level、Formater和Appender的概念后,整个log系统的架子搭建完成,如下图所示。作为一个普通的程序员,可以安??心的使用这个日志系统。高效打印日志是另外一个问题——高效,但是作为一个有追求的普通程序,我们想知道的是日志系统在大型系统的极端环境下能不能撑得住。答案是上面设计的日志系统是撑不住的。因为大型系统的极端环境,实时性要求高,不能容忍写文件或写网络的延迟。怎么做?请对付IO延时利器——Buffer。人们基于Buffer,考虑到Buffer带来的线程同步问题,设计了如下方案。在这种方案中,每个线程生成一个Buffer,然后写线程轮询从Buffer中读取信息并写入目的地。在该方案中,写入日志不会造成服务延迟。除了架构设计,还有一些提升性能的小技巧。例如,我们在log4j官方API中发现了丑陋的INFO函数。在Java语言不支持变长参数的情况下,log4j强加了一个支持变长的INFO函数,所以只能靠写不同的函数重载,最后只支持9个参数。但这些丑陋的信息是有道理的。这些丑陋的INFO就是为了能够实现下表中的变长参数。这种变长参数方式和字符串拼接方式有什么区别呢?当前级别为ERROR时,不需要输出INFO级别的信息,字符串拼接方式仍然需要拼接字符串,不定长参数方式不需要拼接字符串。总结以上就是对日志系统基本概念的介绍。从古代的printf方式到现代的日志系统,为了让我们普通程序员能够直观的了解系统运行状态,大师们介绍了Level、Appender、Formatter等日志系统的核心概念,以及开发了log4j、logback和tinylog等著名的日志系统。