后端日志规范

目录

1.日志级别

2.日志要打印出方法的入参、出参

3. 使用统一的日志格式

4.不能直接使用日志系统(Log4j、Logback)中的 API,而是使用日志框架SLF4J中的API。

5. 使用参数占位{},而不是用+拼接。

6. 建议使用异步的方式来输出日志。

7.不要使用e.printStackTrace(),异常日志不要只打一半,要输出全部错误信息

8.禁止在线上环境开启 debug日志

9.不要记录了异常,又抛出异常

10.避免重复打印日志

11.核心功能模块,打印较完整的日志

12.基础包用打印error日志代替抛出异常

13.线上日志要使用英文描述

14.测试环境可以使用debug级别打印日志

15.需要打印日志的场景(待补充)


1.日志级别

常见的日志级别有5种,分别是error、warn、info、debug、trace。日常开发中,我们需要选择恰当的日志级别
error:错误日志,指比较严重的错误,对正常业务有影响,需要运维配置监控的;
warn:警告日志,一般的错误,对业务影响不大,但是需要开发关注;
info:信息日志,记录排查问题的关键信息,如调用时间、出参入参等等;
debug:用于开发DEBUG的,关键逻辑里面的运行时数据;
trace:最详细的信息,一般这些信息只记录到日志文件中。

2.日志要打印出方法的入参、出参

我们并不需要打印很多很多日志,只需要打印可以快速定位问题的有效日志。
哪些算得的上有效关键的日志呢?比如说,方法进来的时候,打印入参。再然后呢,在方法返回的时候,就是打印出参,返回值。入参的话,一般就是userId或者bizSeq这些关键信息。正例如下:

public String testLogMethod(Document doc, Mode mode){ 
	log.debug(“method enter param:{}”,userId); 
	String id = "666"; 
	log.debug(“method exit param:{}”,id); 
	return id; 
}

3. 使用统一的日志格式

必须使用logback-spring.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_PATH" value="${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH}spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%i</fileNamePattern>
        </rollingPolicy>
        <triggeringPolicy
            class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>
    <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
        <File>${LOG_PATH}error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>10</maxHistory>            
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter> 
    </appender>
    <!-- 异步输出 -->
    <appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold >0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>512</queueSize>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref ="FILE"/>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
        <appender-ref ref="FILE_ERROR" />
    </root>
</configuration>

4.不能直接使用日志系统(Log4j、Logback)中的 API,而是使用日志框架SLF4J中的API。

SLF4J 是门面模式的日志框架,有利于维护和各个类的日志处理方式统一,并且可以在保证不修改代码的情况下,很方便的实现底层日志框架的更换。

import org.slf4j.Logger; 
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(TianLuoBoy.class);

5. 使用参数占位{},而不是用+拼接。

反例:

logger.info("Processing trade with id: " + id + " and symbol: " + symbol);

上面的例子中,使用+操作符进行字符串的拼接,有一定的性能损耗。

正例如下:

logger.info("Processing trade with id: {} and symbol : {} ", id, symbol);

我们使用了大括号{}来作为日志中的占位符,比于使用+操作符,更加优雅简洁。并且,相对于反例,使用占位符仅是替换动作,可以有效提升性能。

6. 建议使用异步的方式来输出日志。

日志最终会输出到文件或者其它输出流中的,IO性能会有要求的。如果异步,就可以显著提升IO性能。
以logback为例吧,要配置异步很简单,使用AsyncAppender就行

<!-- 异步输出 -->
<appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
    <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
    <discardingThreshold >0</discardingThreshold>
    <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
    <queueSize>512</queueSize>
    <!-- 添加附加的appender,最多只能添加一个 -->
    <appender-ref ref ="FILE"/>
</appender>

7.不要使用e.printStackTrace(),异常日志不要只打一半,要输出全部错误信息

反例:

try{
 	// 业务代码处理 
} catch(Exception e){
  e.printStackTrace(); 
}

try { 
//业务代码处理 
} catch (Exception e) { 
// 错误 
LOG.error('你的程序有异常啦'); 
}

try { 
	//业务代码处理 
} catch (Exception e) { 
	// 错误 
	LOG.error('你的程序有异常啦', e.getMessage()); 
}

正例:

try{ 
	// 业务代码处理 
} catch(Exception e){ 
	log.error("你的程序有异常啦",e); 
}

理由:

e.printStackTrace()打印出的堆栈日志跟业务代码日志是交错混合在一起的,通常排查异常日志不太方便。
e.printStackTrace()语句产生的字符串记录的是堆栈信息,如果信息太长太多,字符串常量池所在的内存块没有空间了,即内存满了,那么,用户的请求就卡住啦~

8.禁止在线上环境开启 debug日志

因为一般系统的debug日志会很多,并且各种框架中也大量使用 debug的日志,线上开启debug不久可能会打满磁盘,影响业务系统的正常运行。

9.不要记录了异常,又抛出异常

反例如下:

log.error("IO exception", e);
throw new MyException(e);

这样实现的话,通常会把栈信息打印两次。这是因为捕获了MyException异常的地方,还会再打印一次。
这样的日志记录,或者包装后再抛出去,不要同时使用!否则你的日志看起来会让人很迷惑。

10.避免重复打印日志

重复打印日志会浪费磁盘空间。如果你已经有一行日志清楚表达了意思,避免再冗余打印,反例如下:

if(user.isVip()){ 
	log.info("该用户是会员,Id:{}",user,getUserId()); 
	//冗余,可以跟前面的日志合并一起 
	log.info("开始处理会员逻辑,id:{}",user,getUserId()); 
	//会员逻辑 
}else{ 
	//非会员逻辑 
}

如果你是使用log4j日志框架,务必在log4j.xml中设置 additivity=false,因为可以避免重复打印日志
正例:

<logger name="com.taobao.dubbo.config" additivity="false"> 

11.核心功能模块,打印较完整的日志

我们日常开发中,如果核心或者逻辑复杂的代码,建议添加详细的注释,以及较详细的日志。
日志要多详细呢?脑洞一下,如果你的核心程序哪一步出错了,通过日志可以定位到,那就可以啦。

12.基础包用打印error日志代替抛出异常

13.线上日志要使用英文描述

14.测试环境可以使用debug级别打印日志

15.需要打印日志的场景(待补充)

  • 系统初始化流程
  • 编程语言提示异常
  • 业务流程预期不符
  • 系统核心角色,组件关键动作
  • 作为服务提供方(打印入参、出参)
  • 作为服务调用方(打印入参、返参)
  • 定时任务开始记录
  • 定时任务运行相关记录
  • 定时任务结束时成功或失败记录
  • 大批量数据执行进度
  • 关键变量以及正在做的重要事情
  • 异步任务开始执行记录
  • 异步任务关键流程和重要参数日志
  • 程序中重要的状态信息的变化
  • else逻辑要打印关键参数日志,要知道为什么走到else逻辑
  • 在启动应用或启动相应配置时打印日志
  • 配置信息变更日志
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jet-W

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值