应用日志规范（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-29T15：30：45，789
·日志链路id(traceld、rpcld）（可选）0b26053315407142451016402xxxxx0.3－///-
·日志级别：DEBUG，INFO，WARN，ERROR，FATAL
·线程名：SofaBizProcessor-4-thread-333，main
·类名或Logger名称：com.example.MyClass
·方法名与行号（可选）：·myMethod（L45）
·调用耗时（可选）1ms
·调用是否成功(Y/N）(可选)
·状态码（可选）SUCCESS
·系统上下文信息（调用系统名、调用系统ip、调用时间戳、是否压测（Y/N)）（可选）appName，ip地址，时间戳，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 log，data={}"，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(data，T
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：[}，开始处理会员逻辑",user，getUserId（））；
4
//会员逻辑
5
}else{
6
log.info（"该用户是非会员，Id：[}，开始处理非会员逻辑",user，getUserId（））
//非会员逻辑
9}
6.【推荐】宜打印必要的参数，不宜整个对象打印
反例：
1 public void doSth(）{
2
log.info("print log，data={}"，data.toString(）);
3
//业务逻辑
4
5}
注：首先分析下自己是否必须把所有对象里的字段打印出来？如果对象中有50个字段，但只需其中两个参数就可以定
位具体的原因，那么全量打印字段将浪费内容空间且因为字段过多，影响根因排查。
正例：
1 public void doSth(){
2
log.info("print log，id={}，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
不能实现日志按等级输出。具体来说就是不能和日志框架一样，有debug，info，error等级别的控制。
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
}catch（Exception 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}
