⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 潘志的研发笔记 「志哥聊技术」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

01、背景介绍

对于日志文件,相信大家都并不陌生,通过在关键位置打印相关的日志,有利于快速跟踪和定位软件系统运行中存在的问题。

在之前的 Java 实现日志记录的文章中,我们介绍了能实现日志记录的主流框架有 Log4j、Log4j2、Logback 等,通过一些性能测试发现,Logback 和 Log4j2 两个都比较优秀。同时,它们都支持与 SLF4J 框架的集成,可以轻松实现系统日志框架实现的切换,这主要得益于门面模式的设计。

当采用 Slf4j 来实现日志输出时,我们不需要再纠结到底是用 Log4j2 还是用 Logback 。Slf4j 相当于一个门面接口,可以让代码更加统一,同时它并不是一个日志实现框架,具体的实现会在 Slf4j 接口被调用的时候委托给具体的日志框架来实现。比如,当系统中有 Logback 时,就委托 Logback 来输出日志;当有 Log4j2 时,就委托 Log4j2 来实现;如果两者同时存在,可能会报循环依赖的错误,因此在项目添加依赖的时候,只能选择其中一个,如果有不兼容的问题,需要手动排除。

对于一个 Java web 项目,当采用Slf4j + Logback来实现日志信息的输出时,通常会添加类似于如下的相关依赖包。

<!-- 添加slf4j依赖包 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- 添加logback依赖包 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>

然后,在项目根目录下创建logback.xml并配置相关参数,示例如下。

<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">

<!--
%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
newline:%n
-->
<property name="CUSTOM_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{90} - %msg%n" />

<!-- 上下文名称 -->
<contextName>${CONTEXT_NAME}</contextName>

