Java日志性能那些事

在任何系统中,日志都是非常重要的组成部分,它是反映系统运行情况的重要依据,也是排查问题时的必要线索。绝大多数人都认可日志的重要性,但是又有多少人仔细想过该怎么打日志,日志对性能的影响究竟有多大呢?今天就让我们来聊聊Java日志性能那些事。

DEBUG级别的日志在生产环境中不会输出到文件中,也可能带来不小的开销。我们撇开判断和方法调用的开销,在Log4J 2.x的性能文档中
有这样一组对比:logger.debug("Entry number: " + i + " is " +
String.valueOf(entry[i])); logger.debug("Entry number: {} is {}", i,
entry[i]);

上面两条语句在日志输出上的效果是一样的,但是在关闭DEBUG日志时,它们的开销就不一样了,主要的影响在于字符串转换和字符串拼接上,无论是否生效,前者都会将变量转换为字符串并进行拼接,而后者则只会在需要时执行这些操作。Log4J官方的测试结论是两者在性能上能相差两个数量级。试想一下,如果某个对象的toString方法里用了ToStringBuilder来反射输出几十个属性时,这时能省下多少资源。

因此,某些仍在使用Log4J 1.x或Apache Commons Logging(它们不支持{}模板的写法)的公司都会有相应的编码规范,要求在一定级别的日志(比如DEBUG和INFO)输出前增加判断:

if (logger.isDebugEnabled) { logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); }

除了日志级别和日志消息,通常在日志中还会包含一些其他信息,比如日期、线程名、类信息、MDC变量等等,根据Takipi的测试,如果在日志中加入class,性能会急剧下降,比起LogBack的默认配置,吞吐量的降幅在6成左右。如果一定要打印类信息,可以考虑用类名来命名Logger。

在分布式系统中,一个请求可能会经过多个不同的子系统,这时最好生成一个UUID附在请求中,每个子系统在打印日志时都将该UUID放在MDC里,便于后续查询相关的日志。《The
Ultimate Guide: 5 Methods For Debugging Production Servers At
Scale》一文中就如何在生产环境中进行调试给出了不少建议,当中好几条是关于日志的,这就是其中之一。另一条建议是记录下所有未被捕获的日志,其实抛出异常有开销,记录异常同样会带来一定的开销,主要原因是Throwable类的fillInStackTrace方法默认是同步的:

public synchronized native Throwable
fillInStackTrace;一般使用logger.error都会打出异常的堆栈,如果对吞吐量有一定要求,在情况运行时可以考虑覆盖该方法,去掉synchronized
native,直接返回实例本身。聊完日志内容,再来看看Appender。在Java中,说起IO操作大家都会想起NIO,到了JDK
7还有了AIO,至少都知道读写加个Buffer,日志也是如此,同步写的Appender在高并发大流量的系统里多少有些力不从心,这时就该使用在10线程并发下,输出200字符的日志,的吞吐量最高能是FileAppender的3.7倍。在不丢失日志的情况下,同样使用AsyncAppender,队列长度对性能也会有一定影响。如果使用Log4J

2.x,那么除了有AsyncAppender,还可以考虑性能更高的异步Logger,由于底层用了Disruptor,没有锁的开销,性能更为惊人。根据Log4J
2.x的官方测试,同样使用Log4J 2.x:64线程下,异步Logger比异步Appender快12倍,比同步Logger快68倍。

同样是异步,不同的库之间也会有差异:

同等硬件环境下,Log4J 2.x全部使用异步Logger会比LogBack的AsyncAppender快12倍,比Log4J 1.x的异步Appender快19倍

Logger性能强悍,但也有不同的声音,觉得这只是个看上去很优雅,只能当成一个玩具。关于这个问题,还是留给读者自己来思考吧。Appender,那么可以考虑使用STDOUT大部分生产系统都是集群部署,对于分布在不同服务器上的日志,用Logstash之类的工具收集就好了。很多时候还会在单机上部署多实例以便充分利用服务器资源,这时千万不要贪图日志监控或者日志查询方便,将多个实例的日志写到同一个日志文件中,虽然LogBack提供了prudent模式,能够让多个JVM往同一个文件里写日志,但此种方式对性能同样也有影响,大约会使性能降低10%。如果对同一个日志文件有大量的写需求,可以考虑拆分日志到不同的文件,做法之一是添加多个Appender,同时修改代码,不同的情况使用不同Logger;LogBack提供了SiftingAppender,可以直接根据MDC的内容拆分日志,Jetty的教程中就有根据host来拆分日志的范例,而根据Takipi的测试,SiftingAppender的性能会随着拆分文件数的增长一同提升,当拆分为4个文件时,10并发下SiftingAppenderFileAppender的3倍多。看了上面这么多的数据,不知您是否觉得自己的日志有不少改进的余地,您还没有把系统优化到极致,亦或者您还有其他日志优化的方法,不妨分享给大家。


来源:51CTO

时间: 2024-11-03 00:54:13

Java日志性能那些事的相关文章

Java日志性能那些事(转)

