当前位置: 首页 > news >正文

运维人员需知:最佳日志实践

为什么80%的码农都做不了架构师?>>>   hot3.png

运维人员需知:最佳日志实践

文/王 健

前言

 日志用来记录用户操作、系统运行状态等,

是一个系统的重要组成部分。然而由于日志并非

系统核心功能,通常情况下并不受团队的重视。

在出现问题需要通过日志来定位时,才发现日志

还存在很多问题。

 日志记录的好坏直接关系到系统出现问题时

定位的速度,同时可以通过对日志的观察和分

析,提前发现系统可能的风险,避免线上事故的

发生。

我们在开发和运维NOS(网易对象存 储,Netease Object Storage)的过程中,对整个系 统的日志进行了分析优化,积累出一些经验,归 纳如下。

相关问题经验整理:

1. 关于日志级别

我们通常使用的日志库(如log4j等),将日 志基本分为以下几类(从低到高):

尽管log4j官方文档对各个日志级别进行了简 单定义。然而在实践中,究竟哪些操作需要记入 日志,哪种错误应该记为WARN级别,而哪种错 误又为ERROR级别,还需要进行进一步讨论。

关于该问题,在StackOverflow上有一个讨论贴 进行过讨论。

 此处对贴子中的一些观点,加上我们在平时

运维过程中遇到的相关问题进行归纳:

• 一个项目各个log级别的定义应该是清楚明 确的,是每个开发人员所遵循的;

• 即使是TRACE或者DEBUG级别的日志,也 应该有一定的规范,要保证除了开发人员自己以 外,包括测试人员和运维人员都可以方便地通过 日志定位问题;

 对于日志级别的分类,有以下参考:

FATAL — 表示需要立即被处理的系统级错 误。当该错误发生时,表示服务已经出现了某种 程度的不可用,系统管理员需要立即介入。这属 于最严重的日志级别,因此该日志级 别必须慎 用,如果这种级别的日志经常出现,则该日志也 失去了意义。通常情况下,一个进程的生命周期 中应该只记录一次FATAL级别的日志,即该进程 遇到无 法恢复的错误而退出时。当然,如果某 个系统的子系统遇到了不可恢复的错误,那该子 系统的调用方也可以记入FATAL级别日志,以便 通过日志报警提醒系统管 理员修复;

ERROR — 该级别的错误也需要马上被处理, 但是紧急程度要低于FATAL级别。当ERROR错 误发生时,已经影响了用户的正常访问。从该意 义上来说,实际上 ERROR错误和FATAL错误对 用户的影响是相当的。FATAL相当于服务已经挂 了,而ERROR相当于好死不如赖活着,然而活 着却无法提供正常的服 务,只能不断地打印 ERROR日志。特别需要注意的是,ERROR和 FATAL都属于服务器自己的异常,是需要马上得 到人工介入并处理的。而对于用户自己 操作不 当,如请求参数错误等等,是绝对不应该记为 ERROR日志的;

TRACE - The TRACE Level designates finer- grained informational events than the DEBUG

DEBUG – The DEBUG Level designates fine-grained informational events that are most useful to debug an application.

INFO - The INFO level designates informational messages that highlight the progress of the application at coarse-grained level.

WARN - The WARN level designates potentially harmful situations.

ERROR - The ERROR level designates error events that might still allow the application to continue running.

FATAL - The FATAL level designates very severe error events that will presumably lead the application to abort.

30 观技察巧思经考验 LiLninuux运x运维维趋趋势势2021021年43年123月号 总第23245期

“通过查看INFO级别的日志,可以很快地对系统中出 现的 WARN,ERROR,FATAL错误进行定位。”

WARN — 该日志表示系统可能出现问题,也 可能没有,这种情况如网络的波动等。对于那些 目前还不是错误,然而不及时处理也会变为错误 的情况,也可以记为WARN日 志,例如一个存储 系统的磁盘使用量超过阀值,或者系统中某个用 户的存储配额快用完等等。对于WARN级别的日 志,虽然不需要系统管理员马上处理,也是需要 即使查看并处理的。因此此种级别的日志也不应 太多,能不打WARN级别的日志,就尽量不要 打;

INFO — 该种日志记录系统的正常运行状 态,例如某个子系统的初始化,某个请求的成功 执行等等。通过查看INFO级别的日志,可以很 快地对系统中出现的 WARN,ERROR,FATAL错误 进行定位。INFO日志不宜过多,通常情况 下,INFO级别的日志应该不大于TRACE日志的 10%;

