日志对性能的影响以及我的几个日志习惯

 关注公众号【1024个为什么】,及时接收最新推送文章! 

本文内容:

1、背景

2、影响性能的日志因素

3、好的日志习惯

||  背景

写这篇文章有 2 个原因,一是前段时间架构组同事的一次性能优化分享,单单日志(log4j2)这一项优化性能就提升了 19 倍,QPS 从1660 升到 32000,被震撼到了。

二是最近接手的一个项目中,日志加了彩色打印,按日志的级别设置了不同的高亮颜色,看得我眼花缭乱,用 less 等一些命令查看时还会展示出来一堆 "ESC[m]",这让有点强迫症的我看着很不爽。

趁着技术优化,把这块也改一下,自己先爽了再说。顺便也重新认识一下在各种算法、秒杀设计大行其道的当下,这个不太起眼的小家伙。

||  影响性能的日志因素

| 位置信息

官网称作 Location Information,就是我们配置文件里的这类信息(%c{3}#%M %L),含义是当前这行日志是哪个类的哪个方法哪一行打印的。输出效果如下:

cashpool.task.OrderReceiveTask#run 78

可配置的模式有很多,具体见官网 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 倍。

Generating line number information (location information) is an expensive operation and may impact performance. Use with caution.

为什么会这么慢呢?

我们都知道,java 程序执行时,每个线程都会有自己的栈,每个方法都会生成一个 frame,要获取位置信息,就要获取当前线程的栈帧信息,java 9 之前提供了两种获取栈帧的方法

java.lang.Throwable#getStackTrace()
java.lang.Thread#getStackTrace()

java 9 提供了 StackWalker 类,有网友说它的性能要好一些,我没找到有力证据,看到的都是介绍跳帧的功能,有兴趣的可以深究一下。

我们就看一下 java 9 之前的 2 种方法:

c6b78ea6d2a5f9779bd8012693f18708.png

2a0f55419b7add7f3ffb3a8a65302413.png

有网友说 java.lang.Throwable#getStackTrace() 的性能要好一些,我也没有找到直接证据,但从调用底层 native 的方法名字看 Thread 里调用的是 dumpThreads ,看到 dump 字眼,就会联想到 stop the world ,如果真挂起线程,那效率就低了。

而且 log4j 里用的是java.lang.Throwable#getStackTrace()。

d3d0550199e37c8fd31a8a1069e16c0c.png

不管是哪种方法获取堆栈信息,应该都不会太高效。

想想我们一个请求,从框架层面交给一个线程后,动辄几十层方法才能调到我们的业务代码太正常不过了。

既然这么低效,那我们不打位置信息,有问题怎么定位呢?

我仔细回想了一下过往排查问题时,好像都是通过日志内容,定位到哪一行代码,几乎没有通过类名行号去定位代码,编译后的行号准不准确也另说。

其实我们的主要目的是不通过框架获取堆栈的形式打印位置信息,我们完全可以在日志的内容中带上位置信息,通过一个切面就能搞定的事。

| 不同的 Appender

我们工作中一般都是把日志输出到文件中的,我们就挑 3 个文件相关的 Appender 说明一下,不同的 Appender 性能的差异主要在 I/O 上。

FileAppender RollingFileAppender   内部使用的都是 BufferedOutputStream。

RandomAccessFileAppender 内部使用了 ByteBuffer + RandomAccessFile 技术,与 FileAppender 相比,性能提高了20 ~ 200%。

AsyncAppender 它不能独立存在,要依赖其他的 Appenders,配置在它们之后。

它只是新起一个线程中把 LogEvents 交给了它所依赖的 Appenders。默认内部使用的是 ArrayBlockingQueue ,多线程并发打日志时,性能可能会变得更差。这种场景官方推荐使用无锁的 Asynchronous Loggers 。

Asynchronous Loggers,它是 log4j2 新提供的功能,通过新起线程执行 I/O 操作来提升性能,底层使用的是  Disruptor 框架,通过无锁线程通信,代替了 ArrayBlockingQueue 。

它支持所有 Loggers 异步处理,也支持同步、异步 组合使用。可靠性要求高的比如异常信息就可以配成同步的,其他配成异步的。

每个 Appender 的具体用法可以从官网查看,

https://logging.apache.org/log4j/2.x/manual/appenders.html

| 不同的刷盘策略

上面说到的 Appenders 的配置项中,都有一个 “immediateFlush”,默认 = false,日志文件不像数据库那样追求高可靠性,可以忽略此配置,知道配置为 true 性能会变差就行。

貌似所有涉及到刷盘的技术,都会提供这类的配置项,这里就不多说了。

||  好的日志习惯

| 日志格式统一

这里主要指日志内容中各个元素的顺序,统一的日志格式,不仅查看起来方便、高效,而且如果有通过日志做一些筛选、统计类的需求,可以轻松搞定。

| 日志文件大小

如果访问量不是很大,一天也就几百兆,个人觉得完全没必要按大小或者按小时拆分日志文件。

反倒是决定拆分文件之前,更应该看看日志打的是否合理,是不是打了很多无用日志导致的。

| 不打无用日志

日志内容,能少就少,不在循环中打,大 list 简化,不打无用内容。

比如日志里的时间,[年-月-日] 是不是可以不打,文件名上带年月日就行,这样每行就能省 10 个字符,如果只打时分秒很难受,可以加上[月-日],年真没必要加。

还有线程名,打出来的都是线程池里的线程名称,没有任何意义,有 traceId 过滤出一次请求的日志就可以了,这个不打又可以省一二十个字符。

单靠这几点小优化后,日志文件就缩小了 20%。

| 关键信息提到最前

很多告警都是通过监控日志关键字触发的,然后发消息给负责人。但消息内容一般都很长,可能包含前后行的信息,如果前行内容长,消息再做省略处理,你收到消息后啥有用的内容也看不到了,只知道收到了报警,具体信息还要去看日志。

我们需要把最关键的信息打在最前面,尤其像失败原因之类的,理想的效果就是看到报警消息就知道问题原因,不用再去查日志了。

| 敏感信息和日期

我们打的日志中难免会包含手机号、银行卡号等敏感信息,这些都要脱敏的。

还有日志里打出来的业务时间,如果不做特殊处理,一般都是一个毫秒串,需要知道具体时间的话还得再通过工具转换一次,很不方便。

我们打日志一般都是 JSON 串,这 2 个问题都可以统一解决,通过 fastjson 的 Filter 就能轻松搞定。

脱敏需要自己写 Filter 和 脱敏规则,日期就用 fastjson  自带的 SerializerFeature#WriteDateUseDateFormat。

| 用好 debug 级别

我发现很多人当然也包括我在内,都不太用 debug 级别的日志,就像大家都不喜欢写单测一样。

其实 debug 日志是一个很好的习惯,不仅在联调测试过程中方便定位问题,也不会造成生成环境过多无用的日志,还有助于代码的阅读,何乐不为。

| 用好切面日志

我们一般加切面日志,都是不加区分的把方法的入参、返回结果统统打印出来,而且有的还控制层、服务层等层层打印,尤其像返回结果是 List 的,就造成很多无用日志。

所以在加切面的时候是不是可以加上日志级别、返回类型的考虑,用不用每层都加切面。

前面最开始提到的位置信息,就可以通过切面解决,即不损失功能,也提高了性能。

扯两句

做一个“抠门”的程序员,性能应该不会太差吧

你忽略掉的,往往是重要的

原创不易,如有收获,一键三连,感谢支持!

参考文献:

https://docs.oracle.com/javase/9/docs/api/java/lang/StackWalker.html

https://stackoverflow.com/questions/2347828/how-expensive-is-thread-getstacktrace

https://zhuanlan.zhihu.com/p/471819212

 https://logging.apache.org/log4j/2.x/manual/layouts.html#LocationInformation

https://logging.apache.org/log4j/2.x/manual/layouts.html#Patterns

https://logging.apache.org/log4j/2.x/manual/appenders.html

https://logging.apache.org/log4j/2.x/performance.html#asyncLogging

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PlugNT CMS,一款免费开源内容管理系统,PlugNT系统组成部分之一,是一个功能强大、操作人性化、搜索引擎优化、高效性、安全性、扩展性强的Web系统,是定位于中高端市场的CMS内容管理系统,能够以最低的成本、最少的人力投入在最短的时间内架设一个功能齐全、性能优异、规模庞大的网站平台。 PlugNT开源CMS 4.7 更新日志:2013-12-17 完善授权模块,单个页面授权可自定义操作权限 优化生成统计SQL。 修复了以前提出的常见问题。 产品主要优点有以下几点。 1、功能强大:Web所使用功能,主要包括新闻管理(包括产品管理,下载管理等),留言管理,广告管理,以及URL地址重写功能。 2、操作人性化:后台主要由“左菜单,右功能”的布局,左菜单全自动化设置,可以完全按照用户的使用习惯定制管理菜单,右边的功能的设置和排版经过参考多数Web用户的习惯设置。 3、搜索引擎优化:系统不但对关键字,标题等经行优化,还增加了动态地址重写功能,增加搜索引擎的友好性,让您的页面更容易受到搜索引擎的青睐。 4、高效性:系统采用三层构架,充分利用了缓存技术;对sql语句和相关逻辑的优化;经过多次的反复测试;大大提高了系统的反应速度。 5、安全性:严格的权限控制机制,让您可以精确控制到每一步的操作;操作日志的记录,可以随时查询系统的变化情况;强有力的漏洞检测(Sql注入,地址欺骗等),让系统可以免除安全隐患。 操作系统:Windows Server 2003、Windows 2000、Windows XP IIS版本:IIS 5.0、IIS6.0(推荐)及以上版本 .NET版本:.NET Framework 2.0 以上 数据库:Access, SQLSERVER 2000 以上 许可:开源免费

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值