SpringBoot从入门到放弃之日志配置

1.概述

日志作为程序开发过程中不可或缺的重要因素,它能将程序运行过程中的关键数据输出到控制台或文件中,在程序出现异常时,我们能够根据这些关键数据快速定位异常出现的原因,进而能够快速修复程序,保持程序持续健康运行。在JAVA程序开发中,比较收欢迎的开源日志框架包括:

JUL(Jakarta Commons Logging)、JCL(java.util.logging)、Jboss-logging、logback、log4j、log4j2、SLF4J(Simple Logging Facade for Java)

这些日志框架中有一部分是具体的日志框架实现,有一部分是框架的规范接口(也称为门面模式)。具体划分如下表所示:

抽象接口(日志门面)日志框架
JCL、Jboss-logging、SLF4JJUL、log4j、log4j2 、logback

JCL由apache基金会提供,它为所有的JAVA日志实现提供了一个统一接口,然后在适配类中将对日志的操作委托给具体日志框架,使用了设计模式中的适配器模式。它自身也提供了一个日志的功能实现,但是功能较弱,所有一般不单独使用它。
Jboss-logging已经多年未更新了,逐渐淡出市场,上一个使用它作为日志门面的还是Hibernate。SLF4J是目前日志门面的主流,在《阿里巴巴Java开发手册》曾作如下规约:

【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

JUL、log4j、log4j2 、logback等都是具体的日志框架。log4j、logback均出自同一人之手(Ceki Gülcü),logback相比log4j具有更高的性能,原因可查看作者本人对logback的描述:https://logback.qos.ch/reasonsToSwitch.html。下面看关于SpringBoot文档对依赖日志的说明:
在这里插入图片描述
这一段话的大致意思是:SpringBoot内部主要使用Commons Logging(JCL)作为日志记录框架,但对其底层日志实现框架持开放态度,底层可以选择Java Util Logging、Log4J2 和 Logback 作为具体实现框架。在任意情况下,日志记录器都预先配置了控制台方式输出,也可以将日志输出到文件。默认情况下,如果使用“启动器”,则使用Logback进行日志记录。同时还包括适当的Logback路由,以确保使用Java Util日志记录、Commons日志记录、Log4J或SLF4J的依赖库都能正常工作。
所以,默认方式创建的SpringBoot项目,日志框架主要使用slf4j+logback(这两个框架也出自于Ceki Gülcü),为什么要选择这两个作为springboot的默认框架,原因自然是从性能和适配性各方面考虑。本文将详细讲述SpringBoot中默认日志框架类型,以及相关类型的配置。

2.SLF4J使用

SLF4J(Simple Logging Facade for Java)翻译为:简单日志门面。它不是具体日志解决方案,只是一个日志抽象层,底层允许使用其它类型的日志框架,只要符合规范,可以随时切换底层日志框架。SLF4J官网地址如下:https://www.slf4j.org/manual.html,这里贴出一张SLF4J与其它日志框架整合时依赖的jar包:
在这里插入图片描述
1.当SLF4J与logback一起使用时,需要依赖的jar包有:logback-classic.jar和logback-core.jar;
2.当SLF4J与reload4j一起使用时,所依赖的适配层为:slf4j-reload4j.jar,底层所依赖具体日志框架为:reload4j.jar;
3.当SLF4J与JUL一起使用时,所依赖适配层为:slf4j-jdk14.jar,所依赖底层日志框架为jdk自带的java.util.logging;

2.1 SpringBoot默认日志系统

SpringBoot的日志系统默认是slf4j+logback,具体可以从它的启动pom文件来看:

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

该pom文件中依赖的JAR包如下:
在这里插入图片描述
由上图可以看出,spring-boot-starter-web中依赖了spring-boot-starter,spring-boot-starter中依赖了spring-boot-starter-logging,
spring-boot-starter-logging主要依赖了slf4j和logback等日志框架,所以SpringBoot默认日志框架就是slf4j+logback的搭配。
那么在SpringBoot中如何使用该日志框架呢,在创建SpringBoot项目后,使用方式如下:

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(classes = LogApp.class)
public class LogTest {

