Wyle.Gong-巩文昕 32676a314f init
2025-05-12 14:34:50 +08:00

392 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

应用日志规范Java)
1.技术要求
1.1记录原则
1.【强制】隔离性:日志输出不应影响系统正常运行;
2.【强制】安全性:日志打印本身不应存在逻辑异常或漏洞,导致产生安全问题;
3.【强制】数据安全不应输出机密、敏感信息如用户联系方式、身份证号码、token等应保证日志的完整性、
可审计性;
4.【强制】可监控分析:日志应提供给监控进行监控,分析系统进行分析;
5【强制】可定位排查日志信息输出应有意义应具有可读性可供开发人员排查线上问题。
1.2日志级别
在我们日常开发中有四种比较常见的日志打印等级,不同的等级适合在不同的时机下打印日志。
主要使用的有以下四个等级:
1.DEBUG
DEUBG级别应主要输出调试性质的内容该级别日志应用于并发、测试阶段输出。该级别的日志应尽可能地详尽
开发人员可以将各类详细信息记录到DEBUG里起到调试的作用包括参数信息调试细节信息返回值信息等
等,便于在开发、测试阶段出现问题或者异常时,对其进行分析。
2. INFO
INFO级别的主要记录系统关键信息旨在保留系统正常工作期间关键运行指标开发人员可以将初始化系统配置
业务状态变化信息或者用户业务流程中的核心处理记录到INFO日志中方便日常运维工作以及错误回溯时上下文
场景复现。应在项目完成后在测试环境将日志级别调成INFO然后通过INFO级别的信息看看是否能了解这个应用
的运用情况,如果出现问题后是否这些日志能否提供有用的排查问题的信息。
3.WARN
WARN级别的主要输出警告性质的内容这些内容是可预知且是有规划的比如某个方法入参为空或者该参数的值
不满足运行该方法的条件时。在WARN级别的时应输出较为详尽的信息以便于事后对日志进行分析。
常见的WARN级别异常
·用户输入参数错误
·非核心组件初始化失败
·后端任务处理最终失败如果有重试且重试成功就不需要WARN
·数据插入幂等
4.ERROR
ERROR级别主要针对于一些不可预知的信息诸如错误、异常等比如在catch块中抓获的网络通信、数据库连
接等异常若异常对系统的整个流程影响不大可使用WARN级别日志输出。在输出ERROR级别的日志时应尽量多
地输出方法入参数、方法执行过程中产生的对象等数据,在带有错误、异常对象的数据时,应将该对象一并输出。
常见的ERROR级别异常
·程序启动失败
·核心组件初始化失败
·连不上数据库
·核心业务访问依赖的外部系统持续失败
Woo·
不应滥用ERROR级别日志。一般来说在配置了告警的系统中WARN级别一般不会告警ERROR级别则会设置监控
告警甚至电话报警ERROR级别日志的出现意味着系统中发生了非常严重的问题应有人立即处理。
错误的使用ERROR级别日志不区分问题的重要程度只要是问题就采用ERROR级别日志这是极其不负责任的表
因为大部分系统中的告警配置都是根据单位时间内ERROR级别日志出现的数量来定的随意打ERROR日志会造
成极大的告警噪音,造成重要问题遗漏。
1.3日志格式
1.3.1后端日志格式
后端日志可以保存或输出到文件,远程服务器,控制台及数据库等,日志输出包含以下要素:
·时间戳2024-01-29T153045789
·日志链路id(traceld、rpcld可选0b26053315407142451016402xxxxx0.3///-
·日志级别DEBUGINFOWARNERRORFATAL
·线程名SofaBizProcessor-4-thread-333main
·类名或Logger名称com.example.MyClass
·方法名与行号可选·myMethodL45
·调用耗时可选1ms
·调用是否成功(Y/N(可选)
·状态码可选SUCCESS
·系统上下文信息调用系统名、调用系统ip、调用时间戳、是否压测Y/N)可选appNameip地址时间戳Y
·请求入参可选参数1
·请求出参可选参数1
·日志消息内容:"Thisis alog message""No URLs will be polled as dynamic
configurationsources"
日志自定义输出格式如下:
[SofaBizProcessor-4-thread-333] {com.example.MyClass}-L22-[(1ms,Y,SUCCESS)(appName,ip
地址时间戳Y)参数1,参数2)]-NoURLswillbe polled asdynamicconfiguration sources
[ob26053315407142451016402xXXxx0.3-///-]是traceld[INF0]是日志级别;
[SofaBizProcessor-4-thread-333]是线程名;{com.example.MyClass}是类名L22是行号No
URLswillbepolledasdynamicconfigurationsources是实际的日志消息具体的业务相关日志打
印形式可以根据业务实际情况自定义。
日志标准输出格式如下:
1 2024-01-29 15:30:45,789 [INF0] [main]{com.example.MyClass} ]-No URLs will be polled
as dynamicconfigurationsources
注:移动端日志遵循后端日志格式。
1.3.2前端日志格式
因素:
1.时间戳:在日志条目的开头添加一个时间戳,以便跟踪事件发生的时间。
2.日志级别:指定日志条目的级别(例如,错误、警告、信息、调试等),以便根据需要进行筛选和过滤。
3.源代码位置:在日志条目中包含源代码的位置信息(例如,文件名、行号等),以便快速定位问题。
4.消息内容:提供有关事件或错误的详细信息,包括任何相关的上下文或错误消息。
5.自定义字段根据需要添加具他自定义字段例如用户ID、会话ID、浏览器信息等。
以下是一个简单的前端日志格式示例:
1[2023-03-15 14:32:01] INF0: main.js:123 -User logged in successfully.
[2023-03-15 14:32:15] ERR0R: user.js:45-Failed to fetch user data. Error: Network
Error.码
在这个示例中,每条日志条目都包含时间戳、日志级别、源代码位置以及消息内容。此外,还可以根据需要添加其他
自定义字段例如用户ID、会话ID等。
需要注意的是,前端日志格式应该根据项目的具体需求和规范进行定制,以便更好地满足项目的需求。同时,还需要
考虑日志的存储、传输和展示等方面的问题,以确保日志的可用性和可维护性。
1.4日志存储
应及时清理和管理日志,以避免过大的日志文件或存储空间的浪费。及时地分析日志,以便发现潜在的问题或改进系
统性能。
志文件的重要程度、文件大小以及磁盘空间自行调整6个月内至少清理一次过期的或无用的日志数据此外还可
以对日志进行监控收到磁盘报警时对1个月之前的数据可以进行删除或者转储。
1.5日志安全
1.5.1保护范围
日志记录中包含的某些内容出于隐私保护、数据安全和法规遵从的要求,不应直接以原始形式显示。以下是一些需要
进行处理的内容类型:
1.法律法规不充许的信息
·《个人信息保护法》涉及的敏感信息:《个保法》第二十八条敏感个人信息是一旦泄露或者非法使用,容易导致
自然人的人格尊严受到侵害或者人身、财产安全受到危害的个人信息,包括生物识别、宗教信仰、特定身份、医
疗健康、金融账户、行踪轨迹等信息,以及不满十四周岁未成年人的个人信息。根据法规,个人可识别信息如身
份证号码、电话号码、住址、电子邮件地址等必须严格保护,未经同意不得非法收集、使用或披露。同时,个人
敏感数据如健康状况、医疗记录、生物识别信息等高度私密,处理时需特别谨慎,确保合法合规。
2.石油集团保密要求
·包括但不限于涉密技术资料、勘探数据、生产数据、商业合作细节等信息,信息系统日志中应避免直接记录这些
内容,并对所有操作进行严格的审计和安全控制,确保日志的安全存储、访问权限控制以及必要时的数据脱敏。
3.涉及的商业秘密信息
·在日志中,商业秘密信息可能以各种形式体现,例如研发数据、客户名单、内部策略、未公开的产品设计和技术
方案等。
4.业务定义敏感信息
·金融交易信息银行卡号、支付卡数据CVV码、有效期等是金融行业严控的信息必须采取加密或具他安全
措施妥善处理和存储。
致企业竞争优势受损或违反合同约定的信息。
5.可能有助于攻击者利用的敏感数据
·用户的会话lD如果需要跟踪会话相关的事件可以考虑用Hash值代替
·会话ID与Hash值为了防止会话劫持原始会话ID不应在日志中明文记录可以考虑采用哈希值代替但要确保
该哈希不能被反向解析为原始会话标识。
此建议不在日志中详细记录或仅记录到主版本级别。
·散列值:如果散列为敏感数据的散列,例如密码散列,在记录日志时应当谨慎,避免将过于详细的散列值存入日
志,特别是对于易破解的弱散列算法。
式去标识化。
·数据库连接字符串与密钥:包含数据库凭据的连接字符串、加密密钥和其他主密钥绝对不能出现在日志中,这些
信息一旦泄露可能导致数据大规模泄露或系统完全失控。
1.5.2脱敏方法
不应在日志中明文记录敏感信息数据,必要时对这些数据进行脱敏处理。对敏感的日志信息进行加密,以保护数据安
全。确保在存储和传输过程中对敏感信息进行加密,以防止未授权的访问和泄露。
需要脱敏的信息见”保护范围“部分。
注:在进行日志脱敏时,会根据不同的敏感级别采用不同的脱敏策略,包括替换、遮盖、截断、哈希加密等方法,以
保护这些信息不被泄露,同时确保日志能够满足必要的业务分析和故障排查要求
具体方式包括但不限于:
·移除Remove直接从日志条目中删除敏感字段。
·分类与分级处理:根据数据的敏感程度进行分类,并针对不同类别实施不同的脱敏策略,例如部分脱敏或完全替
换。
·脱敏Masking):替换敏感字段为星号、占位符或其他非敏感字符,如将电话号部分数字脱敏为“******
1234”。
·净化Sanitizing):对敏感数据进行格式化或规范化,确保不泄露实际意义,例如使用哈希函数处理用户身份信
息。
·Hash化对于需要保持唯一性的数据比如用户名或电子邮件地址可以采用不可逆的哈希算法进行处理用于
审计目的但不能反向还原出原始数据。
实施上述措施有助于企业在满足日志分析和故障排查需求的同时,最大限度地降低数据泄露风险,并符合《网络安全
在使用日志框架如logback、Log4j等时可以配置相应的插件或者自定义拦截器来实现自动化脱敏功能。
1.5.3安全措施
日志数据安全是确保系统在生成、传输、存储日志时,保护敏感信息不被未经授权的访问、泄露、篡改或丢失的重要
环节。以下是维护日志数据安全的一些关键措施:
1.日志数据传输加密:
·在传输过程中可使用SSL/TLS等安全协议加密SYSLOG或具他形式的日志数据流。
2.访问控制:
·实施严格的访问控制策略,只有授权人员才能查看和操作日志数据,
3.敏感信息处理:
·对于包含敏感信息的日志条目执行脱敏处理如替换或模糊化密码、个人识别信息PII)、信用卡号等敏感内
容。
4.标准化与合规:
·确保日志格式和实践符合行业标准和法规要求,比如《网络安全法》和《网络安全等级保护基本要求》对于数据
处理的规定。
5.备份与恢复:
·定期备份日志,并保证备份数据的安全性,同时设计有效的灾难恢复方案。
通过上述措施,可从多个维度保障日志数据的安全性,同时也为数据分析、故障排查、安全审计及合规性检查提供了
有力支持。
1.6Java最佳实践
1.【推荐】日志语言应使用英文
注:应在打印日志时输出英文,防止中文编码与终端不一致导致打印出现乱码的情况,对故障定位和排查存在一定的
干扰。如果日志中的错误信息用英文描述不清楚可使用中文描述,否则容易产生歧义。国际化团队或海外部署的服务
器由于字符集问题uing用英文来注释和描述日志错误信息。
2.【推荐】日志打印时不宜直接用JSON工具将对象转换成String
反例:
1 public void doSth(){
2
log.info("do sth and print logdata={}"JsoN.toJsoNstring(data));
3
//业务逻辑
4
51
注:
·fastison等序列化组件是通过调用对象的get方法将对象进行序列化如果对象里某些get方法被覆写存在抛出异
常的情况,则可能会因为打印日志而影响正常业务流程的执行。
打日志过程中对一些对象的序列化过程也是比较耗性能的。首先序列化过程本身时一个计算密集型过程,浪费
cpu。其次这个过程会产生很多中间对象对内存也不友好。
正例:
可以使用对象的toString(方法打印对象信息如果代码中没有对toString有定制化逻辑的话可以使用apache的
ToStringBulider工具。
1public void doSth(){
2
log.info("do sth and print log, data={}", data.toString());
3
log.info("do sth and print log data=[}" ToStringBuilder.reflectionToString(dataT
4
3.
【强制】不应打印无意义无业务上下文、无关联日志链路id的日志
反例:
不带任何业务信息的日志,对排查故障毫无意义。
1public void doSth({
2
log.info("do sth and print log");
3
//业务逻辑
4
5}
对于无异常分支的代码打印日志,一般流程下,异常分支都会打日志,如果没有出现异常,那就正常执行了。
1public void doSth({
2
doIt1();
3
log.info("do sth 1l1");
4
doIt2();
5
log.info("do sth 222");
6}
正例:
·日志应带相关的业务信息,有利于排查问题快速定位到原因。
1public void doSth({
2
log.info("do sth and print log id=[}"id);
3
//业务逻辑
4
+
【强制】不应在循环中打印非DEBUG级别的日志
反例:
1 public void doSth(){
2
for(String s:strList){
3
log.info("do sth and print log: {}"s);
4
//业务逻辑
5
6
7
5.【推荐】在核心业务逻辑中遇到if..else等条件每个分支首行都应打印日志
在编写核心业务逻辑代码时如遇到if..else...或者switch这样的条件可以在分支的首行就打印日志这样排查问题
时,就可以通过日志,确定进入了哪个分支,代码逻辑更清晰,也更方便排查问题。
正例:
1 public void doSth(){
2
if(user.isVip()){
3
log.info"该用户是会员Id[},开始处理会员逻辑",usergetUserId
4
//会员逻辑
5
}else{
6
log.info"该用户是非会员Id[},开始处理非会员逻辑",usergetUserId
//非会员逻辑
9}
6.【推荐】宜打印必要的参数,不宜整个对象打印
反例:
1 public void doSth({
2
log.info("print logdata={}"data.toString();
3
//业务逻辑
4
5}
首先分析下自己是否必须把所有对象里的字段打印出来如果对象中有50个字段但只需其中两个参数就可以定
位具体的原因,那么全量打印字段将浪费内容空间且因为字段过多,影响根因排查。
正例:
1 public void doSth(){
2
log.info("print logid={}type={}"data.getId()data.getType());
3
//业务逻辑
4
5}
使用这个种方法需及时防止npe并考虑是否核心场景核心场景建议还是打全避免漏打、少打影响线上问题
定位&排查。
7.【强制】在接口/方法的入口/出口处,打印请求及响应参数日志。
8
【推荐】日志内容中不应仅打印特殊字符或数字。
9.
【推荐】日志内容中应包含关键特征类信息,例如:用户标识或流水号。
10.
【强制】日志单行大小应不超过200K
11【强制】打印日志的代码不应失败阻断流程
一定要确保不会因为日志打印语句抛出异常造成业务流程中断如下所示shop为null的会导致抛出NPE。
1public void doSth(){
2
log.info("do sth and print log: [}"shop.getId());
3
//业务逻辑
4
5}
12.【强制】不应使用System.out.println(输出日志
反例:
1public void doSth(){
2
System.out.println("doSth...");
3
//业务逻辑
4
5}
通过分析System.out.println源码可知System.out.println是一个同步方法在高并发的情况下大量执行
println方法会严重影响性能。
1 public void println(String x){
2
synchronized this[
3
print(x) ;
4
newLine();
5
6
不能实现日志按等级输出。具体来说就是不能和日志框架一样有debuginfoerror等级别的控制。
System.out、System.error打印的日志并没有打印在日志文件中而是直接打印在终端无法对日志进行收集。
正例:
在日常开发或者调试的过程中应使用标准日志记录系统log4j2或者logback但不应直接使用其中的APl),异步的进
行日志统一收集。
1 public void doSth(){
2
log.info("doSth...") ;
3
//业务逻辑
13.【强制】对于debug/info级别的日志输出应进行日志级别的开关判断
反例:
1 public void doSth(){
2
String name = "xxx";
3
logger.debug("print debug log"+ name);
4
logger.info("print infolog"+ name);
5
//业务逻辑
6
7}
注:
如果配置的日志级别是warn的话上述日志不会打印但是会执行字符串拼接操作如果name是对象还会执行
toString方法浪费了系统资源执行了上述操作最终日志却没有打印因此建议加日志开关判断。
正例:
在debug、info级别日志打印前加上对应级别的日志开关判断通常可以将开关判断逻辑包装在日志工具类中统一
实现。
1 public void doSth(){
2
if(logger.isDebugEnabled(){
3
logger.debug("print debug log {}"name);
4
5
if(logger.isInfoEnabled(){
6
logger.info("print info log [}"name;
7
}
8
//业务逻辑
9
10}
14.
【强制】打印异常日志应要输出全部错误信息
反例:
没有打印异常e无法定位出现什么类型的异常
1 public void doSth(){
2
tryf
3
//业务逻辑
4
5
}catchException e{
6
log.error("execute failed");
7
8}
没有记录详细的堆栈异常信息,只记录错误基本描述信息,不利于排查问题。
1 public void doSth(){
2
tryf
3
//业务逻辑
4
5
}catch (Exception e){
6
log.error("execute failed",e.getMessage());
7
8
正例:
一般日志框架中的warn、error级别均有存在传递Throwable异常类型的APl可以直接将抛出的异常传入日志APl中。
1void error(String varl Throwable var2);
2 public void doSth(){
3
tryf
4
//业务逻辑
5
6
}catch Exception e){
7
log.error("executefailed"e);
8
9}