【03】Spring Boot 入门之日志的配置

一、市面上的日志框架

1、日志框架介绍

JUL
JCL
Jboss-logging
logback
log4j
log4j2
slf4j
....


# 注意  ===>  SLF4J、Log4j、Logback 都是出自同一个人之手
日志门面 (日志的抽象层)日志实现
SLF4j(Simple Logging Facade for Java)
jboss-logging
JCL(Jakarta Commons Logging)
Log4j
JUL(java.util.logging)
Log4j2
Logback

2、日志框架选择

现在我们从 左边选一个门面(抽象层)、右边来选一个实现。 最佳的搭配如下:

# 选择的原因
1、不使用【Log4j】,是因为 Logback 比它更好,而且作者为同一人
2、不使用【JUL】,因为诞生就是和 Log4j 进行市场竞争的,所以没有后来的 Logback 好
3、不选择【Log4j2】,的理由就是 它做的太好了,还不能和框架适配起来
   就像计算机网络中的 OSI 七层协议和 OSI 五层协议一样,七层太好了,还不能适配;五层并非官方,市场上却用的最多


# 最终选择
日志门面  ===>  SLF4J
日志实现  ===>  Logback


# 注意事项
Spring Boot 的底层是 Spring 框架,Spring 框架默认使用 JCL(Jakarta  Commons Logging) 作为日志框架
而 Spring Boot 选用 SLF4j 和 Logback 作为日志框架

二、SLF4J 的使用

那我们如何在系统中使用 SLF4J 呢? 具体如下图示 【需了解更多请 参考官网】:

以后在开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法。 前提是要给系统里面 导入 SLF4J 的 jar 包和其实现类的 jar 包。 示例如下:

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

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


// 注意事项:
// 每一个日志的实现框架都有自己的配置文件,使用 SLF4J 以后,配置文件还是做成日志实现框架自己本身的配置文件
// 如果你是用 Logback 实现的,那就做成 Logback 的配置文件

三、历史遗留问题

打个比方,开发 A 系统时使用的是【SLF4J + Logback】日志框架,其中又使用 Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis 等等各种 JavaEE 框架,那么就会出现好多日志框架,导致冲突。


那么问题来了,如果我们就要统一日志记录,即要求别的框架统一使用 SLF4J 进行日志输出。那么该如何做呢?该如何让系统中所有的日志都统一到 SLF4J 呢? 解决思路如下:

# 实现系统中所有的日志都统一到 SLF4J

1、将系统中其他日志框架先排除出去

2、用中间包来替换原有的日志框架

3、最后导入 SLF4J 其他的实现

上述思路参考的是 官方 的解决方案。 下图是官方的解决方案图:

四、Spring Boot 日志关系

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- Spring Boot 使用它来做日志功能 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</dependency>

Spring Boot 日志底层依赖关系, 如下图 【建议在新标签页打开图片】:

# Spring Boot 日志总结

1、Spring Boot 底层也是使用【SLF4J + Logback】的方式进行日志记录

2、Spring Boot 也把其他的日志都替换成了 SLF4J

3、中间替换包,log4j 的底层仍然使用 SLF4J,如下面代码所示,其他的中间替换包原理都一样
@SuppressWarnings("rawtypes")
public abstract class LogFactory {

    static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = "http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j";

    static LogFactory logFactory = new SLF4JLogFactory();
# 注意事项

1、如果我们要引入其他框架,一定要把这个框架的默认日志依赖移除掉

2、Spring Boot 能自动适配所有的日志,而且底层使用【SLF4J + Logback】的方式记录日志

3、引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可

五、Spring Boot 日志使用

1、编写测试类

Spring Boot 默认帮我们配置好了日志,只要我们运行程序,控制台便会有日志输出。 测试程序如下:

package edu.jgsu;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

    // 创建记录器
    Logger logger = LoggerFactory.getLogger(getClass());

