Java日志框架

在Java开发中,常用的日志框架有JDKLog、Log4J、LogBack、SLF4J。这些日志记录框架各有各的特点,各有各的应用场景。了解这些框架的特点及应用场景,有利于我们做技术选型的时候做出正确的判断。

JAVA日志框架简要介绍

JDKLog——日志小刀

JDKLog是JDK官方提供的一个记录日志的方式,直接在JDK中就可以使用。

1
2
3
4
5
6
7
8
import java.util.logging.Logger;

public class JDKLog {
public static void main(String[] args) {
Logger logger = Logger.getLogger("JDKLog");
logger.info("Hello World.");
}
}

​JDKLog的有点是使用非常简单,直接在 JDK 中就可以使用。但 JDKLog 功能比较太过于简单,不支持占位符显示,拓展性比较差,所以现在用的人也很少。

Log4J——日志大炮

Log4J是Apache的一个日志开源框架,有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,可以很好地将不同日志界别的日志分开记录,极大地方便了日志的查看。
Log4J有1.X和2.X两个版本,现在官方推荐使用2.X版本,2.X版本在架构上进行了一些升级,配置文件也发生了一些变化。
使用Log4J框架首先需要引入依赖的包:

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>

增加log4J2.xml放在resource目录下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

其中<ROOT>节点的level级别表示输出的最低级别。
最后编写一个测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/****
** Log4J Demo
**/
public class Log4jLog {
public static void main(String args[]) {
Logger logger = LogManager.getLogger(Log4jLog.class);
logger.debug("Debug Level");
logger.info("Info Level");
logger.warn("Warn Level");
logger.error("Error Level");
}
}

如果没有配置log4j2.xml配置文件,那么LOG4J将自动启用类似于下面的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

使用默认配置文件的输出结果:

1
2
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
11:40:07.377 [main] ERROR com.chanshuyi.Log4jLog - Error Level

从上面的使用步骤可以看出Log4J的使用稍微复杂一些,但是条理还是很清晰的。而且因为Log4J有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,所以可以很好地记录不同业务问题。因为这些优点,所有在几年前几乎所有人都使用LOG4J作为日志记录框架。
但LOG4J本身也存在一些缺点,比如不支持使用占位符,不利于代码阅读等缺点,但是相比JDKLog,LOG4J已经是非常好的日志记录框架了。

LogBack——日志火箭

LogBack其实可以说是Log4J的进化版,因为它们两个都是同一个人设计的开源框架。LogBack 除了具备 Log4j 的所有优点之外,还解决了 Log4J 不能使用占位符的问题。
使用LogBack需要首先引入依赖:

1
2
3
4
5
6
<!-- LogBack -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>

配置logback.xml配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<logger name="com.chanshuyi" level="TRACE"/>

<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>

LogBack的日志级别区分可以细分到类或者包,这样就可以使日志记录变得更加灵活。之后在类文件中引入Logger类,并进行日志记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/****
** LogBack Demo
**/
public class LogBack {
static final Logger logger = LoggerFactory.getLogger(LogBack.class);
public static void main(String[] args) {
logger.trace("Trace Level.");
logger.debug("Debug Level.");
logger.info("Info Level.");
logger.warn("Warn Level.");
logger.error("Error Level.");
}
}

LogBack解决了Log4J不能使用占位符的问题,这使得阅读日志代码非常方便。除此之外,LogBack比Log4J有更快的运行速度,更好的内部实现。并且LogBack内部集成了Slf4J可以更原生地实现一些日志记录的实现。

SLF4J——适配器

为了避免切换日志组件时要改动代码,这时候一个叫做SLF4J(Simple Logging Facade for Java,即Java简单日志记录接口集)的东西出现。
SLF4J是一个日志的接口规范,它对用户提供了统一的日志接口,屏蔽了不同日志组件的差异。这样我们在编写代码的时候只需要看SLF4J这个接口文档即可,不需要去理会不同日志框架的区别。而当我们需要更换日志组件的时候,我们只需要更换一个具体的日志组件Jar包就可以。
下面是整合SLF4J和日志框架的使用方式

SLF4J + JDKLog

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>

SLF4J + LOG4J

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

配置log4J.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' >

<appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="[%d{dd HH:mm:ss,SSS\} %-5p] [%t] %c{2\} - %m%n" />
</layout>
<!--过滤器设置输出的级别-->
<filter class="org.apache.log4j.varia.LevelRangeFilter">
<param name="levelMin" value="debug" />
<param name="levelMax" value="error" />
<param name="AcceptOnMatch" value="true" />
</filter>
</appender>

<!-- 根logger的设置-->
<root>
<priority value ="debug"/>
<appender-ref ref="myConsole"/>
</root>
</log4j:configuration>

SLF4J + LogBack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>

配置logBack.xml文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<logger name="com.chanshuyi" level="TRACE"/>

<root level="warn">
<appender-ref ref="STDOUT" />
</root>
</configuration>

JAVA日志框架的抉择

现在最流行的日志框架解决方案莫过于SLF4J + LogBack。其有以下几个优点:

  • LogBack自身实现了SLF4J的日志接口,不需要SLF4J去做进一步的适配
  • LogBack自身是在Log4J的基础上优化而成的,其运行速度和效率都比LOG4J高
  • SLF4J + LogBack支持占位符,方便日志代码的阅读,而Log4j则不支持

LogBack被分成三个组件:logback-core、logback-classic和logback-access

  • logback-core提供了LogBack的核心功能,是另外两个组件的基础
  • logback-classic则实现了SLF4J的API,所以当想配合SLF4J使用时,需要将它引入
  • logback-access是为了集成Servlet环境而准备的,可提供HTTP-access的日志接口

了解LogBack日志的日志流向,对于我们后面学习日志框架的配置很有帮助。从下图可以看出LogBack的日志记录数据流是从Class或Package流到Logger,再从Logger到Appender,最后从Appender到具体的输出终端

LogBack配置文件介绍

LogBack配置文件可以分为几个节点,其中Configuration是根节点,Appender、Logger和Root是Configuration的子节点。

Appender节点

<appender>是<configuration>的子节点,是负责写日志的组件。appender有两个必要属性name、class。name指定appender的名称,class指定appender的全限定名

class主要包括:

  • ch.qos.logback.core.ConsoleAppender 控制台输出
  • ch.qos.logback.core.FileAppender 文件输出
  • ch.qos.logback.core.RollingFileAppender 文件滚动输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?> 
<configuration debug="true" scan="true" scanPeriod="2">
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
</appender>

<!-- conf file out -->
<appender name="file_out" class="ch.qos.logback.core.FileAppender">
</appender>

<!-- conf file out -->
<appender name="file_out" class="ch.qos.logback.core.RollingFileAppender">
</appender>

<root></root>
<logger></logger>
</configuration>

ConsoleAppender

把日志添加到控制台,有如下节点:

  • <encoder>:对日志进行格式化
  • <target>:字符串System.out 或者 System.err, 默认 System.out
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="console_out" />
</root>
</configuration>

FileAppender

把日志添加到文件,有如下节点:

  • <file>:被写入的文件名,可以是相对目录,也可以是绝对目录,如果目录不存在则会自动创建
  • <append>:如果是true,日志被追加到文件末尾,如果是false,清空现存文件,默认是true
  • <encoder>:对日志进行格式化
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appender name="file_out" class="ch.qos.logback.core.FileAppender">
<file>logs/debug.log</file>
<encoder>
<pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
</encoder>
</appender>
</configuration>

RollingFileAppender

滚动记录文件,先将日志记录到指定文件,当符合某种条件时,将日志记录到其他文件,有如下节点:

  • <file>:被写入的文件名,可以是相对目录,也可以直接目录,如果目录不存在则自动创建
  • <append>:如果是true,日志被追加到文件末尾,如果是false,清空现存文件,默认是true
  • <encoder>:对日志进行格式化
  • <rollingPolicy>:当发生滚动时,决定RollingFileAppender的行为,涉及文件移动和重命名

rollingPolicy

  • TimeBaseRollingPolicy:最常用的滚动策略,根据时间来制定滚动策略,即负责滚动也负责除非滚动。有如下节点
    • <fileNamePattern>:必要节点,包含文件及"%d"转换符,"%d"可以包含一个java.text.SimpleDateFormat制定的时间格式,如:%d{yyyy-MM},如果直接使用%d ,默认格式是 yyyy-MM-dd
    • <maxHistory>:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,假设设置每个月滚动,且<maxHistory>是6,则只保存最近6个月的文件,删除之前的旧文件,注意:删除旧文件是哪些为了归档而创建的目录也会被删除
    • <filenamePattern>:必须包含"%i",例如:设置最小值,和最大值分别为1和2,命名模式为log%i.log,会产生归档文件log1.loglog2.log,还可以指定文件压缩选项,例如log%i.log.gz或者log%i.log.zip
  • triggeringPolicy:告知RollingFileAppender,激活RollingFileAppender滚动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 03:conf errorAppender out -->
<appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">
<file>logs/error.log</file>
<!-- 设置滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--设置日志命名模式-->
<fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--最多保留30天log-->
<maxHistory>30</maxHistory>
</rollingPolicy>
<!-- 超过150MB时,触发滚动策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>150</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
</encoder>
</appender>

logger节点

logger是<configuration>的子节点,来设置某一个包或者具体的某一个类的日志打印级别,以及指定<appender>。logger仅有一个name属性,两个可选属性level/addtivity

  • name:用来指定受此logger约束的某一个包或者具体的某一个类
  • level:用来设置打印级别,大小写无关。可选值有TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。还有一个特殊值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设此属性,那么当前logger将会继承上级的级别
  • addtivity:是否向上级logger传递打印信息,默认为true

<logger>可以包含零个或多个<appender-ref>元素,表示这个appender将会添加到logger

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
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤掉非INFO级别 -->
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>

<!-- conf infoAppender out -->
<appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">
<file>logs/info.log</file>
<!-- 设置滚动策略 -->
<rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--设置日志命名模式-->
<fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--最多保留30天log-->
<maxHistory>30</maxHistory>
</rollingPoliy>
<!-- 超过150MB时,触发滚动策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>150</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
</encoder>
</appender>

<!-- 添加两个appender节点 -->
<logger name="logback.olf.log" level="info">
<appender-ref ref = "console_out"/>
<appender-ref ref = "infoAppender"/>
</logger>
</configuration>

root节点

元素配置根logger。该元素有一个level属性,没有name属性,因为已经被命名为root。Level属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。如果root元素没有引用任何appender,就会失去所有appender。

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
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤掉非INFO级别 -->
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>

<!-- 01:conf infoAppender out -->
<appender name="infoAppender" class="ch.qos.logback.core.RollingFileAppender">

<file>logs/info.log</file>
<!-- 设置滚动策略 -->
<rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--设置日志命名模式-->
<fileNamePattern>infoFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--最多保留30天log-->
<maxHistory>30</maxHistory>
</rollingPoliy>
<!-- 超过150MB时,触发滚动策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>150</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
</encoder>
</appender>

<!-- 02:conf debugAppender out -->
<appender name="debugAppender" class="ch.qos.logback.core.RollingFileAppender">
<file>logs/debug.log</file>
<!-- 设置滚动策略 -->
<rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--设置日志命名模式-->
<fileNamePattern>debugFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--最多保留30天log-->
<maxHistory>30</maxHistory>
</rollingPoliy>
<!-- 超过150MB时,触发滚动策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>150</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
</encoder>
</appender>

<!-- 03:conf errorAppender out -->
<appender name="errorAppender" class="ch.qos.logback.core.RollingFileAppender">
<file>logs/error.log</file>
<!-- 设置滚动策略 -->
<rollingPoliy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--设置日志命名模式-->
<fileNamePattern>errorFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!--最多保留30天log-->
<maxHistory>30</maxHistory>
</rollingPoliy>
<!-- 超过150MB时,触发滚动策略 -->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>150</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d [%p] %-5level %logger - %msg%newline</pattern>
</encoder>
</appender>

<root level="ALL">
<appender-ref ref="infoAppender"/>
<appender-ref ref="debugAppender"/>
<appender-ref ref="errorAppender"/>
</root>
</configuration>

filter过滤节点

级别过滤器(LevelFilter)

LevelFilter根据记录级别对记录事件进行过滤。如果事件的级别等于配置的级别,过滤器会根据onMatch和onMismatch属性接收和拒绝时间。下面是和配置文件例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤掉非INFO级别 -->
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>

<encoder>
<pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="console_out" />
</root>
</configuration>

临界值过滤器(ThresholdFilter)

ThresholdFilter过滤掉低于指定临界值的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <!-- 过滤掉TRACE和DEBUG级别的日志 -->
<level>INFO</level>
</filter>

<encoder>
<pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="console_out" />
</root>
</configuration>

求值过滤器(EvaluatorFilter)