DEBUG or TRACE — 这两种日志具体的规范 应该由项目组自己定义,该级别日志的主要作用 是对系统每一步的运行状态进行精确的记录。通 过该种日志,可以查看某一个操作每一步的执 行过程,可以准确定位是何种操作,何种参数, 何种顺序导致了某种错误的发生。可以保证在不 重现错误的情况下,也可以通过DEBUG(或 TRACE)级别的 日志对问题进行诊断。需要注 意的是,DEBUG日志也需要规范日志格式,应 该保证除了记录日志的开发人员自己外,其他的 如运维,测试人员等也可以通过 DEBUG(或 TRACE)日志来定位问题;

Rule 1:整个团队(包括运维人员)需要对日 志级别有明确的规定,什么日志记入什么级别的 日志,什么级别的错误出现要如何处理等。

2. 对记录的日志要进行更新维护

由于DEBUG(或TRACE)级别的日志对于定 位问题至关重要,因此该种日志记录是否完备且 不冗余、格式是否规范等也需要花费大量精力来 优化。此处有以下几个比较好的实践:

• 定义好整个团队记录DEBUG(或TRACE) 日志的规范,保证开发记录的日志格式统一;

• 整个团队(包括开发,运维和测试)定期 对记录的日志内容进行Review;

• 开发做运维,通过在查问题的过程来优化 日志记录的方式;

• 运维或测试在日志中发现的问题,都需要 及时向开发人员反映;

Rule 2:需要定期对日志内容进行优化更新, 目的就是通过日志快速准确的定位问题。

3. 关于日志分类

 日志从功能来说,可分为诊断日志、统计日

志、审计日志。

诊断日志, 典型的有:

• 请求入口和出口

• 外部服务调用和返回

• 资源消耗操作: 打开文件等

• 容错行为: 譬如云硬盘的副本修复操作 • 程序异常: 譬如数据库无法连接

• 后台操作:清理程序

• 启动、关闭、配置加载

• 抛出异常时,不记录日志

统计日志:

• 用户访问统计

• 计费日志(如记录用户使用的网络资源或 磁盘占用,格式较为严格,便于统计)。

审计日志:

• 管理操作

 将不同需求的日志记入到不同的日志文件

中,可以方便相关问题(管理平台操作审计,用

户操作计费等)的处理。针对每一种需求,需要

对日志的格式,日志记录的内容等进行特别的记

录。

投稿信箱:yhaunagnsgadi@an5@1c5t1oc.tco.mcom  31


“Fuse请求时,在Http头部加入 User-Agent 字段,当 NOS发现请求是 Fuse发过来的且为HeadObject操作且为 NoSuchKey错误时,则不打印错误日志。”

Rule 3:要明确不同日志的用途,对日志内容 进行分类

4. 日志中不要记录无用信息

在很多应用中,用户都需要通过Fuse方式来 挂载使用NOS。

POSIX标准中文件系统接口不允许文件 /a 与 目录 /a/ 同时存在,而NOS作为对象存储系 统,/a 和 /a/是不同的对象,是能够同时存在 的,一般地,NOS 中我们会规定 /a/ 是目录,/a 是文件,目录对象大小为0。

POSIX标准对文件的getattr操作,无论是 /a 还 是 /a/,对应的请求都是 /a。为了避免遗漏,需 分别向 NOS 请求 HeadObject(“/a“)和 HeadObject(“/a/“)。如果命中/a,说明 /a 是一 个文件,不用再请求 getattr(“/a/“)。

因此当用户访问 */a/b/c.txt* 时,实际上向 NOS发送了以下请求:

对于上面的请求,实际上HeadObject(“/a”) 和HeadObject(“/a/b”)都会返回NoSuchKey错 误,而Fuse正是该错误来判断该文件不存在,而 可能是个目录的。

然而对于NOS来说,这将导致产生大量无意 义的NoSuchKey日志(整个日志文件的80%都是 该错误日志)。这些日志对于开发人员进行日志 观察,运维人员定位问题,日志监控等都造成了 困难。

Rule 4: 绝不要打印没有用的日志,防止无用 日志淹没重要信息

解决办法:Fuse请求时,在Http头部加入 User-Agent 字段,当NOS发现请求是 Fuse发过来 的且为HeadObject操作且为NoSuchKey错误时, 则不打印错误日志。

5. 日志记录信息要完整

问题描述:

NOS提供分块上传的接口,用户可以通过以 下的调用序列,来实现一次分块上传的流程:

• InitMultiUpload(生成一个UploadID) • UploadPart

• UploadPart

• ......

• UploadPart

• CompleteMultiUpload(AbortMultiUpload)

