# HiLog打印规范

#### 简介

公共流水日志通过系统日志打印接口（HiLog）统一输出到日志服务。日志记录用户操作和程序运行状态，是系统的重要组成部分。虽然日志不是核心功能，但其质量常被开发人员忽视。错误的日志打印可能影响应用性能和稳定性。每个领域在打印日志时应尽量合理，避免覆盖其他领域的日志。

#### 日志级别

**【规则】**根据实际情况正确的使用日志打印等级。

说明：日志级别要符合日志内容的实际级别，可参考LogLevel，日志级别说明如下：

*   FATAL：重大致命异常，表明程序或功能即将崩溃，故障无法恢复。
*   ERROR：程序或功能发生了错误，该错误会影响功能的正常运行或用户的正常使用，可以恢复但恢复代价较高，如重置数据等。
*   WARN：发生了较为严重的非预期情况，但是对用户影响不大，程序可以自动恢复或通过简单的操作就可以恢复的问题。
*   INFO：用来记录业务关键流程节点，可以还原业务的主要运行过程；用来记录非正常情况信息，但这些情况都是可以预期的（如无网络信号、登录失败等）。这些日志都应该由该业务内处于支配地位的模块来记录，避免在多个被调用的模块或低级函数中重复记录。
*   DEBUG：比INFO级别更详细的流程记录，通过该级别的日志可以更详细地分析业务流程和定位分析问题。DEBUG级别的日志在正式发布版本中默认不会被打印，只有在调试版本或打开调试开关的情况下才会打印。

#### 日志内容

**【规则】**日志打印内容使用英文描述，单词拼写无误，符合语法规范，准确表述日志的含义。

说明：日志内容应该简明扼要地描述发生的事情，阅读日志的人可以通过日志直接知道表述的含义，尽量不要通过产生日志的代码才能知道；符合语法语义的日志打印也有利于后续日志分析工具对日志进行自动化解析。

如：“1234”除了开发自己没人知道代表什么意义;“Error happened”哪里发生了什么错误，错误的原因值等都没有打印出来，这样不利于问题的定位分析。

**【规则】**日志中禁止打印隐私信息。

说明：硬件序列号、个人账号、密码、身份等隐私信息禁止在日志中打印。隐私信息的范围遵从国家和地区的政策要求。

**【规则】**日志中禁止打印其它与业务无关的信息。

说明：禁止打印与业务无关的信息，如issue单号、需求单号、公司部门名称、开发人员的姓名、工号、名字缩写、个人心情、当天的天气等。

**【规则】**日志中禁止打印重复信息。

说明：禁止在不同的地方打印内容完全一样的日志，在问题定位时无法准确找到代码位置。

**【规则】**禁止在日志打印语句中调用业务接口函数。

说明：日志打印不应该对业务的正常流程产生任何影响，定位问题走读代码时通常要忽略日志代码对业务逻辑的影响。

**【规则】**禁止将开发调试过程中使用的日志打印提交到代码仓中。

说明：为了定位问题，可以在代码的每一步添加一行日志打印，或者输出各种变量的内容（可能包含用户隐私）。这些代码在提交到代码仓库之前必须删除。

**【建议】**日志打印长度不要过长，尽可能使日志记录显示在一行以内。

说明：一行的长度通常为100个字符左右，尽量避免打印超过160个字符的日志。

#### 打印时机

**【规则】**高频代码的正常流程中禁止打印日志。

说明：在高频调用的接口函数、大数据量处理的循环、高频软硬件中断处理、协议数据流处理、多媒体音视频流处理、显示屏幕刷新处理等场景中，这些代码通常会一直运行，只要系统不休眠。这些代码的正常处理逻辑中禁止打印日志，但可在错误分支中打印日志。开发调试过程中在此处增加的日志在提交代码时必须清除或使用开关关闭。

**【规则】**可能重复发生的日志需要进行频率限制。

说明：当事实证明日志记录可能重复出现时，应实施频率限制机制，防止生成大量重复或相似的日志。

**【规则】**在基本不可能发生的点必须要打印日志。

说明：根据墨菲定律，只要有可能发生的事就一定会发生，一旦发生就是疑难杂症。

**【建议】**日志字符串应在日志打印时再生成。

说明：日志字符串应在打印时生成，这样在日志关闭时不会产生额外开销。

【建议】日志打印耗时较长，需要提前校验日志打印是否满足日志级别。

说明：例如日志打印中存在函数入参: hilog.debug(0xd010, 'testTag', 'map info: %{public}s', mapToJson(this.paramsMap)), 这行日志打印代码中，需要先执行mapToJson(this.paramsMap)获取日志参数字符串，此函数可能有耗时操作，从而使hilog.debug接口耗时变长，如果确认日志打印中参数需要调用耗时函数接口，可以在日志打印前先使用hilog.isLoggable判断当前打印是否符合日志级别要求，如果不满足日志级别则不执行日志打印，例如：

```
if (hilog.isLoggable(0xd010, 'testTag', hilog.LogLevel.DEBUG)) {
  hilog.debug(0xd010, 'testTag', 'map info: %{public}s', mapToJson(this.paramsMap))
}
```