评估是否符合指定的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.EvaluatorFilter">
<evaluator>
<!--过滤掉所有日志中不包含hello字符的日志-->
<expression>
message.contains("hello")
</expression>
<onMatch>NEUTRAL</onMatch>
<onMismatch>DENY</onMismatch>
</evaluator>
</filter>

<encoder>
<pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="console_out" />
</root>
</configuration>

匹配器(Matchers)

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
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.EvaluatorFilter">
<evaluator>
<matcher>
<Name>odd</Name>
<!-- 过滤掉序号为奇数的语句-->
<regex>statement [13579]</regex>
</matcher>
<expression>odd.matches(formattedMessage)</expression>
<onMatch>NEUTRAL</onMatch>
<onMismatch>DENY</onMismatch>
</evaluator>
</filter>

<encoder>
<pattern>%-4relative [%thread] %-5level %logger{30} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="console_out" />
</root>
</configuration>

logback配置文件实例

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="true" scan="true" scanPeriod="30 seconds">

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are by default assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>

<!-- 常用的Pattern变量,大家可打开该pattern进行输出观察 -->
<!--
<pattern>
%d{yyyy-MM-dd HH:mm:ss} [%level] - %msg%n
Logger: %logger
Class: %class
File: %file
Caller: %caller
Line: %line
Message: %m
Method: %M
Relative: %relative
Thread: %thread
Exception: %ex
xException: %xEx
nopException: %nopex
rException: %rEx
Marker: %marker
%n
</pattern>
-->
</encoder>
</appender>

<!-- 按日期区分的滚动日志 -->
<appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/error.log</file>

<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>

<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>

<!-- keep 30 days' worth of history -->
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>

<!-- 按文件大小区分的滚动日志 -->
<appender name="INFO-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/info.log</file>

<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>

<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>

<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>info.%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>

<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>

</appender>

<!-- 按日期和大小区分的滚动日志 -->
<appender name="DEBUG-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/debug.log</file>

<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>

<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>

<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 100MB -->
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>

</rollingPolicy>

</appender>

<!-- 级别阀值过滤 -->
<appender name="SUM-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/sum.log</file>

<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>

<!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>

<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 100MB -->
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>

</rollingPolicy>

</appender>

<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="ERROR-OUT" />
<appender-ref ref="INFO-OUT" />
<appender-ref ref="DEBUG-OUT" />
<appender-ref ref="SUM-OUT" />
</root>
</configuration>

日志系统转换

从日志框架转向slf4j

  • jul-to-slf4j:jdk-logging到slf4j的桥梁
  • log4j-over-slf4j:log4j1到slf4j的桥梁
  • jcl-over-slf4j:commons-logging到slf4j的桥梁

从slf4J转向具体的日志框架

  • slf4j-jdk14:slf4j到jdk-logging的桥梁
  • slf4j-log4j12:slf4j到log4j1的桥梁
  • log4j-slf4j-impl:slf4j到log4j2的桥梁
  • logback-classic:slf4j到logback的桥梁
  • slf4j-jcl:slf4j到commons-logging的桥梁

日志框架技术一览

  • JUL:JDK中的日志记录工具,也常称为JDKLog、jdk-logging。

  • LOG4J1:一个具体的日志实现框架。

  • LOG4J2:一个具体的日志实现框架,是LOG4J1的下一个版本。

  • LOGBACK:一个具体的日志实现框架,但其性能更好。

  • JCL:一个日志门面,提供统一的日志记录接口,也常称为commons-logging。

  • SLF4J:一个日志门面,与JCL一样提供统一的日志记录接口,可以方便地切换看具体的实现框架。

    JUL、LOG4J1、LOG4J2、LOGBACK是日志实现框架,而JCL、SLF4J是日志实现门面

注意

  • 一般建议日志对象logger声明为private static final。声明为private可防止logger对象被其它类非法使用。声明为static可防止重复new出logger对象,造成资源浪费;还可防止logger被序列化,造成安全风险。声明为final是因为在类的声明周期内无需变更logger
  • 如果日志不分级别或级别设置不合理,则定位问题时就无法快速有效地屏蔽大量低级别信息,给快速定位问题带来难度。建议与具体实现的日志使用debug级别,一般的业务处理日志使用info级别,不影响业务进行的错误使用warn级别,而记录异常或重要错误的日志应为error级别
Author: Toyan
Link: https://toyan.top/spring-logger/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
支付宝打赏
微信打赏