在任何系统中,日志都是非常重要的组成部分,它是反映系统运行情况的重要依据,也是排查问题时的必要线索.绝大多数人都认可日志的重要性,但是又有多少人仔细想过该怎么打日志,日志对性能的影响究竟有多大呢?今天就让我们来聊聊Java日志性能那些事. 说到Java日志,大家肯定都会说要选择合理的日志级别.合理控制日志内容,但是这仅是万里长征第一步--哪怕一些DEBUG级别的日志在生产环境中不会输出到文件中,也可能带来不小的开销.我们撇开判断和方法调用的开销,在Log4J 2.x的性能文档中有这样一组对比:

Java程序性能优化(辛苦了几个小时,还经历了一次停电,我真是命苦!)

程序|性能|优化 Java程序性能优化 一.避免在循环条件中使用复杂表达式 在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快. 例子:import java.util.Vector;class CEL {    void method (Vector vector) {        for (int i = 0; i < vector.size (); i++)  // Violation            ; //

Java日志终极指南

Java日志基础Java使用了一种自定义的.可扩展的方法来输出日志.虽然Java通过java.util.logging包提供了一套基本的日志处理API,但你可以很轻松的使用一种或者多种其它日志解决方案.这些解决方案尽管使用不同的方法来创建日志数据,但它们的最终目标是一样的,即将日志从你的应用程序输出到目标地址. 在这一节中,我们会探索Java日志背后的原理,并说明如何通过日志来让你成为一个更好的Java开发人员. Java日志组件 Java日志API由以下三个核心组件组成: Loggers:Lo

Java编程性能优化技巧有哪些

1.尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: 第一,控制资源的使用,通过线程同步来控制资源的并发访问: 第二,控制实例的产生,以达到节约资源的目的: 第三,控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信. 2.尽量避免随意使用静态变量 要知道,当某个对象被定义为static变量所引用,那么GC通常是不会回收这个对象所占有的内存,如: 此时静态变量b的生命

java 日志接口怎么写? 考虑高并发

问题描述 java 日志接口怎么写? 需要考虑高并发,web容器和数据库基本动不了吧,代码怎么写更好些? 问题补充:日志是记到数据库表里的,不是生成日志文件 解决方案 默认情况下 如log4j都是有buffer的 满了之后批量写 而且写线程只有一个?不知道你说的高并发 是怎么个高并发 多高? 如果你要提高性能,无外乎:1.分开写(即不同的内容写到不同的日志) 2.批量写 即不要每次都写文件 而是成批的 3.提升硬件解决方案二:我是采用Netty + zookeeper + (hadoop.ora

Java日志管理的最佳实践

概述 对于现在的应用程序来说,日志的重要性是不言而喻的.很难想象没有任何日志记录功能的应用程序运行在生产环境中.日志所能提供的功能是多种多样的,包括记录程序运行时产生的错误信息.状态信息.调试信息和执行时间信息等.在生产环境中,日志是查找问题来源的重要依据.应用程序运行时的产生的各种信息,都应该通过日志 API 来进行记录.很多开发人员习惯于使用 System.out.println.System.err.println 以及异常对象的 printStrackTrace 方法来输出相关信息.这些

Java日志缓存机制的实现

概述 日志技术为产品的质量和服务提供了重要的支撑.JDK 在 1.4 版本以后加入了日志机制,为 Java 开发人员提供了便利.但这种日志机制是基于静态日志级别的,也就是在程序运行前就需设定下来要打 印的日志级别,这样就会带来一些不便. 在 JDK 提供的日志功能中,日志级别被细化为 9 级,用以 区分不同日志的用途,用来记录一个错误,或者记录正常运行的信息,又或是记录详细的调试信息.由于日志 级别是静态的,如果日志级别设定过高,低级别的日志难以打印出来,从而导致在错误发生时候,难以去追踪 错误

64位系统环境时Java的性能

如果你要买一辆车而且你的首要目标是性能或者更具体的说是原始动力,那么在4缸发 动机和8缸发动机之间选择的话,答案很显然,因为越大越好.通常而言,当我们看计算 机配置列表或者产品宣传的时候,64位的性能也比32位有优势,同样四核比双核更棒. 然而许多在大同世界里很简单的道理包括越多/大越好,移到计算机领域里就不是那么 回事了.当处理多重CPU时,你会觉得那些多核所多出来的处理单元很有用,但如果你的 工作仅仅是单线程的,那你要做的却是让其他核一边歇着. 32位与64位的比较则更加细微.x86-64

ibaties+mysql+java 查询性能问题

问题描述 ibaties+mysql+java 查询性能问题 通过ibaties 访问数据库返回列表给Java list,mysql控制台查询0.004秒 Java代码执行1分钟左右.有些时候 通过mysql管理工具查询几百万数据简单的sql实际看到数据时间是4分钟,系统返回的查询时间时0.002秒这种问题是由于什么引起的.怎样优化此类问题谢谢. 补充一下:不考虑硬件. 解决方案 可能的原因为: 1. 和数据库的IO负载有关: 2. mybatis查询开启了缓存. 针对查询,优化查询使用的索引,