使用log4j2 打印日志,对一些敏感数据进行脱敏。【通过配置选择哪个日志生效】
一、常见日志框架
日志框架采用外观模式来实现,给系统提供门面,不暴露具体实现细节。
1、外观模式
使用者只需和外观角色打交道就行

2、门面
2.1 slf4j
SLF4J是一款Java程序编写的日志门面框架,其本身定义了统一的日志接口,且对不同的日志实现框架进行抽象化,我们的应用只需要跟SLF4J进行沟通
在引入包的时候,需要同时引入slf4j.jar,日志实现.jar,以及对应的适配包.jar
日志使用中都是引入slf4j包里面的
1
2
3
4
|
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final static Logger logger = LoggerFactory.getLogger(SLF4JandLog4j.class);
|
在执行的时候选择具体的实现系统
Slf4j实现机制: Slf4j在编译期间,静态绑定本地的LOG库,因此可以在OSGi中正常使用。它是通过查找类路径下org.slf4j.impl.StaticLoggerBinder,然后绑定工作都在这类里面进。
2.2 Commons Logging
是一个基于Java的日志记录实用程序,是用于日志记录和其他工具包的编程模型。它通过其他一些工具提供API,日志实现和包装器实现。Commons Loggin自动搜索并使用Log4j(Log4j是另一个流行的日志系统),如果没有找到Log4j,再使用JDK Logging。
1
2
3
4
5
6
7
8
9
10
|
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Main {
public static void main(String[] args) {
Log log = LogFactory.getLog(Main.class);
log.info("start...");
log.warn("end.");
}
}
|
3、实现
log4j
logback
Springboot 默认会使用logback 来记录日志,引入spring-boot-starter-logging的时候会引入如下包
pom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!--比较纯粹的时候logback-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<artifactId>jul-to-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>log4j-to-slf4j</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
</exclusions>
</dependency>
|
Logback 则支持 XML 与 groovy 两种方式配置方式;
配置
默认情况下,Spring Boot将日志输出到控制台,不会写到日志文件。可以在application.properties或application.yml配置,这样只能配置简单的场景,保存路径、日志格式等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
logging:
level:
# 全局输出级别
root: info
# 指定包输出级别
com.huochai.controller: error
pattern:
# 输出格式(控制台输出格式)
console: "%d - %msg%n"
# 输出格式(文件输出格式)
file: "%d - %msg%n"
# 日志输出文件的位置
file:
/Users/peilizhi/javaProects/log/logback.log
# 日志输出文件的目录
path:
# 没有指定logging.file 时会默认创建一个spring.log文件用于记录
/Users/peilizhi/javaProects/log
# 自定义配置文件
# classpath 与文件地址要写在同一行中才有效
config: classpath:log/logback-test.xml
|
在配置的时候 系统不建议使用file 与 path 参数,可能是想通过自定义配置来指定文件输出位置
logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log
logging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容。
- 如果同时指定了logging.file 与logging.path 的话,logging.file 会生效,logging.path 不会生效
自定义配置
是xml 形式的配置文件,只需增加固定开头之后,可以直接书写配置
固定开头:
configuration
appender :
负责写日志的组件,定义了一些输出的格式、文件大小等
- name : 名称
- class : 全类名,常见的有ch.qos.logback.core.ConsoleAppender【控制台输出】、ch.qos.logback.core.rolling.RollingFileAppender[文件输出]。
ConsoleAppender
用于控制台输出
- encode :对输出日志进行格式规范
- target : 字符串System.out(默认)或者System.err
FileAppender
负责写日志到文件中
- file:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
- append:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
- encoder:对记录事件进行格式化。
- prudent:如果是 true,日志会被安全的写入文件,即使其他的FileAppender也在向此文件做写入操作,效率低,默认是 false。
RollingFileAppender
滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。可用于每天记录不同的日志到不同的文件中
-
file:被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值。
-
append:如果是 true,日志被追加到文件结尾,如果是 false,清空现存文件,默认是true。
-
rollingPolicy :当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名。属性class定义具体的滚动策略类
logger
用于控制指定包或者类的打印策略,可以指定level,appender
- name : 全类名
- level :日志级别 大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。 如果未设置此属性,那么当前loger将会继承上级的级别。
- append-ref : 指定的输出类,是之前appender 标签中name 字段的引用(可以指定多个)
- addtivity: 是否向上级logger传递打印信息。默认是true。可以包含零个或多个元素,标识这个appender将会添加到这个logger。
root
它也是元素,但是它是根loger,是所有的上级。只有一个level属性,因为name已经被命名为"root",且已经是最上级了。
日志格式
%m (%msg)输出代码中指定的消息
%p(%level) 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r输出自应用启动到输出该log信息耗费的毫秒数
%c输出所属的类目,通常就是所在类的全名
%t输出产生该日志事件的线程名
%n输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
%d输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},
%l输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
%thread 线程名
logback的配置,需要配置输出源appender,打日志的logger(子节点)和root(根节点),实际上,它输出日志是从子节点开始,子节点如果有输出源直接输入,如果无,判断配置的addtivity,是否向上级传递,即是否向root传递,传递则采用root的输出源,否则不输出日志。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
<?xml version="1.0" encoding="UTF-8"?>
<!--打印logback内部执行日志-->
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<!--定义一些常用的参数-->
<property name="LOG_PATH" value="/Users/peilizhi/javaProects/log"/>
<!--控制台日志, 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"-->
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--当天正在使用的日志-->
<file>${LOG_PATH}/logback.log</file>
<!--基于文件大小和时间的滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名【历史记录】-->
<FileNamePattern>${LOG_PATH}/logback-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>60</MaxHistory>
<!--日志文件最大的大小-->
<MaxFileSize>10MB</MaxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志过滤 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 指定日志级别 -->
<level>ERROR</level>
<!-- 匹配则全部接受 -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配则全部拒绝 -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 输出ERROR日志 -->
<appender name="ERROR_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/logback-error.log</file>
<!-- 基于文件大小和时间的滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/logback-error-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!-- 日志文件保留天数 -->
<maxHistory>60</maxHistory>
<!-- 单个日志文件大小 -->
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<!-- 日志输出格式 -->
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36}: %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志过滤 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 指定日志级别 -->
<level>ERROR</level>
<!-- 匹配则全部接受 -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配则全部拒绝 -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE"/>
</appender>
<!-- show parameters for hibernate sql 专为 Hibernate 定制 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG"/>
<logger name="org.hibernate.SQL" level="DEBUG"/>
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG"/>
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG"/>
<!--myibatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志输出级别 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC_FILE"/>
<appender-ref ref="ERROR_LOG_FILE"/>
</root>
</configuration>
|
异步输出
AsyncAppender,异步记录日志。
AsyncAppender并不处理日志,只是将日志缓冲到一个BlockingQueue里面去,并在内部创建一个工作线程从队列头部获取日志,之后将获取的日志循环记录到附加的其他appender上去,从而达到不阻塞主线程的效果。因此AsynAppender仅仅充当事件转发器,必须引用另一个appender来做事。
由于使用了BlockingQueue来缓存日志,因此就会出现队列满的情况。正如上面原理中所说的,在这种情况下,AsyncAppender会做出一些处理:默认情况下,如果队列80%已满,AsyncAppender将丢弃TRACE、DEBUG和INFO级别的event
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<appender name="FILE" class= "ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 按天来回滚,如果需要按小时来回滚,则设置为{yyyy-MM-dd_HH} -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/opt/log/test.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 如果按天来回滚,则最大保存时间为1天,1天之前的都将被清理掉 -->
<maxHistory>30</maxHistory>
<!-- 日志输出格式 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</Pattern>
</layout>
</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 ="trace">
<appender-ref ref ="ASYNC"/>
</root>
|
log4j2
pom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除默认的logback日志-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入log4j2配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>${spring-log4j2.version}</version>
</dependency>
<!-- 引入异步log4j2-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor}</version>
</dependency>
|
配置
1
2
|
logging:
config: classpath:log/log4j2-test.xml
|
自定义配置
其中log4j2支持多个格式自定义配置:log4j2.properties log4j2.yaml log4j2.json `log4j2.xml
加载的优先级为 properties > yaml > json > xml。
配置文件的节点与logback文件节点大致一样
configuration
- status :这个属性表示log4j2本身的日志信息打印级别。如果把status改为TRACE再执行测试代码,可以看到控制台中打印了一些log4j加载插件、组装logger等调试信息。
日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL
- monitorInterval: Log4j2 能够自动检测修改配置 文件和重新配置本身,设置间隔秒数
appender
有RollingFile、Console、RollingRandomAccessFile。
RollingRandomAccessFileAppender 与 RollingFileAppender 在功能上基本一致,但是底层的实现有所区别,RollingFileAppender 底层是 BufferedOutputStream,RollingRandomAccessFileAppender 底层是使用 ByteBuffer + RandomAccessFile ,性能上有了很大的提升。
RollingRandomAccessFileAppender
- fileName 指定当前日志文件的位置和文件名称
- filePattern 指定当发生Rolling时,文件的转移和重命名规则
- SizeBasedTriggeringPolicy 指定当文件体积大于size指定的值时,触发Rolling
- DefaultRolloverStrategy 指定最多保存的文件个数
TimeBasedTriggeringPolicy 这个配置需要和filePattern结合使用,注意filePattern中配置的文件重命名规则是${log_path}/zcrTest%d{yyyy-MM-dd}.log,最小的时间粒度是dd,即分钟,TimeBasedTriggeringPolicy指定的size是1,结合起来就是每一天生成一个新文件。如果改成%d{yyyy-MM-dd HH},最小粒度为小时,则每一个小时生成一个文件。
filers
Log4j2 中的过滤日志的配置,可以排除一些不需要的配置 。根据范围分为:
Context-wide(全局配置),Logger ,Appender ,Appender Reference
配置文件中节点的顺序, 全局Filter节点<Filters> 必须放在<properties>节点之后。 否则会出现意料不到的问题。例如本人碰到的就是 <PatternLayout pattern="${pattern_debug_info_warn}" /> 替换失败。
经常使用的filter
-
CompositeFilter : 组合模式,用以向外界提供统一的访问接口。
-
ThresholdFilter : 这个Filter负责按照所配置的日志级别过滤Log Event,等于或超出所配置级别的log Event将返回onMatch结果。(threshold : 阈值, 临界值; 门槛,入口)
-
LevelRangeFilter : 这个Filter也是负责按照日志级别过滤Log,只是相比较于ThresholdFilter,它比较的是指定区间范围。这里有一个需要密切注意就是其两个参数minLevel,maxLevel的配置;其和我们的思维惯式有着些许的差异,可能会造成困扰。细节之处可以查看StandardLevel中各个日志级别的底层数值,以及Level.isInRange的比较逻辑。概括而言就是 底层支撑的数值越小,日志危险级别越高。也就是我们如果想要只保留WARN到ERROR级别的日志,那么应该如下配置:
```xml
<LevelRangeFilter minLevel="ERROR" maxLevel="WARN"
onMatch="ACCEPT">
</LevelRangeFilter>
```
-
DynamicThresholdFilter :这个Filter允许依据ThreadContext中是否存在特定的某些值,以及日志级别来过滤LOG。
策略
ACCEPT(接受),DENY(拒绝),NEUTRAL(中立)
中立适合就是当前过滤器暂时不处理,交给后面的过滤器处理
1
2
3
4
5
6
|
<Filters>
<!--如果是error级别拒绝,设置 onMismatch="NEUTRAL" 可以让日志经过后续的过滤器-->
<ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
<!--如果是debug\info\warn级别的日志输出-->
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
|
完整配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
<Configuration status="info">
<Properties>
<property name="LOG_PATH" value="/Users/peilizhi/javaProects/log"/>
</Properties>
<Filters>
<!-- 全局级别Filter,放在property之后 -->
<LevelRangeFilter minLevel="ERROR" maxLevel="DEBUG" onMatch="ACCEPT"/>
</Filters>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"/>
</Console>
<RollingRandomAccessFile name="fileAppender"
fileName="${LOG_PATH}/log4j2.log"
filePattern="${LOG_PATH}/log4j2-%d{yyyyMMddHH}-%i.log"
append="true" immediateFlush="false">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
</RollingRandomAccessFile>
<RollingRandomAccessFile name="errorAppender"
fileName="${LOG_PATH}/log4j2-error.log"
filePattern="${LOG_PATH}/log4j2-error-%d{yyyyMMddHH}-%i.log"
append="true" immediateFlush="false">
<PatternLayout
pattern="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
<!--appender 级别filer-->
<Filters>
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<AsyncRoot level="info" includeLocation="true">
<AppenderRef ref="Console"/>
<AppenderRef ref="fileAppender"/>
<AppenderRef ref="errorAppender"/>
</AsyncRoot>
</Loggers>
</Configuration>
|
日志格式
%d 表示时间,默认情况下表示打印完整时间戳 2012-11-02 14:34:02,123,可以调整 %d 后面的参数来调整输出的时间格式
%p 表示输出日志的等级,可以使用 %highlight{%p} 来高亮显示日志级别
%c 用来输出类名,默认输出的是完整的包名和类名,%c{1.} 输出包名的首字母和完整类名
%t 表示线程名称
%m 表示日志内容,%M 表示方法名称
%n 表示换行符
%L 表示打印日志的代码行数
4、对比
老牌的 Log4j2 凭借着入场早、背靠 Apache 两大优势有着不错的用户支持,官网文档也很完善。
新生的 Logback 凭借着 SLF4j 的原生实现以及被 Spring Boot 钦点的日志框架(Spring 也提供了Log4j2 的 starter,切换依赖即可完成更换日志框架,前文讲过,此处不再赘述),同样也拥有着很好的前景。
社区支持方面,Log4j2 作为 Apache 顶级项目,支持度应该是很不错的,Logback 作为Ceki创业后的产物,也能有很好的保证,二者生态可谓不相上下。
日志的功能我们从使用者的角度可以分为:配置、使用、以及独有特性。配置文件方面,Log4j 提供了更多的配置文件配置方式,Log4j2 支持 properties、YAML、JSON、XML四种,Logback 则支持 XML 与 groovy 两种方式;
Appender 方面,两者均支持自定义扩展 Appender ,Log4j2 并且提供了几乎全部场景的 Appender,文件、控制台、JDBC、NoSql、MQ、Syslog、Socket、SMTP等,Logback提供 Appender 略少于 Log4j2,提供了文件、控制台、数据库、Syslog、Socket、SMTP等,动态化输出方面,Log4j2 提供了ScriptAppenderSelector,Logback 则提供了 Evaluator 与 SiftingAppender(两者均可以用于判断并选择对应的 Appender);
独有特性方面,Logback 支持 Receivers, 可以接收其他 Logback 的 Socket Appender 输出,Logbak 还拥有 logback-access 模块,可以用来与 Servlet容器(如 Tomcat 和 Jetty)集成,提供 http 访问日志功能;Log4j2 则拥有号称能够减少 JVM 垃圾回收停顿时间的 Garbage-free(无垃圾模式),Log4j2 API 支持使用 Java 8 lambda,SLF4j 则在 2.0 版本。
总体来说,Logback 更加简单,并且与self4j 匹配更好(同一作者)。log4j2功能更加强大(filter过滤功能)
二、日志输出规范
日志级别
- FATAL:系统级的错误,一旦出现,就意味着整个系统不能服务,是最严重的日志级别,一个进程的生命周期中应该只记录一次FATAL级别的日志,即该进程遇到无法恢复的错误而退出时
- ERROR: 服务错误日志,项目还能正常使用,但是影响客户正常使用,而对于用户自己操作不当,如请求参数错误等等,是绝对不应该记为ERROR日志的;
- WARN: 该日志表示系统可能出现问题,也可能没有,这种情况如网络的波动等。对于那些目前还不是错误,然而不及时处理也会变为错误的情况,也可以记为WARN日志.
- INFO: 系统正常运行日志。某个子系统的初始化,某个请求的成功执行等等;记录一些项目执行的关键节点。INFO日志不宜过多,通常情况下,INFO级别的日志应该不大于TRACE日志的10%;
- DEBUG or TRACE:精确记录执行的过程,使用什么入参、执行顺序是什么、执行何种操作。
日志输出建议
-
日志输出要精简,能够根据前后文推断出来的内容就不要再打了
-
日志的目的是为了不重现问题的情况下,根据日志输出对问题进行诊断。
-
生产环境的日志级别尽可能的设置高一点,避免大量日志打印
-
在执行核心动作时要打日志记录,记录为INFO
-
增删改操作需要打印参数日志(以便定位一些异常业务问题)
-
条件分支需要打印日志:包括条件值以及重要参数;
-
异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws/throw 往上抛出,由父级方法处理
-
不允许记录日志后又抛出异常,因为这样会多次记录日志
反例
1
2
3
4
|
if (virtualIpPortCrash) {
log.error("接入虚拟IP端口冲突");
throw new BusinessException("接入虚拟IP端口冲突");
}
|
-
不允许出现 e.printStackTrace
-
使用 {} 占位符
-
建议规范:
1
|
2019-12-01 00:00:00.000|pid|log-level|[svc-name,trace-id,span-id,user-id,biz-id]|thread-name|package-name.class-name : log message
|
-
打印异常
- log.error(“异常信息: “, e); 能打印完整异常堆栈信息,建议使用,不要使用e.printStackTrace();
- log.error(“异常信息: {}”, e.getMessage()); // 最简单的异常信息,没有异常类,没有堆栈
- log.error(“异常信息: {}”, e.toString()); // 最简单的异常信息,有异常类,没有堆栈
三、日志拓展
1、 日志脱敏
链接:https://blog.csdn.net/qq_40885085/article/details/113385261