    Logger log =  LoggerFactory.getLogger(getClass());

    @Test
    public void test1() {
        log.trace("hello trace");
        log.info("hello info ");
        log.debug("hello debug");
        log.warn("hello warn");
        log.error("hello error");
    }
}

运行结果如下图所示:
在这里插入图片描述
上述日志打印级别应设置为trace,因为springboot默认输出级别为info。在application.properties文件中新增如下配置:

logging.level.com.eckey.lab=trace

SLF4J日志默认级别分为以下五种(日志验证级别由下往上):

  • TRACE:跟踪信息
  • DEBUG :调试信息
  • INFO :一般信息
  • WARN :警告信息
  • ERROR: 错误信息

默认情况下SpringBoot会直接将日志输出到控制台,不会输出到文件中,如果需要将日志输出到指定文件中,则需要进行以下配置:

#指定日志文件名称
logging.file.name=文件名
#指定日志文件路径
logging.file.path=日志文件路径
#设置指定包下的日志级别
logging.level.包名=指定包下的日志级别
#设置控制台日志打印规则
logging.pattern.console=日志打印规则

logging.file.name和logging.file.path默认配置一个即可,如果同时配置,只有logging.file.name会生效。logging.file.path只能配置日志生成的路径,日志生成的默认文件名称是spring.log。下图是不同两种情形下所生成的文件情况,具体大家可自行测试:
在这里插入图片描述
如果想让日志按照指定格式进行输出,可以在application.properties添加如下配置:

# 修改在控制台输出的日志格式
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

具体每个参数解释如下:

%d-时间格式、%thread-线程、%-5level-从左5字符宽度、%logger{50}-日志50个字符、%msg-信息、%n-换行

上述配置生成的日志格式如下:
在这里插入图片描述
如果不想使用官方的日志输出格式,可以自定义自己的log配置,将该配置文件放在指定目录下或resource目录下,官网给出的配置文件名称如下,优先加载以-spring结尾的配置文件。
在这里插入图片描述

2.2 其它日志框架转换slf4j

在springboot开发中,经常会遇到一个项目中使用多种日志框架,比如Spring日志框架是JCL,Hibernate的框架是Jboss-logging等。那么如果统一日志输出类型(统一为slf4j),统一日志配置的话,该如何处理?SLF4J官网上给出了如下方案:
在这里插入图片描述
翻译上面这段话,意思是:通常情况,项目中某些组件依赖于 SLF4J 以外的日志记录 API。为了解决某些组件以后可能会切换到SLF4J,SLF4J附带了几个桥接模块,这些模块重定向了对log4j,JCL和java.util.logging API的调用,使得使用者看起来是在调用SLF4J。具体模块如下图所示:
在这里插入图片描述
上述图片中描述了commons logging、log4j、java util logging统一到SLF4J框架的方法,以common logging为例进行分析:

1.首先将原系统中自带的commons-logging包先排除出去;
2.接下来引入适配层jcl-over-slf4j.jar来替换commons-logging包,这个包上层是commons-logging的接口,下层是slf4j接口;
最后,引入slf4j的具体实现框架(比如logback)。

其它几个框架类似,只是不同的框架换了不同的适配包而已。

2.3 SpringBoot中日志框架切换

本节主要讲述如何替换springboot的默认日志框架,选择自己想要的日志框架,比如log4j2,主要步骤如下:

1.去除springboot中关于logback的依赖;
2.引入log4j2依赖;
3.配置log4j2-spring.xml。

2.3.1 去除springboot中关于logback的依赖

在springboot的默认依赖spring-boot-starter-web中去除关于spring-boot-starter-logging的配置,具体如下:

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

2.3.2 引入log4j2依赖

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

