压测过程中优化线程池以后单机 qps 存在性能瓶颈,优化过程中发现默认线程池及日志对性能存在严重的影响所以引发了一系列对日志优化的整理
在任何系统中,日志都是非常重要的组成部分,它是反映系统运行情况的重要依据,也是排查问题时的必要线索。绝大多数人都认可日志的重要性,但是又有哪些场景可能导致性能问题?今天就让我们来聊聊 Java 日志性能那些事。
如上 3 种写法,我相信大家或多或少都在项目代码中看到过,那么他们之前有区别呢,会对性能造成什么影响? 如果此时关闭 DEBUG 日志级别,差异就出现了
格式 1 依然还是要执行字符串拼接,即使它不输出日志,属于浪费。
格式 2 的缺点就是参数会提前做 JSON 序列化也产生性能损耗。
所以推荐格式 3,新增日志开关,只有在执行时才会动态的拼接,关闭相应日志级别后,不会有性能损耗。
尽量多的日志,能够把用户的请求串起来,更容易断定出问题的代码位置。由于当前分布式系统,且业务庞杂,任何日志的缺失对于程序员定位问题都是极大的障碍。所以要合理的打印日志,设置合理的日志级别
位置信息 官网称作 Location Information,含义是当前这行日志是哪个类的哪个方法哪一行打印的。
可配置的模式有很多,具体见官网
https://logging.apache.org/log4j/2.x/manual/layouts.html#Patterns 。
这里只说和位置相关的 %C or %class, %F or %file, %l or %location, %L or %line, %M or %method。
官网这几个模式的说明中也都反复强调了会影响性能。同时也给出了具体的性能数据,比常用的同步 logger 慢 1.3 ~ 5 倍。如果在异步 logger 中使用位置信息,将会慢 30 ~ 100 倍。
用好 DEBUG 级别!项目代码需要打印大量 INFO 级别日志,以支持问题定位及测试排查等。但这些大量的 INFO 日志对生产环境是无效的,大量的日志会吃掉 CPU 性能,此时需要能动态调整日志级别,既满足可随时查看 INFO 日志,又能满足不需要时可动态关闭,不影响服务性能需要。
日志内容,能少就少,不在循环中打,大 list 简化,不打无用内容。
明显知道的异常栈不要打印(比如自定义异常捕获后直接打印异常信息即可)
避免使用字符串连接:在日志记录中,字符串连接是一项较为昂贵的操作,特别是在循环中使用。每次进行字符串连接都会产生一个新的字符串对象,浪费了内存和时间。应优先考虑使用占位符的形式,如使用 slf4j 库中的"{}",然后传入参数,并避免使用字符串连接。
按需新增日志开关减少不必要的性能损耗,例如 JSON 序列化及字符串拼接等等。(若无相关操作则不需要新增日志开关,反而存在一堆废代码)
影响性能的位置信息按需选择减少性能损耗
同步打印日志磁盘 I/O 成为瓶颈,导致大量线程 Block,异步缺可能产生日志丢失。
作者:京东零售 王军
来源:京东云开发者社区 转载请注明来源