写点什么

服务日志规范

作者:en
  • 2022 年 5 月 15 日
  • 本文字数:1778 字

    阅读完需:约 6 分钟

服务日志规范

一.背景介绍

为什么我们需要日志?

用于在服务出错的时候更加快速的定位到错误点,进行分析解决

为什么打印日志还需要遵循一定的规范标准

  • 如果日志打印过少甚至不打,那么服务出错的时候就很难通过日志上下文定位具体的错误位置进行处理

  • 如果日志打印随意,每一个地方都打印日志,会对服务的性能有所损失,且大量无用日志会占用较多存储,导致难以长时间的保留有用信息

  • 如果日志级别不规范,比如随便出问题都打印 error 日志,就会导致日志信息噪音过多,出了一个小错误就有几百条错误日志,那就等于没有日志

综上,一个优秀的日志规范,应当兼顾查错和日志量级,在能够帮助我们快速定位错误的同时不对系统性能和存储造成过多的压力。

本文目标

已经有很多打印 log 的第三方包供我们使用,他们会保证我们的日志以结构化(json)的方式打印,我也认同结构化的日志是最优的日志组织结构,作为业务层面的研发,我会更关注在什么时候应当打日志,应当在什么场景打什么级别的日志

最终的目标是在能够通过日志轻松排查错误的前提下尽可能减少日志带来的性能损耗。

二.规范的日志标准

如果让我来设计日志标准,我会如何设计?

2.1 尽可能少的打印日志

这个准则可能看起来和我写的文章背道而驰,我们不是在讨论如何打日志吗,为什么变成了尽可能少的打印日志呢?

因为日志本身也是有所损耗的,我们需要写日志的代码,它会影响系统性能,需要存储它,且它是无关业务的一部分逻辑(再不出问题的前提下,出现问题当然需要它探查),所以我们应当尽可能少的去打印日志。

在这里可以参考的一个准则是--如果我们在查看日志时无法确定确切的原因或场景,请不要记录(if we can’t pin down an exact reason or a scenario when we will look at the log, don’t log)

2.2 不需要太多的日志级别

trace,debug,info,warn,error,fatal;日志的级别有很多类,这里不是说他们中的某项没有用,而是针对一个业务研发而言,如果要我在写代码的同时还要思考这个地方到底是 error 还是 fatal,是 info 还是 debug,那还不如不打,所以我倾向于缩减日志级别类别,将类别限制为 3 个即可:info,warn,error,减少程序员在打印日志时候的思考。

ERROR

影响到程序正常运行、当前请求正常运行的异常情况:

  1. 打开配置文件失败

  2. 所有第三方对接的异常(包括第三方返回错误码)

  3. 所有影响功能使用的异常,包括:SQLException 和除了业务异常之外的所有异常(RuntimeException 和 Exception)

  4. 不应该出现的情况: 比如要使用云存储传图片,但是云存储未响应

WARN

不应该出现但是不影响程序、当前请求正常运行的异常情况:

  1. 有容错机制的时候出现的错误情况

  2. 找不到配置文件,但是系统能自动创建配置文件

  3. 即将接近临界值的时候,例如:缓存池占用达到警告线。

  4. 业务异常的记录,比如: 当接口抛出业务异常时,应该记录此异常。

INFO

系统运行信息

  1. Service 方法中对于系统/业务状态的变更

  2. 主要逻辑中的分步骤

外部接口部分

  1. 客户端请求参数(REST/WS)

  2. 调用第三方时的调用参数和调用结果

针对 warn 和 error 感觉不需要赘述太多,针对于 info 需要额外说一下,根据我们如果我们在查看日志时无法确定确切的原因或场景,请不要记录”的准则,info 更多的可以是一种业务数据需求的补充,除非有需求说要看这部分数据,否则可以不打。

2.3 避免层层打印

代码中经常会有 funcA->funcB->funcC 这样的调用方式,如果 funcC 出现 error,打印 error 日志,返回 funcB,再打印错误原因,同上到 funcA,等于一个错误打印了三遍日志,完全没有必要。

我们可以封装错误日志的上下文,将错误日志只在第一层 A 打印,不过打印出来得带上 b,c 信息

funcA -> funcB -> funcC :errormsg:%s  id:%s 
复制代码

id 的作用是确定那一条数据出现的问题

2.4 打印日志信息要完善

以 2.3 为例子,假设打印的日志内容是查询用户 a,a not found,如果内容是

funcA -> funcB -> funcC :errormsg:sql not found
复制代码

其实就跟没打印一样,所以打印的每条日志我们要确定内容完备

funcA -> funcB -> funcC :{"ErrMsg":"sql not found","ErrData":{"id":1}}
复制代码


参考内容

https://stackify.com/what-is-structured-logging-and-why-developers-need-it/

https://www.sumologic.com/glossary/structured-logging/

https://www.isolves.com/it/cxkf/bk/2020-06-05/19563.html

https://medium.com/unomaly/logging-wisdom-how-to-log-5a19145e35ec

https://www.freecodecamp.org/news/how-to-use-logs-effectively-in-your-code/


用户头像

en

关注

努力分享对他人有价值的知识 2018.06.14 加入

热爱技术,欢迎探讨

评论

发布
暂无评论
服务日志规范_日志_en_InfoQ写作社区