    @Test
    public void contextLoads() {
        // 以下是日志的级别,优先级从低到高 ===> trace < debug < info < warn < error
        logger.trace("这是trace日志...");
        logger.debug("这是debug日志...");

        // Spring Boot 默认给我们使用的是 info 级别的,info 级别以下的不输出
        // 没有指定级别的,就用 Spring Boot 默认规定的级别;root 级别(就是 info 级别)
        logger.info("这是info日志...");
        logger.warn("这是warn日志...");
        logger.error("这是error日志...");
    }

}

2、执行测试类

运行测试方法,控制台输出的结果如下:

2020-02-04 22:27:01.312  INFO 22432 --- [           main] edu.jgsu.ApplicationTests                : Started ApplicationTests in 4.233 seconds (JVM running for 8.279)
2020-02-04 22:27:01.488  INFO 22432 --- [           main] edu.jgsu.ApplicationTests                : 这是info日志...
2020-02-04 22:27:01.488  WARN 22432 --- [           main] edu.jgsu.ApplicationTests                : 这是warn日志...
2020-02-04 22:27:01.488 ERROR 22432 --- [           main] edu.jgsu.ApplicationTests                : 这是error日志...
2020-02-04 22:27:01.501  INFO 22432 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@aba8be: startup date [Tue Feb 04 22:26:57 CST 2020]; root of context hierarchy

3、改变日志级别

现在要改变日志级别,那该如何做呢? 在主配置文件中添加如下配置:

# 改变日志级别,将原先的 info 级别改成了 trace 级别

logging.level.edu.jgsu=trace

修改完成后,运行测试类,然后控制台输出的结果就改变了。 结果如下:

2020-02-04 22:29:48.232  INFO 2368 --- [           main] edu.jgsu.ApplicationTests                : Started ApplicationTests in 3.614 seconds (JVM running for 5.138)
2020-02-04 22:29:48.369 TRACE 2368 --- [           main] edu.jgsu.ApplicationTests                : 这是trace日志...
2020-02-04 22:29:48.369 DEBUG 2368 --- [           main] edu.jgsu.ApplicationTests                : 这是debug日志...
2020-02-04 22:29:48.370  INFO 2368 --- [           main] edu.jgsu.ApplicationTests                : 这是info日志...
2020-02-04 22:29:48.370  WARN 2368 --- [           main] edu.jgsu.ApplicationTests                : 这是warn日志...
2020-02-04 22:29:48.370 ERROR 2368 --- [           main] edu.jgsu.ApplicationTests                : 这是error日志...
2020-02-04 22:29:48.377  INFO 2368 --- [       Thread-2] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@d9b808: startup date [Tue Feb 04 22:29:45 CST 2020]; root of context hierarchy

4、修改日志的默认配置

如何修改 Spring Boot 日志的默认配置呢? 具体如下:

# 修改日志级别
logging.level.edu.jgsu=trace


# 指定完整的路径来保存日志。以下指定了在 C 盘用 springboot.log 文件来记录日志
logging.file=c:/springboot.log


# 在当前磁盘的根路径下创建 spring 文件夹和里面的 log 文件夹
# 然后在 log 文件夹里使用 spring.log 作为默认文件来记录日志
# logging.path=/spring/log


# 在【控制台】输出的日志格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n


# 在【指定文件】中的日志输出格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n

在这里,我们区分一下【logging.file】配置和【logging.path】配置。 具体如下:

logging.filelogging.pathExampleDescription
(none)(none)只在控制台输出日志
指定文件名(none)my.log输出日志到 my.log 文件
(none)指定目录/var/log输出日志到 /var/log 目录的 spring.log 文件中

5、执行测试,查看结果

配置好之后,执行测试类,然后就会在 C 盘生成 springboot.log 文件。 里面的日志与格式如下:

2020-02-04 === [main] === INFO  === edu.jgsu.ApplicationTests ==== Starting ApplicationTests on guoshizhan with PID 1984 (started by guoshizhan in F:\99-Frameworks\01-springboot\003-springboot-logging)
2020-02-04 === [main] === DEBUG === edu.jgsu.ApplicationTests ==== Running with Spring Boot v2.0.1.RELEASE, Spring v5.0.5.RELEASE
2020-02-04 === [main] === INFO  === edu.jgsu.ApplicationTests ==== No active profile set, falling back to default profiles: default
2020-02-04 === [main] === INFO  === o.s.w.context.support.GenericWebApplicationContext ==== Refreshing org.springframework.web.context.support.GenericWebApplicationContext@2ff096: startup date [Tue Feb 04 22:57:02 CST 2020]; root of context hierarchy
2020-02-04 === [main] === INFO  === o.s.web.servlet.handler.SimpleUrlHandlerMapping ==== Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-04 === [main] === INFO  === o.s.w.s.m.m.a.RequestMappingHandlerAdapter ==== Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@2ff096: startup date [Tue Feb 04 22:57:02 CST 2020]; root of context hierarchy
2020-02-04 === [main] === INFO  === o.s.w.s.m.m.a.RequestMappingHandlerMapping ==== Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2020-02-04 === [main] === INFO  === o.s.w.s.m.m.a.RequestMappingHandlerMapping ==== Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2020-02-04 === [main] === INFO  === o.s.web.servlet.handler.SimpleUrlHandlerMapping ==== Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-04 === [main] === INFO  === o.s.web.servlet.handler.SimpleUrlHandlerMapping ==== Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-04 === [main] === INFO  === edu.jgsu.ApplicationTests ==== Started ApplicationTests in 3.936 seconds (JVM running for 5.688)
2020-02-04 === [main] === TRACE === edu.jgsu.ApplicationTests ==== 这是trace日志...
2020-02-04 === [main] === DEBUG === edu.jgsu.ApplicationTests ==== 这是debug日志...
2020-02-04 === [main] === INFO  === edu.jgsu.ApplicationTests ==== 这是info日志...
2020-02-04 === [main] === WARN  === edu.jgsu.ApplicationTests ==== 这是warn日志...
2020-02-04 === [main] === ERROR === edu.jgsu.ApplicationTests ==== 这是error日志...
2020-02-04 === [Thread-2] === INFO  === o.s.w.context.support.GenericWebApplicationContext ==== Closing org.springframework.web.context.support.GenericWebApplicationContext@2ff096: startup date [Tue Feb 04 22:57:02 CST 2020]; root of context hierarchy

与 C 盘的 springboot.log 文件里的日志相比, 控制台的日志与格式如下:

2020-02-04 [main] INFO  edu.jgsu.ApplicationTests - Starting ApplicationTests on guoshizhan with PID 1984 (started by guoshizhan in F:\99-Frameworks\01-springboot\003-springboot-logging)
2020-02-04 [main] DEBUG edu.jgsu.ApplicationTests - Running with Spring Boot v2.0.1.RELEASE, Spring v5.0.5.RELEASE
2020-02-04 [main] INFO  edu.jgsu.ApplicationTests - No active profile set, falling back to default profiles: default
2020-02-04 [main] INFO  o.s.w.context.support.GenericWebApplicationContext - Refreshing org.springframework.web.context.support.GenericWebApplicationContext@2ff096: startup date [Tue Feb 04 22:57:02 CST 2020]; root of context hierarchy
2020-02-04 [main] INFO  o.s.web.servlet.handler.SimpleUrlHandlerMapping - Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-04 [main] INFO  o.s.w.s.m.m.a.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@2ff096: startup date [Tue Feb 04 22:57:02 CST 2020]; root of context hierarchy
2020-02-04 [main] INFO  o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2020-02-04 [main] INFO  o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2020-02-04 [main] INFO  o.s.web.servlet.handler.SimpleUrlHandlerMapping - Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-04 [main] INFO  o.s.web.servlet.handler.SimpleUrlHandlerMapping - Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2020-02-04 [main] INFO  edu.jgsu.ApplicationTests - Started ApplicationTests in 3.936 seconds (JVM running for 5.688)
2020-02-04 [main] TRACE edu.jgsu.ApplicationTests - 这是trace日志...
2020-02-04 [main] DEBUG edu.jgsu.ApplicationTests - 这是debug日志...
2020-02-04 [main] INFO  edu.jgsu.ApplicationTests - 这是info日志...
2020-02-04 [main] WARN  edu.jgsu.ApplicationTests - 这是warn日志...
2020-02-04 [main] ERROR edu.jgsu.ApplicationTests - 这是error日志...
2020-02-04 [Thread-2] INFO  o.s.w.context.support.GenericWebApplicationContext - Closing org.springframework.web.context.support.GenericWebApplicationContext@2ff096: startup date [Tue Feb 04 22:57:02 CST 2020]; root of context hierarchy