<!-- 日志输出组件 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 对日志进行格式化。 -->
<encoder>
<pattern>${CUSTOM_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- 日志级别为INFO,日志输出到控制台 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

最后,通过门面接口来输出日志,示例如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogPrintUtil {

private static final Logger LOGGER = LoggerFactory.getLogger(LogPrintUtil.class);

public static void main(String[] args){
LOGGER.info("info信息");
LOGGER.warn("warn信息");
LOGGER.error("error信息");
}
}

02、Spring Boot 日志配置

当我们采用 SpringBoot 框架来开发系统的时候,其实默认已经帮我们集成好了spring-boot-starter-logging日志依赖包,它底层采用的就是上面介绍的logback日志实现框架,同时也集成了Slf4j依赖库。

默认的logback日志配置文件在org/springframework/boot/logging/logback/defaults.xml下,我们只需要在相关的位置采用slf4j接口来打印日志即可,示例如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LogApplication {

private static final Logger LOGGER = LoggerFactory.getLogger(LogApplication.class);

public static void main(String[] args) {
SpringApplication.run(LogApplication.class, args);

LOGGER.error("Hello World");
LOGGER.warn("Hello World");
LOGGER.info("Hello World");
LOGGER.debug("Hello World");
LOGGER.trace("Hello World");
}
}

启动服务,可以看到类似于如下的打印结果:

默认的日志级别为info,如果想更改日志级别,可以在application.properties文件配置日志打印级别,比如改成trace,参数如下:

logging.level.root=trace

重新启动服务,日志打印结果如下:

从控制台输出的结果可以初步分析出,trace级别最低,可以打印所有级别的日志。在整个日志体系中,级别从低到高分为:

TRACE < DEBUG < INFO < WARN < ERROR

级别越底,可打印的日志就更多;相反,级别越高,输出的日志就更少。

从实际情况来看,太多的日志打印也未必是一件好事,有时候会把服务器磁盘撑爆,导致服务宕机。通常我们会配置INFO级别,在关键的位置打印相关信息即可。

2.1、Logback 自定义配置

在实际的业务开发中,通常我们会自定义Logback相关配置文件,有两种做法。

  • 第一种:创建logback.xml配置文件,这种配置文件会直接被日志框架加载
  • 第二种:创建logback-spring.xml配置文件,这种配置文件不会直接被日志框架加载,而是先由 SpringBoot 去解析日志配置再加载,可以使用 SpringBoot 的一些高级功能,比如 Profile 属性。

这里,我们选择第二种方式,在src/main/resources目录下,创建logback-spring.xml文件,一般标准写法如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<!--定义相关变量-->
<property name="log.dir" value="log-demo" />
<property name="custom.log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{90} - %msg%n" />

<!-- 控制台文件输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${custom.log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- 文件输出 -->
<appender name="APP_LOG"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.dir}/log_info.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${custom.log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.dir}/histroy/log-%d{yyyy-MM-dd}-%i.log
</fileNamePattern>
<maxHistory>30</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>250MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="APP_LOG" />
</root>

</configuration>

其中CONSOLE节点,表示将日志输出到控制台;APP_LOG节点,表示将日志输出到文件中,并自动将最近 30 天的日志文件进行归档到histroy文件夹中。

如果想要读取 Spring Boot properties 或根据 Spring profile 定义日志配置,可以通过如下方式实现。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

<!--获取application.properties中定义的变量-->
<springProperty scope="context"
name="customLogPattern"
source="custom.log.pattern"
defaultValue="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{90} - %msg%n"/>

<springProperty scope="context"
name="LogDir"
source="custom.log.dir"
defaultValue="log-demo"/>


<!-- 控制台文件输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${customLogPattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!-- 文件输出 -->
<appender name="APP_LOG"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LogDir}/log_info.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${customLogPattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<!--获取springProfile变量-->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="APP_LOG" />
</root>
</springProfile>

<springProfile name="prod">
<root level="INFO">
<appender-ref ref="APP_LOG" />
</root>
</springProfile>

</configuration>

application.properties文件相关的配置参数如下:

# 指定spring profiles 参数
spring.profiles.active=dev
# 自定义打印格式
custom.log.pattern=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{90} - %msg%n
# 自定义日志存储路径
custom.log.dir=app-demo

2.2、Log4j2 自定义配置

如果项目更倾向于使用 Log4j2 而不是 Logback,迁移方式也很简单。

首先,需要排除掉默认 Logback 相关依赖库,然后添加log4j2相关依赖包,示例如下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

与Logback类似,当添加相关依赖包之后,Spring Boot 默认带了一个log4j2.xml日志配置文件,在org/springframework/boot/logging/log4j2/log4j2.xml。

但是,基于业务的需要,通常我们会自定义配置文件,一般写法如下:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" monitorInterval="3">

<!--变量配置-->
<Properties>
<!--定义日志存储的路径 -->
<property name="log.dir" value="app-demo"/>
<!-- 定义日志输出格式 -->
<property name="custom.log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %l %msg%n"/>
</Properties>

<Appenders>
<!-- 控制台输出 -->
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="${custom.log.pattern}"/>
</Console>

<!-- 文件输出 -->
<RollingFile name="APP_LOG" fileName="${log.dir}/app.log"
filePattern="${log.dir}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="${custom.log.pattern}"/>
<Policies>
<TimeBasedTriggeringPolicy/>
<!-- size根据实际的日志量填写 -->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
</RollingFile>
</Appenders>

<Loggers>
<!-- 日志记录级别 -->
<Root level="info">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="APP_LOG"/>
</Root>
</Loggers>
</Configuration>

此时如果代码中采用的是门面模式的编程方式,无需做任何的调整,即可实现日志框架的切换改造。

03、小结

最后总结一下,对于简单的应用场景,并发量不高的环境下,可以采用 Logback 来实现日志打印;如果对性能要求较高,可以采用 Log4j2,据官方提供的测试报告中,Log4j2 在性能和新技术的应用,比 Logback 领先,毕竟是后起之秀,但是兼容性方面,Logback 更优。

文章目录
  1. 1. 01、背景介绍
  2. 2. 02、Spring Boot 日志配置
    1. 2.1. 2.1、Logback 自定义配置
    2. 2.2. 2.2、Log4j2 自定义配置
  3. 3. 03、小结