之前在某个产品上线初期,由于其开发人员 对NOS的熟悉程度不够等原因。出现过如下问 题:客户端常常会收到NoSuchUpload的错误。该 错误 出现的原因是,用户在未调用 InitMultiUpload之前,或者在调用了 CompleteMult iUpload(AbortMultiUpload)之后再次调用 UploadPart。

然而当我们查日志,希望可以看到该 UploadPart请求对哪个UploadID进行操作,该 UploadID又对应哪些操作时,却发现我们的日志 中没有记录UploadPart请求对应的UploadID。

 类似的问题还有很多,很多针对特定请求的

日志缺失,导致很多问题无法定位。

 因此,需要进一步对日志中需要记录哪些内

容进行规定,此处推荐的需要在日志中记录的内

容有:

• 在系统启动或初始化时记录重要的系统初 始化参数

• 记录系统运行过程中的所有的错误

• 记录系统运行过程中的所有的警告

• 在持久化数据修改时记录修改前和修改后 的值

• 记录系统各主要模块之间的请求和响应( 如在NOS中的视频处理模块在接收到请求和发送 应答时,或者向客户端发送回调请求时)

# HeadObject(“/a”)

# HeadObject(“/a/”)

# HeadObject(“/a/b”)

# HeadObject(“/a/b/”)

# HeadObject(“/a/b/c.txt”)

32 观技察巧思经考验 LiLninuux运x运维维趋趋势势2021021年43年123月号 总第23245期


“在线上出现问题的时候,需要尽快发现问题并解 决,而同时,需要借此机会好好思考一下当前系统的 日志是否合理。”

• 重要的状态变化(如NOS中对系统白名单 的修改等)

• 系统中一些长期执行的任务的执行进度

 而不推荐记录日志的内容有:

• 函数入口信息 —— 除非该函数入口表示了 一个重要事件的开始,或者将该信息记入 DEBUG级别日志

• 文件内容或者一大段消息的内容 —— 如果 实在需要记录,则可以截取其中一些重要的信息 来记入日志

• “良性”错误 —— 有时候虽然出现了错 误,然而错误处理的流程可以正确解决这种情 况,例如插入数据库时有重复的记录,尽管是个 错误,然而错误处理流程可以对这种情况进行处 理

Rule 5:日志信息要准确全面,能做到仅凭日 志就可以定位问题

 解决办法:整理所有的请求处理流程,针对

每一个操作(去重,分块上传......)打印特定的

日志。

6. 测试的日志

 测试代码(单元测试,接口测试......)的日

志同样重要。特别是,当一个测试失败时,可以

通过日志很快确定是测试代码有问题,还是系统

出现了故障,如果做不到这一点,那就需要优化

测试的日志了。

测试日志应该包含以下内容: • 测试执行的环境

• 测试执行前的初始状态

• 测试的详细步骤

• 测试和系统的交互信息

• 测试期望的返回结果

• 测试实际的返回结果

Rule 6:要以严格的要求对待测试程序的日志

7. 从问题中完善日志

 在线上出现问题的时候,需要尽快发现问题

并解决,而同时,需要借此机会好好思考一下当

前系统的日志是否合理。需要考虑以下问题:

 如果定位问题花费了很长时间,那就说明系

统日志还存在问题,需要进一步完善和优化

 需要思考是否可以通过优化日志,来提前预

判该问题是否可能发生(如某种资源耗尽而导致

的错误,可以对资源的使用情况进行记录)

 通过系统出现的问题来优化日志,应该是一

项长期的实践,不断地从日志发现系统的问题,

不断地从系统异常发现日志的问题。

Rule 7:日志的优化是一件持续不断需要投入 精力的事,需要不断从错误中学习

8. 关于RequestID

RequestID的生成:

如今NOS有8台机器,共40个tomcat对外提供 服务。通常用户在请求出错的时候,我们都希望 用户告诉我们请求的RequestID,以此我们可以确 定请求是在哪台机器上进行处理的。

NOS通过以下信息生成一个请求的 RequestID:

• 收到请求的时间

• 处理请求的服务器ip地址 • 随机数

因此我们可以通过一个简单的程序从 RequestID中得到该请求的处理时间和处理请求的 服务器地址,更方便的去查看日志:

Rule 8:在RequestID中尽量编码更多的信息。

./decode.sh 4b2c009a0a7800000142789f42b8ca96 Thu Nov 21 11:06:12 CST 2013

10.120.202.150

4b2c009a

投稿信箱:yhaunagnsgadi@an5@1c5t1oc.tco.mcom  33


“慢日志的时间阀值应该是可以动态调整的,这样在 进行系统优化时,可以将该报警时间阀值逐渐调小,不 断地对系统进行优化。”

用RequestID将请求的处理流程关联起来:

