日志用来记录用户操作、系统运行状态等,是一个系统的重要组成部分,然而由于日志不是核心功能,所以日志质量常常不被开发人员所重视。
尽管日志记录是必要的,但它对性能有明显的负面影响,如果不保持合理的简洁性,则会很快失去其有用性。每个领域在打印日志的时候都应该尽可能的合理,不要将别人的日志冲掉,就像你希望别人不冲掉你的日志一样。
【规则】根据实际情况正确的使用日志打印等级
说明: 日志级别要符合日志内容的实际级别,日志级别说明如下:
FATAL:重大致命异常,表明程序或功能即将崩溃,故障无法恢复。
ERROR:ERROR级别主要针对于一些不可预知的信息,诸如:错误、异常等,比如,在catch块中抓获的网络通信、数据库连接等异常,若异常对系统的整个流程影响不大,可以使用WARN级别日志输出。在输出ERROR级别的日志时,尽量多地输出方法入参数、方法执行过程中产生的对象等数据,在带有错误、异常对象的数据时,需要将该对象一并输出。
WARN:WARN级别的主要输出警告性质的内容,这些内容是可以预知且是有规划的,比如,某个方法入参为空或者该参数的值不满足运行该方法的条件时。在WARN级别的时应输出较为详尽的信息,以便于事后对日志进行分析。
输出控制宏 QT_NO_WARNING_OUTPUT
INFO:INFO级别的主要记录系统关键信息,旨在保留系统正常工作期间关键运行指标,开发人员可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录到INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现。建议在项目完成后,在测试环境将日志级别调成INFO,然后通过INFO级别的信息看看是否能了解这个应用的运用情况,如果出现问题后是否这些日志能否提供有用的排查问题的信息。
输出控制宏 QT_NO_INFO_OUTPUT
DEBUG:DEBUG级别的主要输出调试性质的内容,该级别日志主要用于在开发、测试阶段输出。该级别的日志应尽可能地详尽,开发人员可以将各类详细信息记录到DEBUG里,起到调试的作用,包括参数信息,调试细节信息,返回值信息等等,便于在开发、测试阶段出现问题或者异常时,对其进行分析。
输出控制宏 QT_NO_DEBUG_OUTPUT
3.1【规则】日志中禁止打印隐私信息
说明: 硬件序列号、个人账号、密码、身份等隐私信息禁止在日志中打印。隐私信息的范围遵从国家和地区的政策要求。
3.2【规则】日志中禁止打印其它与业务无关的信息
说明: 禁止打印如issue单号、需求单号、公司部门名称、开发人员自己的姓名、工号、名字缩写、当天的天气心情等任何与业务代码无关的信息。
3.3【规则】日志中禁止打印重复信息
说明: 禁止在不同的地方打印的内容完全一样的日志,在问题定位时无法准确找到代码位置。
3.4【规则】禁止在日志打印语句中调用业务接口函数
说明: 日志打印不应该对业务的正常流程产生任何影响,定位问题走读代码时通常会忽略日志代码对业务逻辑的影响。
3.5【规则】禁止将开发调试过程中使用的日志打印提交到代码仓中
说明: 为了定位问题可能会在代码中的每一步增加一行日志打印,或将各种变量内容打印出来(可能含用户隐私), 这些代码在提交到代码仓之前必须删除。
3.6【建议】日志打印内容尽量使用英文描述,单词拼写无误,符合语法规范,准确表述日志的含义
说明: 日志内容应该简明扼要地描述发生的事情,阅读日志的人可以通过日志直接知道表述的含义,尽量不要通过产生日志的代码才能知道;符合语法语义的日志打印也有利于后续日志分析工具对日志进行自动化解析。如:
“1234” 除了开发人员自己没人知道什么意义;
“Error happened” 哪里发生了什么错误,错误的原因值等都没有打印出来,不利于问题的定位分析。
3.7【建议】日志打印长度不要过长,尽可能使日志记录显示在一行以内
说明: 一行的长度通常是指在100个字符左右,尽量不要打印超过160个字符以上的日志。
3.8【建议】打印日志的代码不允许失败,阻断流程
说明: 一定要确保不会因为日志打印语句抛出异常造成业务流程中断。
3.9【建议】打印异常日志一定要输出全部错误信息
说明: 不要只有一个错误警告,一定要带上造成异常的所以参数变量值及状态信息,以帮助定位分析问题。
3.10【建议】不要打印无意义(无业务上下文、无关联日志链路id)的日志
说明:
public void doSth(){
log.info("do sth and print log");
// 业务逻辑
...
}
public void doSth(){
doIt1();
log.info("do sth 111");
doIt2();
log.info("do sth 222");
}
public void doSth(){
log.info("some body do sth, id:%d", id);
// 业务逻辑
...
}
3.11【建议】不要在循环中打印INFO级别日志
说明: 却需要打印,可降级为debug。或在在外部打印长度等关键状态信息。判断要点是频次和重要程度。
3.12【建议】不要打印重复的日志
反例:
public void doSth(String s){
log.info("do sth and print log: %s", s);
doStep1(s);
}
private void doStep1(String s){
log.info("do sth and print log: %s", s);
// 业务逻辑
doStep2(s);
...
}
private void doStep2(String s) {
log.info("do sth and print log: %s", s);
try {
doStep3(s);
} catch (Exception e){
log.error("something wrong", e);
}
}
private void doStep3(String s){
log.info("do sth and print log: %s", s);
try {
// 业务逻辑
} catch (Exception e){
log.error("something wrong", e);
throw e;
}
}
分析:
在每一个嵌套环节都打印了重复的日志。
不要记录日志后又抛出异常。抛出去的异常,一般外层会处理。如果不处理,那为什么还要抛出去?原则是,无论是否发生异常,都不要在不同地方重复记录针对同一事件的日志消息。
正例:
直接干掉或者将日志降级成debug级别日志
4.1【规则】高频代码的正常流程中禁止打印日志
说明: 如被高频调用的接口函数,大数据量处理的循环中,高频的软硬件中断处理中,协议数据流处理中,多媒体音视频流处理中,显示屏幕刷新处理中等等,这些地方基本都是只要系统不休眠就会一直运行的代码,这些代码正常处理禁止打印日志,但可在错误分支中打印日志。开发调试过程中在这里增加的日志在提交代码时必须清除掉或使用开关关闭。
4.2【规则】可能重复发生的日志需要进行频率限制
说明: 当事实证明某些日志记录可能会发生多次时,最好实施一种频率限制机制,防止出现具有相同(或非常相似)信息的大量重复日志副本。
4.3【规则】在基本不可能发生的点必须要打印日志
说明: 根据墨菲定律,只要有可能发生的是就一定会发生,一旦发生就是疑难杂症。
4.4【建议】日志字符串应在日志打印时再生成
说明: 日志字符串越迟生成越好,最好是在日志打印那一刻才生成,这样当日志被关闭时也就不会生成这个字符串,从而最大程度地减少对系统的开销。
5.1【规则】事件记录的日志使用who do what 主谓宾的形式打印
5.2【规则】状态变化的日志打印使用state_name:s1->s2, reason:msg的形式打印
5.3【规则】参数值的日志打印使用name1=value1, name2=value2…的形式打印
5.4【规则】代码运行成功的日志使用xxx successful的形式打印
5.5【规则】代码运行失败的日志使用xxx failed, please xxx的形式打印,且需包含可能的解决方案
如:"Connect to server failed, please check network configuration"。
【建议】日志中应当记录业务的关键流程节点日志,包括业务的开始点,关键条件分支节点,错误处理点,业务结束点等
【建议】日志中应当记录对数据库的各种操作及其相关信息
说明: 数据库的常规操作包括: 增、删、改、查;操作的发起者、操作类型、操作成功还是失败也要在日志中记录;但操作及返回结果中的内容通常不应记录以防止泄露用户隐私;查询结果的数量可以打印。
【建议】对于性能敏感型业务的数据库操作日志中需记录操作消耗的时间
说明: 数据库操作涉及I/O读写, 对于性能敏感性业务,数据库操作通常会是性能的关键节点,记录时间可以作为性能调优的参考依据。
【建议】日志中应当记录数据库的JOB相关信息
说明: JOB的名称,执行内容,启动时间,结束时间,执行结果应当在日志中记录。
【建议】日志中应当记录对文件的各种操作及其相关信息
说明: 文件的常规操作包括: 创建、打开、读取、写入、关闭、删除、获取属性,操作的发起者、操作类型、操作结果需要记录日志;但文件内容不可记录以防止泄露用户隐私或暴露系统安全漏洞,系统文件名可以打印,用户文件名不可以打印; 文件句柄值可以打印。
【建议】批量文件操作只打印一条日志而不是打印多条日志
说明: 如批量删除大量文件,不要每删除一个文件就打印一条日志,只要记录删除的文件数即可,如果文件所在目录是系统创建,还要打印目录名称。
说明: 关键对象/对象池可能是一个类或者一个结构体,也可能是一个队列或堆栈的数据结构,也可能是一个简单的原始类型变量,它处于系统的关键地位,系统的调度控制、状态记录、信息流转等动作都依赖它。
【建议】日志中应当记录关键对象的操作过程,操作结果,状态变化
说明: 对象的操作包括:创建、加载、卸载、释放等,对关键对象的操作需记录操作主体,操作类型,操作结果;状态类的需记录状态变化的前后值。
【建议】日志中需记录线程的各种操作及相关信息
说明: 线程的操作包括: 创建、启动、暂停、终止。日志中需记录操作线程的操作类型,操作结果、线程号,线程名称(重要线程一定要设置线程名)。
【建议】线程陷入死循环或死锁等错误时要记录日志
说明: 对于有等锁处理、异步处理、循环处理等逻辑的线程在线程发生死锁或死循环时要有检测机制,并在错误发生时打印日志。
【建议】消息处理型的线程需要打印消息处理相关的日志
说明: 包括消息名称、消息处理结果,消息处理时长;对于高频消息,只需要打印消息处理结果错误时的日志即可
并发控制日志
说明: 并发控制的对象可能是锁、临界区、信号量等。
【建议】日志中需记录并发控制对象的操作及其相关信息
说明: 并发控制的操作包括:创建、占用、释放、等待等。日志中需记录操作的类型,操作对象的名称、操作的结果、操作的位置等信息。
说明: 共享内存是软件系统中常用的进程间通信方法,它常用于在模块间共享数据或传递数据。共享内存所存放的数据可以是配置数据、数据库数据等。
【建议】日志中需记录对共享内存的操作及相关信息
说明: 对共享内存的操作包括:创建、删除、数据设置、数据查询、销毁等。日志中需记录对共享内存操作的操作者,操作类型、操作结果。
接口包括系统的内外部接口,内部接口指系统内部子系统、子模块之间的接口。形式可能包括模块间消息发送、IPC接口、RPC接口等。
【建议】日志需记录接口交互相关的信息
说明: 接口交互相关的信息包括:接口的调用者、消息内容(不能涉及用户隐私)、处理结果、返回值(不能涉及用户隐私)。
【建议】日志需记录状态机的操作及状态转换信息
说明: 状态机的操作包括:创建、开始、状态转换、结束、销毁等。状态机通常受外部条件激励(如消息、资源等)变换状态,状态机的状态变化前后的状态名称、导致变化的外部激励条件等信息也应该被记录。
这里说的主要操作系统资源指Socket、定时器等不在前面小节已专门提及的资源(如文件、线程等)
【建议】日志中需要记录socket连接建立的过程和结果、连接维持的情况、连接断开的情况及原因
【建议】日志中需要记录定时器启动、复位、销毁、超时处理过程
【建议】使用其它类似操作系统中提供的资源也应参照遵循类似上述的日志记录原则
**【建议】qml打印日志需要添加target,指明文件或功能模块 **
**【建议】target规范 **
说明: 分两级 framework.windowsmanager,系统里rsyslog会带上进程名,这可以认为是第一级。如果不复杂,只使用一级也可以。
当方法或者功能出现非正常逻辑执行情况时,需要打印WARN或者ERROR级别日志,那如何区分出现异常时打印WARN级别还是ERROR级别呢,我们可以从以下两个方面进行分析:
用户输入参数错误
非核心组件初始化失败
后端任务处理最终失败(如果有重试且重试成功,就不需要WARN)等
程序启动失败
核心组件初始化失败
连不上数据库
内存溢出
核心业务访问依赖的外部系统持续失败
Debug日志应该只在开发和测试阶段使用,如果在生产环境排查问题,检查信息也需要用到,就应该是Info日志。
WARN级别一般不会告警,ERROR级别则会设置监控告警甚至电话报警,ERROR级别日志的出现意味着系统中发生了非常严重的问题,必须有人立即处理。
错误的使用ERROR级别日志,不区分问题的重要程度,只要是问题就采用ERROR级别日志,这是极其不负责任的表现,因为大部分系统中的告警配置都是根据单位时间内ERROR级别日志出现的数量来定的,随意打ERROR日志将会造成极大的告警噪音,造成重要问题遗漏。
有的进程模块较多的,系统日志里仅打印一个进程名,不利于过滤日志。建议添加标签。
Q_LOGING_CATEGORY(cat, "cat")
qDebug(cat) <<"out put";
qCDebug(cat) <<"out put"; // 动态控制时效率更高,如果用QT_NO_DEBUG_OUTPUT宏控制就都一样。
Item {
LoggingCategory{
id: cat
name: "syberos.setting.net"
defaultLogLevel: LoggingCategory.Debug
}
Component.onCompleted: {
console.log(cat, "page init.")
}
}
可用通过宏添加标签,用syslog等打印,打印标准相同。
系统日志规范及最佳实践
https://zhuanlan.zhihu.com/p/643089256
OpenHarmony日志打印规范 https://www.seaxiang.com/blog/8232c5f6f9114c72905ec6c0f3ecaaac