成功避开日志打印代码的这些坑,应用系统性能瞬间提升20%

文章探讨了在日志代码中隐藏的性能问题,如字符串拼接导致的性能损耗,以及如何通过使用占位符和logger.isDebugEnabled()方法进行优化,以减少在生产环境中的调试日志对性能的影响,最终实现性能提升20%以上。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


背景

我们的应用系统代码里,通常会有各种不同日志级别的代码,如error、warning、info、debug等日志级别。在不同的系统运行环境中,可以设置打印不同日志级别的日志。在开发环境和测试环境,会设置打印debug级别以上的日志。而在生产环境,会设置只打印info级别以上的日志,避免打印debug级别的日志对系统性能的影响。在大促时,还可以通过日志降级,只打印warning、error级别的日志,最大限度的减少对系统性能的影响,进一步提高系统吞吐量和保障系统的稳定性。

那么当我们设置好更高级别的日志级别时,是否就能真正提高系统的性能呢。有没有什么坑需要注意的呢?最近接触了几个应用系统,发现了日志打印代码的一些坑,一起来看一下。


一、日志代码的第一个坑

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

这是某个系统其中的一段debug日志代码:

logger.debug("入参:order=" + JSON.toJSONString(order));

当我们在生产环境中,设置打印info级别以上的日志,是不是觉得涉及debug日志的代码就不会对我们的系统有性能影响。

首先代码中的logger.debug方法是肯定会执行的,但我们会想到方法内部应该加了对debug日志级别的判断,当我们的日志级别是info的,是不会去真正执行日志打印。

我们首先来看下debug方法内部调用的代码:

    public void debug(final String format) {
        this.logger.logIfEnabled(FQCN, Level.DEBUG, (org.apache.logging.log4j.Marker)null, format);
    }

    public void logIfEnabled(final String fqcn, final Level level, final Marker marker, final String message) {
        if (this.isEnabled(level, marker, message)) {
            this.logMessage(fqcn, level, marker, message);
        }

    }

从代码看,debug方法内部会先判断当前日志级别是否符合设置的打印日志级别,比如说我们当前设置的打印info级别以上日志,内部会通过isEnabled方法判断,对于debug级别的日志就不会去执行打印了。从这点上来看,内部处理逻辑是没有问题的。

但是我们要注意的一点是,在logger.debug方法调用时,还是会解析传入方法的入参,也就是 "入参:order=" + JSON.toJSONString(order) 这段代码,可以看到入参会执行一个字符串变量的拼接,这会导致一定的性能损耗。

二、使用占位符提升性能

对于字符串变量的拼接这个问题,在log4j之后的版本,包括log4j2和logback中,可以使用占位符的方式来替代字符串拼接,提升性能。

使用占位符优化后的debug日志代码如下:

logger.debug("入参:order={}", JSON.toJSONString(order));

三、日志代码的第二个坑

经过占位符优化后的代码还有问题吗?

我们再看下优化后的代码,可以看到第二个入参JSON.toJSONString(order)是一个方法。程序执行logger.debug方法时,必然会调用到toJSONString()这个方法,得到这个入参的值。JSON.toJSONString(order)本身是一个JSON序列化操作,如果order对象比较大的话,是比较耗时的。我们曾经测试过一个100k字节的对象,JSON序列化的时间需要50ms以上。那如何解决这个问题呢。

四、使用logger.isDebugEnabled()方法提升性能

我们可以通过使用logger.isDebugEnabled()方法进行前置判断,从而在设置info日志级别以上时,避免对debug方法的调用,从而避免了序列化耗时操作。

先看下logger.isDebugEnabled()方法内部代码:

    public boolean isDebugEnabled() {
        return this.logger.isEnabled(Level.DEBUG, (org.apache.logging.log4j.Marker)null, (String)null);
    }

可以看到logger.isDebugEnabled()方法只对可打印的日志级别进行了一次判断,在性能方面的影响是最小的。

使用logger.isDebugEnabled()优化后的代码:

        if (logger.isDebugEnabled()) {
            logger.debug("入参:order={}", JSON.toJSONString(order));
        }

通过在logger.debug方法调用前,预先使用logger.isDebugEnabled()方法进行判断,可以避免logger.debug方法执行,把debug日志代码对系统性能的影响降到最低。


总结

对于info,debug,trace级别的代码,都可以使用占位符和logger.isInfoEnabled、logger.isDebugEnabled、logger.isTraceEnabled方法,降低在生产环境时,不必要的日志打印对系统性能的影响。通过这种方式的优化,这几个应用系统优化后的性能比优化前的性能平均提升20%以上。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值