在NOS性能测试中,之前存在的一个问题 是,由于在打印错误堆栈的地方,并没有打印请 求的RequestID,因此当一个请求出现错误时,很 难(日志量太大)将该请求的错误堆栈和具体的 请求关联起来。

另一个问题是,NOS后端有视频服务器集群 和图片处理服务器集群。因此我们可能会有以下 需求:当用户视频截图失败时,用户会告诉我们 请求的 RequestID,由于NOS并没有将该 RequestID转发到后端的图片处理服务器,因此无 法利用该信息去查看视频处理服务器上的日志, 而需要通过 用户请求的URL进行查找。同时,由 于我们无法知道该请求是在哪个具体的视频处理 的worker上进行,进一步导致查找日志的困难。

还有一个潜在的问题是:如果NOS将所有的 日志收集起来(tomcat,图片处理集群,视频处 理集群......),我们无法做到通过requestID来查 找一个请求的处理流程。

Rule 9:将一个请求的整个处理流程和唯一的 requestID关联起来

9. 关于线上机器的日志级别

问题描述:

NOS的DEBUG日志非常详细的记录了请求处 理相关信息,然而由于DEBUG日志量太大,因 此通常线上只开INFO级别日志。然而INFO级别 的 日志却有可能导致部分问题无法定位。NOS 线上一个请求可能随机地分发到4台机器进行处 理,因此如果某一种错误在一段时间内多次出 现,它也会在4台服务器 上都出现。

因此我们推荐的做法是,选择一台机器开启 DEBUG级别的日志,方便定位问题。其实该做 法背后的目的是,在线上任何问题的时候,都可 以通过日志最快的找到问题的根源。

Rule 10:让一台机器开启DEBUG日志

10. 上线后的日志观察

随着NOS开始服务越来越多的产品,NOS每 次版本升级之后,通过对日志的观察来确定服务 是否正常变得至关重要。

 同时在上线新功能时,来发人员需要通过观

察一些特定的日志,来确定新功能是否工作正

常。

举例来说:

NOS在实现了桶表缓存的功能之后,首先上 线一台服务器,并对该功能是否工作正常进行观 察。通过将桶缓存的所有操作(如插入,查找, 过期删除等)以 及桶缓存的状态(如缓存桶数 量)都记录在DEBUG级别的日志中。将新上线 的机器的日志级别调为DEBUG,并对桶缓存的 相关操作是否正确,缓存桶数量等 信息进行观 察,确认一切正常之后再上线其他机器。

Rule 11:新上线服务器后一定要对日志进行 观察,特别地,开发人员可以通过观察日志来确 认新功能是否工作正常

11. 慢操作日志

NOS在接收到一个请求的时候,会记录请求 的接收时间(T1),在请求处理完成待发送的时 候,会记录请求发送时间(T2),通常一个请求 的日志都记 为INFO级别,然而当出现请求处理 时间(T2-T1)超过一定时间(如10s)时,会将 该日志提升为WARN级别。通过该方法,可以预 先发现系统可能存 在的一些问题。

同样的慢操作日志还可以用来记录系统一些 外部依赖的处理时间,如NOS依赖外部认证服务 器来进行认证。我们会记录每个请求的认证时 间,如果认证时间超过某个值,也需要将该事件 的日志级别进行提升,这样我们可以尽早发现认 证服务器是不是需要扩容等问题。

 慢日志的时间阀值应该是可以动态调整的,

这样在进行系统优化时,可以将该报警时间阀值

逐渐调小,不断地对系统进行优化。

Rule 12:通过日志级别的提升来发现潜在问 题

12. 日志报警

错误日志报警:

NOS通过[运维平台|https://m.hz.netease.com/] 设置了日志监控报警,周期性的(1分钟,5分 钟)对服务器新产生的日志进行监控。

34 观专察题思专考栏 LiLninuux运x运维维趋趋势势2021021年43年123月号 总第23245期


“日志报警相当于grep操作,如果日志量过大,或 者匹配规则过多,可能对线上的服务产生影响。”

如果发现错误数超过某个阀值,则进行报 警。这类报警通常不一定是我们服务本身的问 题,也有可能是用户使用NOS不当造成的。

此处需要注意的问题是,日志报警相当于grep 操作,如果日志量过大,或者匹配规则过多,可 能对线上的服务产生影响。因此在设置好日志报 警后,需要周期性的关注每次日志扫描的时间, 评估日志监控是否对服务产生影响。

Rule 13:对日志进行监控报警,比客户先发 现系统问题

关键字报警:

NOS为每个用户分配了一定量的存储配额, 当用户容量超限时,会限制用户的上传操作。通 过在日志中记录关键字,如“Quota Warning” 等,可以及时提醒用户进行扩容,避免用户服务 中断。

类似的关键字报警还有很多:如对 InternalError的数量进行监控,对缓存的桶数量进 行监控等等。

Rule 14:通过日志中的关键字来确定系统的 运行状态

13. 关于日志格式

日志格式一定要统一,不能任由开发人员的 喜好来。举例来说,对于NOS视频截图超时的 ERROR日志,有以下几种方式打印:

第一种:

第二种:

第三种:

 第一种方式打印日志即是开发人员按照自己

的喜好来的,这种方法带来的问题是:

• 系统中日志格式不统一不利于自动化处理 • 有些日志可能只有开发人员自己才能看懂 • 代码规范性不好

 而第三种方式,通过一个函数来规范日志格

式,所有开发人员便可以通过该接口实现统一的

日志。

Rule 15:日志格式要统一规范

14. 错误日志输出到不同文件

在性能测试中遇到的另一个问题是,当并发 量很大时,可能会有一些请求处理失败(如0.5% ),为了对这些错误进行分析,需要去查这些错 误请求的日志。而由于这种情况下并发量很大, 使得对错误日志的分析变得困难。

 这种情况下可以将所有的错误日志同时输出

到一个单独的文件之中。

Rule 16:将错误日志输出到一个单独的文件 中进行分析

15. 关于日志文件大小

日志文件不宜过大,过大的日志文件对于日 志监控,问题定位等都会带来不便。因此需要进 行日志文件的切分,日志文件的切分可以通过 log4j等日志工具来配置,日志文件应该按天来分 割,还是按照小时来分割,应该根据日志量来决 定,原则就是方便开发或运维人员能快速查找日 志。

 为了防止日志文件将整个磁盘空间占满,需

要定期对日志文件进行删除。例如,在收到磁盘

报警时,可以将两个月以前的日志文件删除。此

处比较好的实践是:

• 将所有日志文件收集起来,这样即使在记 录日志的机器上删除,也可以通过收集的日志对 之前的问题进行定位;

• 每天通过定时任务来删除过期日志,如每 天在凌晨4点删除60天前的日志■(未完待续)

logger.error(“Gearman timeout exception for request ” + getRequestID() + ” value: ” + value, e);

logger.error(“RequestID: ” + getRequestID() + “, Error Message: Gearman timeout exception: ” + e);

logger.error(getErrorMessage(getRequestID(), getErrorMessage(), e));


转载于:https://my.oschina.net/HankCN/blog/221845

相关文章:

  • 移植svn trac 及trac错误解答
  • Extjs表单验证小结
  • 从ResultSet获取select count统计结果
  • 学会自重
  • 哈佛图书馆自习室墙上的训言
  • 深受打击-MDX这次更新没有了!
  • 什么是线程安全性
  • 网络边界---安全防护思想的演进
  • windows 如何查看端口占用情况?
  • 精妙的SQL语句
  • 谷歌jquery开发地址引用
  • 技术算几斤几两又值几个钱?
  • 轻量级实用JQuery表单验证插件:validateForm5
  • SCJP啊!
  • System Center 2012 R2实例1—构建自服务私有云5—SCO Runbook
  • 07.Android之多媒体问题
  • 2017前端实习生面试总结
  • exports和module.exports
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • PermissionScope Swift4 兼容问题
  • React Transition Group -- Transition 组件
  • React 快速上手 - 07 前端路由 react-router
  • Vim 折腾记
  • vue--为什么data属性必须是一个函数
  • 程序员该如何有效的找工作?
  • 记一次用 NodeJs 实现模拟登录的思路
  • 容器化应用: 在阿里云搭建多节点 Openshift 集群
  • 算法-插入排序
  • 物联网链路协议
  • 线性表及其算法(java实现)
  • 详解移动APP与web APP的区别
  • 想写好前端,先练好内功
  • 小而合理的前端理论:rscss和rsjs
  • 异常机制详解
  • 主流的CSS水平和垂直居中技术大全
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 浅谈sql中的in与not in,exists与not exists的区别
  • #1014 : Trie树
  • (10)ATF MMU转换表
  • (C#)获取字符编码的类
  • (二)Eureka服务搭建,服务注册,服务发现
  • (二)正点原子I.MX6ULL u-boot移植
  • (附源码)springboot课程在线考试系统 毕业设计 655127
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (强烈推荐)移动端音视频从零到上手(上)
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (数据结构)顺序表的定义
  • (五)Python 垃圾回收机制
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .Net Redis的秒杀Dome和异步执行
  • .net 简单实现MD5