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

Java日志性能那些事

时间:2023-03-14 12:21:30 科技观察

在任何系统中,日志都是非常重要的一部分,它是反映系统运行情况的重要依据,也是排查问题时必不可少的线索。大多数人都认识到日志的重要性,但是有多少人认真思考过如何记录日志,日志对性能的影响有多大?今天我们来谈谈Java日志记录性能。DEBUG级别的日志在生产环境中不会输出到文件中,也可能会带来很大的开销。抛开判断和方法调用的开销不谈。在Log4J2.x的性能文档中,有这样一组对比:logger.debug("Entrynumber:"+i+"is"+String.valueOf(entry[i]));logger.debug("条目号:{}为{}",i,entry[i]);以上两条语句对日志输出的影响是一样的,但是在关闭DEBUG日志的情况下,它们的开销是不一样的,主要影响在字符串转换和字符串拼接上,不管有效与否,前者会转换将变量转换为字符串并进行拼接,而后者只会在需要时才进行这些操作。Log4J官方的测试结论是,两者的性能可以相差两个数量级。试想一下,如果在一个对象的toString方法中使用ToStringBuilder来反映输出几十个属性,这时候可以节省多少资源。所以一些还在使用Log4J1.x或者ApacheCommonsLogging(不支持{}模板写法)的公司会有相应的编码标准,需要在输出某个级别的日志(比如DEBUG和INFO)之前进行额外的判断:if(logger.isDebugEnabled){logger.debug("条目号:"+i+"是"+String.valueOf(entry[i]));}除了日志级别和日志消息之外,通常日志还会包含一些其他信息,比如日期、线程名称、类信息、MDC变量等。根据Takipi的测试,如果在日志中添加一个类,性能会急剧下降。与LogBack默认配置相比,吞吐量会下降60%左右。如果一定要打印类信息,可以考虑使用类名来命名Logger。在分布式系统中,一个请求可能会经过多个不同的子系统。这时候***生成一个UUID附加到请求中。各子系统打印日志时将UUID放在MDC中,方便后续查询。相关日志。《The Ultimate Guide: 5 Methods For Debugging Production Servers At Scale》关于如何在生产环境进行调试,文章中给出了很多建议。其中有几篇是关于日志的,这就是其中之一。另一个建议是记录所有未捕获的日志。其实抛出异常是有开销的,记录异常也会带来一定的开销。主要原因是Throwable类的fillInStackTrace方法默认是同步的:publicsynchronizednativeThrowablefillInStackTrace;general使用logger.error会产生一个异常堆栈。如果你对吞吐量有一定的要求,可以考虑在运行的时候重写这个方法,去掉synchronizednative,直接返回实例本身。说完了日志内容,我们再来看看Appender。在Java中,一提到IO操作,大家就会想到NIO。有了JDK7和AIO,至少大家都知道怎么读写和加Buffer了。日志也是如此。同步编写的appender在高并发、大流量的系统中有些力不从心。此时应该在10个并发线程下输出200个字符的日志,吞吐量可以是FileAppender的3.7倍。在不丢失日志的情况下,同样使用AsyncAppender,队列长度也会对性能产生一定的影响。如果使用Log4J2.x,除了AsyncAppender,还可以考虑性能更高的异步Logger。由于底层使用了Disruptor,没有锁开销,性能更加惊人。根据官方对Log4J2.x的测试,在相同的Log4J2.x:64个线程下,异步Logger比异步Appender快12倍,比同步Logger快68倍。同样是异步的,不同的库会有差异:在相同的硬件环境下,Log4J2.x都使用异步Logger,会比LogBack的AsyncAppender快12倍,比Log4J1.x的异步Appender快19倍.Logger性能强大,但也有不同的声音,认为这只是摆设,只能当玩具。这个问题留给读者自己去思考。Appender,那么可以考虑使用STDOUT。大多数生产系统都部署在集群中。对于分布在不同服务器上的日志,最好使用Logstash等工具进行收集。很多情况下,会在一台机器上部署多个实例,以充分利用服务器资源。这个时候不要贪图日志监控或者日志查询的方便,将多个实例的日志写到同一个日志文件中。虽然LogBack提供了prudent模式,可以让多个JVM将日志写入同一个文件,但是这种方式对性能也有影响,会降低10%左右的性能。如果你对同一个日志文件有很多的写入需求,可以考虑将日志拆分成不同的文件。一种方式是添加多个Appender,同时修改代码,针对不同情况使用不同的Logger;LogBack提供了SiftingAppender,可以直接根据MDC的内容拆分日志。Jetty的教程有一个根据主机拆分日志的例子。根据Takipi的测试,SiftingAppender的性能会随着分割文件数量的增加而增加。当拆分成4个文件时,在10并发时,SiftingAppenderFileAppender是SiftingAppenderFileAppender的三倍多。看了上面这么多资料,不知道大家有没有觉得你们的日志还有很大的提升空间。你还没有把系统优化到最佳水平,或者你有其他的日志优化方法。不妨分享给大家。