写点什么

Java 日志性能那些事

  • 2015-05-26
  • 本文字数:2177 字

    阅读完需:约 7 分钟

在任何系统中,日志都是非常重要的组成部分,它是反映系统运行情况的重要依据,也是排查问题时的必要线索。绝大多数人都认可日志的重要性,但是又有多少人仔细想过该怎么打日志,日志对性能的影响究竟有多大呢?今天就让我们来聊聊 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(它们不支持{}模板的写法)的公司都会有相应的编码规范,要求在一定级别的日志(比如DEBUGINFO)输出前增加判断:

复制代码
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在高并发大流量的系统里多少有些力不从心,这时就该使用AsyncAppender了,同样是使用 LogBack:

在 10 线程并发下,输出 200 字符的INFO日志,AsyncAppender的吞吐量最高能是FileAppender的 3.7 倍。在不丢失日志的情况下,同样使用AsyncAppender,队列长度对性能也会有一定影响。

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

64 线程下,异步Logger比异步Appender快 12 倍,比同步Logger68倍。

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

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

(点击放大图像)

Log4J 2.x 的异步Logger性能强悍,但也有不同的声音,觉得这只是个看上去很优雅,只能当成一个玩具。关于这个问题,还是留给读者自己来思考吧。

如果一定要用同步的 Appender,那么可以考虑使用ConsoleAppender,然后将STDOUT重定向到文件里,这样大约也能有 10% 左右的性能提升。

大部分生产系统都是集群部署,对于分布在不同服务器上的日志,用 Logstash 之类的工具收集就好了。很多时候还会在单机上部署多实例以便充分利用服务器资源,这时千万不要贪图日志监控或者日志查询方便,将多个实例的日志写到同一个日志文件中,虽然 LogBack 提供了prudent模式,能够让多个 JVM 往同一个文件里写日志,但此种方式对性能同样也有影响,大约会使性能降低 10%。

如果对同一个日志文件有大量的写需求,可以考虑拆分日志到不同的文件,做法之一是添加多个Appender,同时修改代码,不同的情况使用不同Logger;LogBack 提供了SiftingAppender,可以直接根据 MDC 的内容拆分日志, Jetty 的教程中就有根据host来拆分日志的范例,而根据 Takipi 的测试,SiftingAppender的性能会随着拆分文件数的增长一同提升,当拆分为 4 个文件时,10 并发下SiftingAppender的吞吐量约是FileAppender的 3 倍多。

看了上面这么多的数据,不知您是否觉得自己的日志有不少改进的余地,您还没有把系统优化到极致,亦或者您还有其他日志优化的方法,不妨分享给大家。


感谢丁晓昀对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015-05-26 21:1820532
用户头像

发布了 135 篇内容, 共 67.2 次阅读, 收获喜欢 43 次。

关注

评论

发布
暂无评论
发现更多内容

HTTP协议基础知识

穿过生命散发芬芳

HTTP 12月月更

2022年中国汽车智能网联云服务分析

易观分析

5G 汽车 智联网

用javascript分类刷leetcode3.动态规划(图文视频讲解)

js2030code

JavaScript LeetCode

如何实现移除控件?

Towify

编辑器 无代码 无代码微信小程序

Osx10.14升级watchman踩坑记

macos homebrew

React Streaming SSR 原理解析

字节跳动终端技术

React

信用卡应用评测系列——阳光惠生活APP7.0打造“世界波”专区

易观分析

金融 银行 信用卡

React源码分析7-state计算流程和优先级

flyzz177

React

react hook 源码完全解读

flyzz177

React

分支路径图调度框架在 vivo 效果广告业务的落地实践

vivo互联网技术

图调度 效果广告 分支路径

2022-12-19:大的国家。如果一个国家满足下述两个条件之一,则认为该国是 大国 : 面积至少为 300 万平方公里(即,3000000 km2),或者 人口至少为 2500 万(即 250000

福大大架构师每日一题

数据库 福大大

GBase 8s安装

@下一站

国产数据库 12月日更 12月月更 Gbase8s

React源码分析6-hooks源码

flyzz177

React

react源码中的fiber架构

flyzz177

React

好酒要有好工艺,贾斯特里尼&布鲁克斯,用心酿造每一滴酒

联营汇聚

从URL输入到页面展现到底发生什么?

loveX001

JavaScript

SPL 和 SQL 能不能融合在一起?

jiangxl

从输入URL到渲染的完整过程

loveX001

JavaScript

前端关于面试你可能需要收集的面试题

loveX001

JavaScript

vivo 推荐业务 x DeepRec:全链路优化实践

阿里云大数据AI技术

人工智能 gpu 推荐引擎 12 月 PK 榜

如何使用Towify在微信小程序中配置输入必填报错?

Towify

小程序 微信小程序 无代码 无代码微信小程序

正向拆解mlops

mlops

MLOps

前端工程师leetcode算法面试必备-二分搜索算法(上)

js2030code

JavaScript LeetCode

前端刷完这12道滑动窗口,就可以出山面试了

js2030code

JavaScript LeetCode

前端必会面试题总结

loveX001

JavaScript

华为时习知,让企业培训更简单!

科技怪授

华为

互联网医疗领域月度观察——二十大报告明确提出健康中国建设目标,互联网医疗是建设重点

易观分析

互联网医疗

React源码分析5-commit

flyzz177

React

「Go框架」路由:web框架中是如何根据url地址找到对应的处理逻辑的?

Go学堂

golang 个人成长 程序员‘ 12月月更

React源码解读之更新的创建

flyzz177

Java日志性能那些事_Java_丁雪丰_InfoQ精选文章