其他分享
首页 > 其他分享> > 日志最佳实践

日志最佳实践

作者:互联网

近一年多以来想要分享知识的欲望降低了许多,不知道是否是近一年来工作较忙的原因,导致整个21年没有对外输出什么内容,唯一的一篇 “Log4j2 Jndi 漏洞原理解析、复盘” 还是在趁热想抓波热点的情况下写的这篇文章(21年12月10号爆出漏洞、11号公司内修复、12号凌晨05:00趁热发布文章,发布完以后直接埋头睡了一天,现在想来简直丧心病狂!)。

转眼已经到了2022,复盘2021的时候会发现并非如此,整个2021年是有做很多输入的,但由于并没有对外输出,而只是将对应的内容简单的记录到了自己的笔记中,导致回头再看时,会发现笔记中的很多内容并不系统,只是记录了一个点的问题,而非面。

输出是一个很好的习惯,输入并输出的过程也是在不断的翻新自我知识体系的过程,在持续输出的过程中,不仅帮助了自己也可能会帮助到他人。一举多得。

所以,就用这篇来作为2022年博客园的第一篇吧,GO,GO! (PS:该篇文章已经在笔记中躺了有段时间了、略微整理后,重新分享出来。)

背景

公司当前日志平台一天所沉淀的业务日志有近600亿条,40T左右的大小,随着公司内降本增效的事项推进,再加上业务方业务日志越来越多等问题,导致整个日志平台的资源是越发紧张的。

为了约束业务团队的日志数量,除了一些硬性的手段(日志SDK端采样、服务端采样、成本平摊),以及通过一些硬性的指标,要求业务团队的日志条数链路比、日志大小链路比 必须控制在一个合理的范围内之外,还需要一些小的科普。

而这个科普便是这次内容的主题 “日志最佳实践”,目的是为了让部分研发知晓:“噢,日志原来也是要像代码一样不断优化的啊”。

前言

日志用来记录用户操作、系统运行状态等,是一个系统的重要组成部分。然而,由于日志通常不属于系统的核心功能,所以常常不被团队成员所重视,在出现问题需要通过日志来定位时,才发现日志还存在很多问题;日志记录的好坏直接关系到系统出现问题时定位的速度,同时通过对日志的监控和分析,可以提前发现系统可能的风险,避免线上事故的发生。

现阶段我们的监控系统已经为我们的服务稳定性提供了较强的支撑,其中实时的异常大盘、日志的异常告警、日志的统一收集检索以及日志和链路的串联等功能为我们在微服务体系下问题的诊断提供了快速的排查方案。但随着时间的推移,代码中的日志质量持续降低(当项目变大时,项目的代码也存在一样的问题,越写越乱)。随着越来越多不规范的日志开始出现,除了会增加监控系统的承压,还会对线上的问题定位、稳定性监控等造成干扰。所以,日志的优化则势在必行,一方面监控系统降压降本,另一方面增加线上服务的稳定性及故障快速定位的能力。

原创声明:作者:陈咬金、 博客地址:https://www.cnblogs.com/zh94/

日志的分类

日志从功能来说,可分为诊断日志、统计日志、审计日志。
诊断日志, 典型的有:

统计日志:

审计日志:

需要避免的日志记录方式

在写代码的过程中,统计和审计日志是带有强烈目的性、功能性的,比如:用户访问统计的图表,非法访问的记录等。

但是针对诊断日志则不然,由于我们无法确定系统在具体哪行代码中会出现异常,所以诊断日志的添加也就因人而异,不同研发的风格,喜欢在不同的代码处增加诊断日志。

比如:有些研发同学喜欢在方法的入口处增加日志,有些研发则是在可能会出现不确定性的判断体中增加日志。这些都没问题,毕竟某块代码哪里最容易出现问题,最需要诊断日志,这些只有具体的研发才知晓。

但是我们需要约束并避免的情况是:

1、日志中不要记录无用的信息

如下图中的服务日志:单条日志的长度已经超出了5000的长度,并已经触发了服务端的日志截断操作。(既然是诊断日志,应该是诊断该方法内的部分属性,或者可能出现问题的属性,而不应该是全部输出)

2、不要记录无用的日志(不利于问题分析诊断的日志)

如下图中:日志Msg为 “threadLocal进行清除”这段日志,如果说这段日志有没有意义?“有的”,这段日志表示 threadLocal被清除了,很直接明了。但是如果说这段日志对问题的分析诊断有没有作用?作用极小。

假设这里 ThreadLocal remove() 的操作执行前是有 If 判断,判断为 true 时,则执行ThreadLocal 清除,那么对应的日志应该是添加到 else 代码块中,如果判断条件为 false,则 else 代码中输出 WARN 日志,或者 ERROR 日志,表示此处存在风险或者异常需要进行关注,而不是在此处增加 INFO 日志,告知对应的研发或者测试同学 “ThreadLocal 清除成功了”。

ThreadLocal remove() 这个代码块被执行,是一个必然为真的操作,如果存在对应清除失败的情况,应该最终是会体现在对应的异常当中。所以针对这样一个必然为真的代码一定要增加对应的日志,那么最好的方式是调整日志级别为DEBUG。

3、能够放在一条日志中的信息,尽量放在一条日志当中,而不要放在多条日志中进行输出。

4、日志内容中不能包含敏感性信息

原创声明:作者:陈咬金、 博客地址:https://www.cnblogs.com/zh94/