#### 日志形式

**【规则】**事件记录的日志使用who do what主谓宾的形式打印。

**【规则】**状态变化的日志打印使用state\_name:s1->s2, reason:msg的形式打印。

**【规则】**参数值的日志打印使用name1=value1, name2=value2…的形式打印。

**【规则】**代码运行成功的日志使用xxx successful的形式打印。

**【规则】**代码运行失败的日志使用xxx failed, please xxx的形式打印，且需包含可能的解决方案。

如："Connect to server failed, please check network configuration"。

#### 常见模式日志打印要求

#### 流程类日志

**【建议】**日志中应当记录业务的关键流程节点，包括业务的开始点，关键条件分支节点，错误处理点，业务结束点等。

#### 数据库类日志

**【建议】**日志中应当记录对数据库的各种操作及其相关信息。

说明：数据库的常规操作包括：增加、删除、修改和查询。日志中应记录操作的发起者、操作类型及操作是否成功。为了防止泄露用户隐私，不应记录操作和返回结果的具体内容。查询结果的数量可以打印。

**【建议】**对于性能敏感型业务的数据库操作日志需记录操作消耗的时间。

说明：数据库操作涉及I/O读写，对于性能敏感的业务，数据库操作是性能的关键节点。记录时间可作为性能调优的参考依据。

**【建议】**日志中应当记录数据库的JOB相关信息。

说明：日志应记录JOB的名称、执行内容、启动时间、结束时间和执行结果。

#### 文件类日志

**【建议】**日志中应当记录对文件的各种操作及其相关信息。

说明：文件的常规操作包括创建、打开、读取、写入、关闭、删除和获取属性。操作的发起者、操作类型和操作结果需要记录日志，但文件内容不可记录，以防止泄露用户隐私或暴露系统安全漏洞。系统文件名可以记录，用户文件名不可记录，文件句柄值可以记录。

**【建议】**批量文件操作只打印一条日志而不是打印多条日志。

说明：批量删除文件时，不要每删除一个文件就打印一条日志，只需记录删除的文件数量。如果文件所在目录是系统创建的，还需要打印目录名称。

#### 关键对象/对象池日志

关键对象/对象池可能是一个类或者一个结构体，也可能是一个队列或堆栈的数据结构，也可能是一个简单的原始类型变量，它处于系统的关键地位，系统的调度控制、状态记录、信息流转等动作都依赖它。

**【建议】**日志中应当记录关键对象的操作过程，操作结果，状态变化。

说明：对象的操作包括：创建、加载、卸载、释放等。对关键对象的操作需记录操作主体，操作类型，操作结果；状态类的对象需记录状态变化的前后值。

#### 线程日志

**【建议】**日志中需记录线程的各种操作及相关信息。

说明：线程操作包括创建、启动、暂停和终止。日志需记录操作类型、操作结果、线程号及线程名称（重要线程必须设置线程名）。

**【建议】**线程陷入死循环或死锁等错误时要记录日志。

说明：对于包含等锁处理、异步处理、循环处理等逻辑的线程，需在发生死锁或死循环时进行检测，并在错误发生时打印日志。

**【建议】**消息处理型的线程需要打印消息处理相关的日志。

说明：包括消息名称、处理结果和处理时长；对于高频消息，仅在处理结果错误时打印日志。

#### 并发控制日志

并发控制的对象可能是锁、临界区、信号量等。

**【建议】**日志中需记录并发控制对象的操作及其相关信息。

说明：并发控制的操作包括创建、占用、释放和等待。日志中需记录操作类型、操作对象名称、操作结果和操作位置。

#### 共享内存日志

共享内存是软件系统中常用的进程间通信方法，它常用于模块间共享数据或传递数据。共享内存所存放的数据可以是配置数据、数据库数据等。

**【建议】**日志中需记录对共享内存的操作及相关信息。

说明：对共享内存的操作包括创建、删除、数据设置、数据查询和销毁。日志需记录操作者、操作类型和操作结果。

#### 接口交互日志

接口包括系统的内外部接口，内部接口指系统内部子系统、子模块之间的接口。形式可能包括模块间消息发送、IPC接口、RPC接口等。

**【建议】**日志需记录接口交互相关的信息。

说明：接口交互信息包括：调用者、消息内容（不含用户隐私）、处理结果、返回值（不含用户隐私）。

#### 状态机日志

**【建议】**日志需记录状态机的操作及状态转换信息。

说明：状态机的操作包括创建、开始、状态转换、结束和销毁。状态机受外部条件（如消息、资源等）激励而变换状态，状态变化前后的名称及导致变化的外部激励条件应被记录。

#### 其它操作系统资源

这里说的主要操作系统资源指socket、定时器等未在前面小节已提及的资源（如文件、线程等）。

**【建议】**日志中需要记录socket连接建立的过程和结果、连接维持的情况、连接断开的情况及原因。

**【建议】**日志中需要记录定时器启动、复位、销毁、超时处理过程。

**【建议】**使用其他类似操作系统资源时，遵循上述日志记录原则。