日志打印最佳实践
标题为什么要记录日志?
打印调试:用日志来记录变量或者某一段逻辑,记录程序运行的流程,即程序运行了哪些代码,方便排查逻辑问题。
问题定位:程序出异常或者出故障时快速的定位问题,方便后期解决问题。因为线上生产环境无法debug,在测试环境去模拟一套生产环境费时费力。所以依靠日志记录的信息定位问题,这点非常重要。
监控告警 & 用户行为审计:格式化后日志可以通过相关监控系统(AntMonitor)配置多维度的监控视图,让我们可以掌握系统运行情况或者记录用户的操作行为并对日志采集分析,用于建设业务大盘使用
什么时候记录日志?
代码初始化时或进入逻辑入口时 :系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录INFO日志,打印出参数以及启动完成态服务表述。
编程语言提示异常:这类捕获的异常是系统告知开发人员需要加以关注的,是质量非常高的报错。应当适当记录日志,根据实际结合业务的情况使用WARN或者ERROR级别。
业务流程预期不符:项目代码中结果与期望不符时也是日志场景之一,简单来说所有流程分支都可以加入考虑。取决于开发人员判断能否容忍情形发生。常见的合适场景包括外部参数不正确,数据处理问题导致返回码不在合理范围内等等。
系统/业务核心逻辑的关键动作:系统中核心角色触发的业务动作是需要多加关注的,是衡量系统正常运行的重要指标,建议记录INFO级别日志。
第三方服务远程调用:微服务架构体系中有一个重要的点就是第三方永远不可信,对于第三方服务远程调用建议打印请求和响应的参数,方便在和各个终端定位问题,不会因为第三方服务日志的缺失变得手足无措。
日志格式一般需包含以下几类关键信息:
调用时间
日志链路id(traceId、rpcId)
线程名
接口名
方法名
调用耗时
调用是否成功(Y/N)
错误码
系统上下文信息(调用系统名、调用系统ip、调用时间戳、是否压测(Y/N))
2022-12-12 06:05:05,129 [0b26053315407142451016402xxxxx 0.3 - /// - ] INFO [SofaBizProcessor-4-thread-333] - [(interfaceName,methodName,1ms,Y,SUCCESS)(appName,ip地址,时间戳,Y)
详细日志
详细日志是用于补充摘要日志中的一些业务参数的日志文件,用于问题排查。详细日志一般包含以下几类信息:
调用时间
日志链路id(traceId、rpcId)
线程名
接口名
方法名
调用耗时
调用是否成功(Y/N)
系统上下文信息(调用系统名、调用系统ip、调用时间戳、是否压测(Y/N))
请求入参
请求出参
2022-12-12 06:05:05,129 [0b26053315407142451016402xxxxx 0.3 - /// - ] INFO [SofaBizProcessor-4-thread-333] - [(interfaceName,methodName,1ms,Y,SUCCESS)(appName,ip地址,时间戳,Y)(参数1,参数2)(xxxx)
日志记录原则
隔离性:日志输出不能影响系统正常运行;
安全性:日志打印本身不能存在逻辑异常或漏洞,导致产生安全问题;
数据安全:不允许输出机密、敏感信息,如用户联系方式、身份证号码、token等;
可监控分析:日志可以提供给监控进行监控,分析系统进行分析;
可定位排查:日志信息输出需有意义,需具有可读性,可供日常开发同学排查线上问题。
打印日志要点
1.防止NPE
2.禁止使用 System.out.println()输出日志
原因: 此打印日志为控制台日志 , 并且是同步方法,高并发情况会影响性能 , 而且无法对日志统一收集
3.应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架 (SLF4J、JCL–Jakarta Commons Logging)中的API。
原因: 日志系统需要与代码解耦
4.声明日志工具对象Logger应声明为private static final
原因: private 防止日志对象被其它类非法使用
static 防止重复new日志对象,防止被序列化 , 减少安全风险
final 在类的生命周期无需变更logger,避免程序运行期对logger进行修改。
5.捕获异常后不要使用e.printStackTrace()打印日志
原因: e.printStackTrace()语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,系统请求将被阻塞。
6.不要打印无意义(无业务上下文、无关联日志链路id)的日志
7.日志打印禁止使用JSON打印对象 , 应使用 toString() 方法或者apache的ToStringBulider
8.禁止打印敏感信息
9.日志打印尽量使用英文
原因: 尽量在打印日志时输出英文,防止中文编码与终端不一致导致打印出现乱码的情况,对故障定位和排查存在一定的干扰。
10.调用第三方接口一定需要打印出入参