日志的持续优化

理想中的日志应该是不多不少的状态,太多的信息则是噪音,太少的信息又不够充分,我们需要在实际的运维过程中,结合线上问题的定位,不断地进行优化。最关键的一点是,团队要重视日志优化这件事情,不要让日志的质量持续降低。

此处推荐如下几个较好的实践:

1、输出日志时要考虑日志的使用者,例如如果日志需要由系统的测试人员来看,那就不能输出:

2022-03-01 14:08:16.244 WARN [http-nio-20959-exec-86] com.ymm.ability.IdentifyValidateAbility ErrorCode:1426

至少应该是:

2022-03-01 14:08:16.244 WARN [http-nio-20959-exec-86] com.ymm.ability.IdentifyValidateAbility ErrorCode:1426, Message: callback request (to http://example.com/callback) failed due to socket timeout

这样测试人员一眼就能清楚问题的原因,而不需要再通过开发来查看ErrorCode对应的具体错误。

2、日志的记录信息要完整,尤其是针对异常的日志而言,如:

关于日志级别

原创声明:作者:陈咬金、 博客地址:https://www.cnblogs.com/zh94/

INFO和DEBUG

了解了上述日志级别的含义后,可能仍然会存在的一个问题是,INFO和DEBUG到底如何区分?我觉得这个日志既可以是INFO也可以是DEBUG?如何处理?

此处简单给出一些参考:

对于业务系统来说,INFO 日志应该看起来像一本书。它应该告诉你发生了什么,而不一定是如何发生的,比如:

INFO  | 发送用户注册邮件通知。[user="Thomas", email="thomas@tuhrig.de"]
INFO  | 邮件发送给用户 [user="Thomas"]
INFO  | 发送用户取消关注邮件通知。[user="Thomas", email="thomas@tuhrig.de"]

而对于系统当中的技术细节,则通常更应该使用DEBUG日志,DEBUG 日志可以更详细地了解该过程的工作方式

DEBUG | 将用户信息发送进MQ通知列表。[user="Thomas", email="thomas@tuhrig.de"]
INFO  | 发送用户注册通知 [user="Thomas", email="thomas@tuhrig.de"]
DEBUG | 开始发送当天邮件通知的定时任务。 [subscribers=24332]
INFO  | 邮件发送给用户 [user="Thomas"]
INFO  | 发送用户取消关注邮件通知。 [user="Thomas", email="thomas@tuhrig.de"]

想象一下,你在浏览INFO日志的时候,是在查看该系统的运行状态,此时看INFO日志更关注的是系统的运行指标,而并非那些技术上的细节是否执行成功。对应的技术细节是否执行成功应该是体现在对应的DEBUG日志当中,通过浏览DEBUG日志以此来对系统的运行进行精确的记录和诊断。

所以,类似于上面最初所提到的“ threadLocal 清除成功”这种日志,如果有必须存在的意义,则应该是添加到DEBUG级别当中更为合适。

而对于技术类系统来说,比如中间件,工具类,公共服务等来说,INFO级别的日志同样应该是告诉你发生了什么,而不是对应的技术细节。

比如:

  1. 远程加载了对应的配置文件,那么此时 INFO 输出该配置文件的内容则没有问题。
  2. JVM启动成功后,会有一个INFO级别的 Started Application in 35.499 seconds (JVM running for 45.536) 来表示JVM启动成功,但JVM是不会把类的加载过程 INFO 输出出来,因为对于INFO日志来说,并不关注这个。
  3. MVC的服务在服务启动后,同样会使用INFO级别的日志,将加载成功的Controller接口输出出来,而并不会通过INFO来告知使用方Controller的加载细节。
2022-03-01 15:18:03,553 INFO  [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] [main] Mapped "{[/nondestructive/release/health/check],methods=[POST],consumes=[application/json;charset=UTF-8],produces=[application/json;charset=UTF-8]}"

所以基于上述的一些内容,我们可以得到一些共性的认知,INFO级别的日志更应该关注的是系统中发生了什么较为重要的事件,需要让日志的查看者关注到这些信息。而不是技术的执行细节上执行了那些流程。执行细节应该是由DEBUG日志来进行输出。

所以对于在线下DEV和QA环境进行日志诊断、问题排查时,应该更多的是查看DEBUG日志来进行问题的排查,而不应该过多的依赖 INFO 日志。

对于线上环境则应该屏蔽掉DEBUG日志的输出,仅需要观察INFO,WARN,ERROR日志,来观察服务的运行状态。

在项目中日志优化应该是比代码、架构优化更为频繁的操作,我们应该随着问题的排查来不断的优化我们的日志输出。

同时,从日志的功能分类上我们也可以得知,日志的最重要的一个特性是:诊断日志,我们在不断优化日志的同时,也是对自己项目深入诊断的过程,在优化诊断日志的过程中,如果可以提前预判到某些问题的发生,如:连接池的耗尽,对象等资源的加载统计,而提前增加了诊断日志。这将会对于我们后续故障时的问题定位有着非常好的参考作用。

参考资料

Log4j2 Articles and Tutorials
Logging best practices to get the most out of application level logging
The Art of Test Driven Development: Understanding Logging
中小企业运维需要重视日志分析
王健:最佳日志实践

标签:INFO,记录,实践,诊断,最佳,DEBUG,日志,级别
来源: https://www.cnblogs.com/zh94/p/16109603.html