2.3.3 配置log4j2-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration status="INFO" monitorInterval="30">
    <!-- 自己设置属性,后面通过${}来访问 -->
     <properties>
            <property name="LOG_HOME">/springboot-demos/spring-logs</property>
        </properties>
    <appenders>
        <!--Appender 1. 输出到Console控制台,指定输出格式和过滤器等级为INFO -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--ThresholdFilter指定日志消息的输出最低层次-->
            <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} ==== > %L %M - %msg%xEx%n"/>
        </Console>

        <!--Appender 2. 输出到滚动保存的文件, 触发保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志-->
        <File name="allLog" fileName="${LOG_HOME}/all.log">
            <ThresholdFilter level="ALL" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </File>


        <!--Appender 3. 输出到滚动保存的文件, 触发保存日志文件的条件是日志文件大于3KB,只保存最新的10个日志-->
        <RollingFile name="debugLog" fileName="${LOG_HOME}/debug.log" filePattern="${log.path}/debug-%i.log">
            <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%F:%L] - %m%n"/>
            <SizeBasedTriggeringPolicy size="3KB"/>
            <!-- DefaultRolloverStrategy 中的参数max,可以限制 SizeBasedTriggeringPolicy中size超出后,只保留max个存档-->
            <DefaultRolloverStrategy max="10"/>
        </RollingFile>

        <!--Appender 4. 输出到滚动保存的文件, 触发保存日志文件的条件是每分钟第一次的日志事件。ERROR日志是按分钟产生日志 -->
        <RollingFile name="errorLog" fileName="${LOG_HOME}/error.log"
                     filePattern="${log.path}/error-%d{yyyy-MM-dd_HH-mm}.log">
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout charset="UTF-8" pattern="[%-5level][%d{yyyy-MM-dd HH:mm:ss}][%C:%F:%L] - %m%n"/>
            <TimeBasedTriggeringPolicy/>
        </RollingFile>

        <RollingFile name="RollingFile" fileName="${LOG_HOME}/rar.log"
                     filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout charset="UTF-8" pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <!--日志文件最大值 第二天压缩-->
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            </Policies>
        </RollingFile>


    </appenders>
    <!--root 默认加载-->
    <loggers>
        <root level="INFO">
            <appender-ref ref="Console"/>
            <!--<appender-ref ref="allLog"/>-->
            <!--<appender-ref ref="debugLog"/>-->
            <!--<appender-ref ref="errorLog"/>-->
            <!--<appender-ref ref="RollingFile"/>-->
        </root>
    </loggers>
</configuration>

2.3.4 测试

测试代码如下:

import com.eckey.Log4jApp;
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(classes = Log4jApp.class)
public class LogTest {

    Logger log =  LoggerFactory.getLogger(getClass());

    @Test
    public void test1() {
        log.trace("hello trace");
        log.info("hello info ");
        log.debug("hello debug");
        log.warn("hello warn");
        log.error("hello error");
    }
}

测试结果如下:
在这里插入图片描述
本节展示了利用log4j2替换logback,这种情况在实际开发中极少发生,因为logback本身性能就优于log4j2,且与slf4j更加适配。这里仅做案例分析而已。

3.小结

1.SpringBoot底层日志框架默认使用的是SLF4J+logback,Spring框架底层默认使用的日志框架是common-logging;
2.SLF4J+logback是目前较为流行的日志框架记录模式,原因是因为logback日志框架性能优良,内存占用小;
3.SLF4J是一个出色的门面日志,它的出现让人只需关注对具体的日志接口调用即可,底层具体的实现框架可以进行任意适配,具有较高的灵活性;
4.对与其它日志框架想要转换为SLF4J框架输出方式,可以调用对应的适配包进行转换。

4.参考文献

1.https://www.bilibili.com/video/BV1sb411H7Po?p=3
2.https://www.slf4j.org/legacy.html
3.https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/spring-boot-features.html#boot-features-logging-file-output

5.附录

1.https://gitee.com/Marinc/log4j-demos

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值