6、日志格式含义

上面的两种日志格式都是 properties 配置文件中配置的格式。现在来说一下日志格式中那些字符的含义:

# 日志输出格式含义解释

%d           #==>   表示日期时间
%thread      #==>   表示线程名
%-5level     #==>   表示级别【-表示左对齐】,从左显示 5 个字符宽度
%logger{50}  #==>   表示 logger 名字最长 50 个字符,否则按照句点分割
%msg         #==>   日志消息
%n           #==>   是换行符


# 使用举例如下
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

六、使用自定义日志配置文件

如何使用自定义的日志文件呢?给类路径下即 resources 目录下放上每个日志框架自己的配置文件即可。 有了自定义的日志配置文件,那么 Spring Boot 就不再使用它自己的默认配置了。注意:自定义的日志配置文件的名字必须在下面表格 Customization 中的其中一个。 需了解更多内容,请参考 官方文档

Logging SystemCustomization
Logbacklogback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy
Log4j2log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging)logging.properties
# Spring Boot 官方文档给出的建议

1、建议不要用【logback.xml】这个名字,因为【logback.xml】直接就被日志框架识别了,因此无法使用高级功能

2、建议使用【logback-spring.xml]这个名字,日志框架就不能直接加载日志的配置项,
   因此就可以由 Spring Boot 来解析日志配置,从而使用 Spring Boot 的高级 Profile 功能

下面标签里面可以 指定某段配置只在某个环境下 生效:

<springProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
</springProfile>

下面配置 自定义了开发环境日志格式和非开发环境日志格式:

<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
        <!-- 开发环境日志格式 -->
        <springProfile name="dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==> [%thread] ==> %-5level %logger{50} - %msg%n</pattern>
        </springProfile>
        <!-- 非开发环境日志格式 -->
        <springProfile name="!dev">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} == [%thread] == %-5level %logger{50} - %msg%n</pattern>
        </springProfile>
    </layout>
</appender>

如果使用 logback.xml 作为日志配置文件,还要配置使用 Profile 功能, 将出现以下错误:

no applicable action for [springProfile]

七、切换日志框架

Spring Boot 默认使用的日志框架是【SLF4J + Logback】,那么如果我要切换其他的日志框架,那该怎么做呢?可以按照 SLF4J 的日志适配图进行相关的切换。 如下图:

举个例如:如果我们要把原先的【SLF4J + Logback】切换为【SLF4J + log4j】的方式,那该这么做呢? 首先,我们 根据日志适配图排除不需要的日志依赖,然后新增我们需要的日志依赖。 相关依赖关系如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!-- 排除不需要的日志依赖 -->
    <exclusions>
        <exclusion>
            <artifactId>logback-classic</artifactId>
            <groupId>ch.qos.logback</groupId>
        </exclusion>
        <exclusion>
            <artifactId>log4j-over-slf4j</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

<!-- 新增 log4j 日志依赖 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
</dependency>

那如果我想把日志框架切换为 log4j2,那该怎么做呢? 具体依赖关系处理如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!-- 排除 Spring Boot 自带的 logging 日志依赖 -->
    <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>
</dependency>
# 结语


到这里,Spring Boot 的日志就结束了

这一章节我感觉后面日志切换有点难,也写的不好,还没有贴代码。后期有时间的话,我会以更好的表达来重新写过

那么,接下来就是 Spring Boot 的重点内容,Spring Boot 与 Web 开发
  • 文章作者:root
  • 创建时间:2020-03-02 22:15:11
  • 更新时间:2022-01-06 22:15:11
  • 版权声明:本文为博主原创文章,未经博主允许不得转载!
请 在 评 论 区 留 言 哦 ~~~
1024