日志最佳实践 实践日志总结报告
bigegpt 2024-09-27 00:33 3 浏览
背景
公司当前日志平台一天所沉淀的业务日志有近600亿条,40T左右的大小,随着公司内降本增效的事项推进,再加上业务方业务日志越来越多等问题,导致整个日志平台的资源是越发紧张的。
为了约束业务团队的日志数量,除了一些硬性的手段(日志SDK端采样、服务端采样、成本平摊),以及通过一些硬性的指标,要求业务团队的日志条数链路比、日志大小链路比 必须控制在一个合理的范围内之外,还需要一些小的科普。
而这个科普便是这次内容的主题 “日志最佳实践”,目的是为了让部分研发知晓:“噢,日志原来也是要像代码一样不断优化的啊”。
前言
日志用来记录用户操作、系统运行状态等,是一个系统的重要组成部分。然而,由于日志通常不属于系统的核心功能,所以常常不被团队成员所重视,在出现问题需要通过日志来定位时,才发现日志还存在很多问题;日志记录的好坏直接关系到系统出现问题时定位的速度,同时通过对日志的监控和分析,可以提前发现系统可能的风险,避免线上事故的发生。
现阶段我们的监控系统已经为我们的服务稳定性提供了较强的支撑,其中实时的异常大盘、日志的异常告警、日志的统一收集检索以及日志和链路的串联等功能为我们在微服务体系下问题的诊断提供了快速的排查方案。但随着时间的推移,代码中的日志质量持续降低(当项目变大时,项目的代码也存在一样的问题,越写越乱)。随着越来越多不规范的日志开始出现,除了会增加监控系统的承压,还会对线上的问题定位、稳定性监控等造成干扰。所以,日志的优化则势在必行,一方面监控系统降压降本,另一方面增加线上服务的稳定性及故障快速定位的能力。
原创声明:作者:陈咬金、 博客地址:https://www.cnblogs.com/zh94/
日志的分类
日志从功能来说,可分为诊断日志、统计日志、审计日志。
诊断日志, 典型的有:
- 请求入口和出口
- 外部服务调用和返回
- 资源消耗操作: 如读写文件等
- 容错行为:如云硬盘的副本修复操作
- 程序异常: 如数据库无法连接
- 后台操作:定期执行删除的线程启动、关闭、配置加载
统计日志:
- 用户访问统计:用户IP、上传下载的数据量,请求耗时等。(常用的Log Metric 则是统计日志)
审计日志:
- 管理操作、系统安全事件、非法访问记录等
需要避免的日志记录方式
在写代码的过程中,统计和审计日志是带有强烈目的性、功能性的,比如:用户访问统计的图表,非法访问的记录等。
但是针对诊断日志则不然,由于我们无法确定系统在具体哪行代码中会出现异常,所以诊断日志的添加也就因人而异,不同研发的风格,喜欢在不同的代码处增加诊断日志。
比如:有些研发同学喜欢在方法的入口处增加日志,有些研发则是在可能会出现不确定性的判断体中增加日志。这些都没问题,毕竟某块代码哪里最容易出现问题,最需要诊断日志,这些只有具体的研发才知晓。
但是我们需要约束并避免的情况是:
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/
日志的持续优化
理想中的日志应该是不多不少的状态,太多的信息则是噪音,太少的信息又不够充分,我们需要在实际的运维过程中,结合线上问题的定位,不断地进行优化。最关键的一点是,团队要重视日志优化这件事情,不要让日志的质量持续降低。
此处推荐如下几个较好的实践:
- 在定位问题的过程中完善日志,如果定位问题花费了很长时间,那就说明系统日志还存在问题,需要进一步完善和优化;
- 需要思考是否可以通过优化日志,来提前预判该问题是否可能发生(如某种资源耗尽而导致的错误,可以对资源的使用情况进行记录)
- 定义好整个团队记录日志的规范,保证每个开发记录的日志格式统一;特别需要说明的是,对于DEBUG/TRACE级别的日志,也需要定义好清晰的格式,而不是由研发人员自由发挥;
- 整个团队(包括开发,运维和测试)定期对记录的日志内容进行Review;
- 开发做运维、测试,通过在查问题的过程来优化日志记录的方式;
- 测试在日志中发现的问题,都需要及时向开发人员反映;
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、日志的记录信息要完整,尤其是针对异常的日志而言,如:
- 在持久化数据修改时记录修改前和修改后的值。
- 记录关键参数,出错时的关键原因等。
- 记录对应的异常代码执行前的上下文信息。
关于日志级别
- FATAL — 表示需要立即被处理的系统级错误。当该错误发生时,表示服务已经出现了某种程度的不可用,系统管理员需要立即介入。这属于最严重的日志级别,因此该日志级别必须慎用,如果这种级别的日志经常出现,则该日志也失去了意义。通常情况下,一个进程的生命周期中应该只记录一次FATAL级别的日志,即该进程遇到无法恢复的错误而退出时。当然,如果某个系统的子系统遇到了不可恢复的错误,那该子系统的调用方也可以记入FATAL级别日志,以便通过日志报警提醒系统管理员修复;
- ERROR — 该级别的错误也需要马上被处理,但是紧急程度要低于FATAL级别。当ERROR错误发生时,已经影响了用户的正常访问。从该意义上来说,实际上ERROR错误和FATAL错误对用户的影响是相当的。FATAL相当于服务已经挂了,而ERROR相当于好死不如赖活着,然而活着却无法提供正常的服务,只能不断地打印ERROR日志。特别需要注意的是,ERROR和FATAL都属于服务器自己的异常,是需要马上得到人工介入并处理的。而对于用户自己操作不当,如请求参数错误等等,是绝对不应该记为ERROR日志的;
- WARN — 该日志表示系统可能出现问题,也可能没有,这种情况如网络的波动等。对于那些目前还不是错误,然而不及时处理也会变为错误的情况,也可以记为WARN日志,例如一个存储系统的磁盘使用量超过阀值,或者系统中某个用户的存储配额快用完等等。对于WARN级别的日志,虽然不需要系统管理员马上处理,也是需要及时查看并处理的。因此此种级别的日志也不应太多,能不打WARN级别的日志,就尽量不要打;
- INFO — 该种日志记录系统的正常运行状态,例如某个子系统的初始化,某个请求的成功执行等等。通过查看INFO级别的日志,可以相对较快的对系统中出现的 WARN,ERROR,FATAL错误进行定位。INFO日志不宜过多,通常情况下,INFO级别的日志应该不大于TRACE日志的10%;
- DEBUG OR TRACE — 这两种日志具体的规范应该由团队自己定义,该级别日志的主要作用是对系统每一步的运行状态进行精确的记录。通过该种日志,可以查看某一个操作每一步的执 行过程,可以准确定位是何种操作,何种参数,何种顺序导致了某种错误的发生。可以保证在不重现错误的情况下,也可以通过DEBUG(或TRACE)级别的日志对问题进行诊断。需要注意的是,DEBUG日志也需要规范日志格式,应该保证除了记录日志的开发人员自己外,其他的如运维,测试人员等也可以通过 DEBUG(或TRACE)日志来定位问题;
原创声明:作者:陈咬金、 博客地址: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级别的日志同样应该是告诉你发生了什么,而不是对应的技术细节。
比如:
- 远程加载了对应的配置文件,那么此时 INFO 输出该配置文件的内容则没有问题。
- JVM启动成功后,会有一个INFO级别的 Started Application in 35.499 seconds (JVM running for 45.536) 来表示JVM启动成功,但JVM是不会把类的加载过程 INFO 输出出来,因为对于INFO日志来说,并不关注这个。
- 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日志,来观察服务的运行状态。
在项目中日志优化应该是比代码、架构优化更为频繁的操作,我们应该随着问题的排查来不断的优化我们的日志输出。
同时,从日志的功能分类上我们也可以得知,日志的最重要的一个特性是:诊断日志,我们在不断优化日志的同时,也是对自己项目深入诊断的过程,在优化诊断日志的过程中,如果可以提前预判到某些问题的发生,如:连接池的耗尽,对象等资源的加载统计,而提前增加了诊断日志。这将会对于我们后续故障时的问题定位有着非常好的参考作用。
作者:陈咬金
链接:https://www.cnblogs.com/zh94/p/16109603.html
相关推荐
- 有些人能留在你的心里,但不能留在你生活里。
-
有时候,你必须要明白,有些人能留在你的心里,但不能留在你生活里。Sometimes,youhavetorealize,Somepeoplecanstayinyourheart,...
- Python学不会来打我(34)python函数爬取百度图片_附源码
-
随着人工智能和大数据的发展,图像数据的获取变得越来越重要。作为Python初学者,掌握如何从网页中抓取图片并保存到本地是一项非常实用的技能。本文将手把手教你使用Python函数编写一个简单的百度图片...
- 软网推荐:图像变变变 一“软”见分晓
-
当我们仅需要改变一些图片的分辨率、裁减尺寸、添加水印、标注文本、更改图片颜色,或将一种图片转换为另一种格式时,总比较讨厌使用一些大型的图像处理软件,尤其是当尚未安装此类软件时,更是如此。实际上,只需一...
- 首款WP8.1图片搜索应用,搜照片得资料
-
首款WP8.1图片搜索应用,搜照片得资料出处:IT之家原创(天际)2014-11-1114:32:15评论WP之家报道,《反向图片搜索》(ReverseImageSearch)是Window...
- 盗墓笔记电视剧精美海报 盗墓笔记电视剧全集高清种子下载
-
出身“老九门”世家的吴邪,因身为考古学家的父母在某次保护国家文物行动时被国外盗墓团伙杀害,吴家为保护吴邪安全将他送去德国读书,因而吴邪对“考古”事业有着与生俱来的兴趣。在一次护宝过程中他偶然获得一张...
- 微软调整Win11 24H2装机策略:6月起36款预装应用改为完整版
-
IT之家7月16日消息,微软公司今天(7月16日)发布公告,表示自今年6月更新开始,已默认更新Windows1124H2和WindowsServer2025系统中预装...
- 谷歌手把手教你成为谣言终结者 | 域外
-
刺猬公社出品,必属原创,严禁转载。合作事宜,请联系微信号:yunlugongby贾宸琰编译、整理11月23日,由谷歌新闻实验室(GoogleNewsLab)联合Bellingcat、DigD...
- NAS 部署网盘资源搜索神器:全网资源一键搜,免费看剧听歌超爽!
-
还在为找不到想看的电影、电视剧、音乐而烦恼?还在各个网盘之间来回切换,浪费大量时间?今天就教你如何在NAS上部署aipan-netdisk-search,一款强大的网盘资源搜索神器,让你全网资源...
- 使用 Docker Compose 简化 INFINI Console 与 Easysearch 环境搭建
-
前言回顾在上一篇文章《搭建持久化的INFINIConsole与Easysearch容器环境》中,我们详细介绍了如何使用基础的dockerrun命令,手动启动和配置INFINICon...
- 为庆祝杜特尔特到访,这个国家宣布全国放假?
-
(观察者网讯)近日,一篇流传甚广的脸书推文称,为庆祝杜特尔特去年访问印度,印度宣布全国放假,并举办了街头集会以示欢迎。菲媒对此做出澄清,这则消息其实是“假新闻”。据《菲律宾世界日报》2日报道,该贴子...
- 一课译词:毛骨悚然(毛骨悚然的意思是?)
-
PhotobyMoosePhotosfromPexels“毛骨悚然”,汉语成语,意思是毛发竖起,脊梁骨发冷;形容恐惧惊骇的样子(withone'shairstandingonend...
- Bing Overtakes Google in China's PC Search Market, Fueled by AI and Microsoft Ecosystem
-
ScreenshotofBingChinahomepageTMTPOST--Inastunningturnintheglobalsearchenginerace,Mic...
- 找图不求人!6个以图搜图的识图网站推荐
-
【本文由小黑盒作者@crystalz于03月08日发布,转载请标明出处!】前言以图搜图,专业说法叫“反向图片搜索引擎”,是专门用来搜索相似图片、原始图片或图片来源的方法。常用来寻找现有图片的原始发布出...
- 浏览器功能和“油管”有什么关联?为什么要下载
-
现在有没有一款插件可以实现全部的功能,同时占用又小呢,主题主要是网站的一个外观,而且插件则主要是实现wordpress网站的一些功能,它不仅仅可以定制网站的外观,还可以实现很多插件的功能,搭载chro...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- libcrypto.so (74)
- linux安装minio (74)
- ubuntuunzip (67)
- vscode使用技巧 (83)
- secure-file-priv (67)
- vue阻止冒泡 (67)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)