三、SpringBoot 日志篇

日志级别

几种常见的日志级别由低到高分为: TRACE < DEBUG < INFO < WARN < ERROR <FATAL 。

如何理解这个日志级别呢?很简单,如果项目中的日志级别设置为 INFO ,那么比它更低级别的日志信息就看不到了,即是 TRACE 、 DEBUG 日志将会不显示。


日志框架有哪些?

常见的日志框架有 log4j 、 logback 、 log4j2

log4j 这个日志框架显示是耳熟能详了,在 Spring 开发中是经常使用,但是据说log4j官方已经不再更新了,而且在性能上比logback 、 log4j2 差了很多。

logback 是由 log4j 创始人设计的另外一个开源日志框架,logback相比之于log4j性能提升了10%以上,初始化内存加载也更小了。作为的Spring Boot默认的日志框架肯定是有着不小的优势。

log4j2 晚于 logback 推出,官网介绍性能比 logback 高,但谁知道是不是王婆卖瓜自卖自夸,坊间流传,log4j2在很多思想理念上都是照抄logback,因此即便log4j2是Apache官方项目,Spring等许多框架项目没有将它纳入主流。

日志框架很多,究竟如何选择能够适应现在的项目开发,当然不是普通程序员考虑的,但是为了更高的追求,至少应该了解一下,哈哈。

Spring Boot 日志框架

Spring Boot默认的日志框架是 logback ,既然Spring Boot能够将其纳入的默认的日志系统,肯定是有一定的考量的,因此实际开发过程中还是不要更换。

原则上需要使用logback,需要添加以下依赖,但是既然是默认的日志框架,当然不用重新引入依赖了。

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-logging</artifactId>

Spring Boot中默认的日志级别是 INFO ,启动项目日志打印如下:

控制台日志
控制台日志

从上图可以看出,输出的日志的默认元素如下:

1. 时间日期:精确到毫秒

2. 日志级别:ERROR, WARN, INFO, DEBUG , TRACE

3. 进程ID

4. 分隔符:— 标识实际日志的开始

5. 线程名:方括号括起来(可能会截断控制台输出)

6. Logger名:通常使用源代码的类名

7. 日志内容

代码中如何使用日志?

在业务中肯定需要追溯日志,那么如何在自己的业务中输出日志呢?其实常用的有两种方式,下面一一介绍。

第一种其实也是很早之前常用的一种方式,只需要在代码添加如下:

private final Logger logger= 

LoggerFactory.getLogger(DemoApplicationTests.class);

这种方式显然比较鸡肋,如果每个类中都添加一下岂不是很low。别着急,lombok为我们解决了这个难题。第二种:要想使用lombok,需要添加如下依赖:

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

</dependency>

使用也是很简单,只需要在类上标注一个注解 @Slf4j 即可,如下:

@Slf4j

class DemoApplicationTests {

@Test

public void test(){

log.debug("输出DEBUG日志.......");

 }

}
如何定制日志级别?

Spring Boot中默认的日志级别是INFO,但是可以自己定制日志级别,如下:

logging.level.root=DEBUG

上面是将所有的日志的级别都改成了 DEBUG ,Spring Boot还支持 package 级别的日志级别调整,格式为: logging.level.xxx=xxx ,如下:

logging.level.com.example.demo=INFO

那么完整的配置如下:

logging.level.root=DEBUG

logging.level.com.example.demo=INFO
日志如何输出到文件中?

Spring Boot中日志默认是输出到控制台的,但是在生产环境中显示不可行的,因此需要配置日志输出到日志文件中。

其中有两个重要配置如下:

1.  logging.file.path  :指定日志文件的路径

2.  logging.file.name  :日志的文件名,默认为  spring.log

注意:官方文档说这两个属性不能同时配置,否则不生效,因此只需要配置一个即可。

指定输出的文件为当前项目路径的 logs 文件下,默认生成的日志文件为spring.log ,如下:

logging.file.path=./logs

日志文件中还有一些其他的属性,比如日志文件的最大size,保留几天的日志等等,下面会介绍到。

如何定制日志格式?

默认的日志格式在第一张图已经看到了,有时我们需要定制自己需要的日志输出格式,这样在排查日志的时候能够一目了然。

定制日志格式有两个配置,分别是控制台的输出格式和文件中的日志输出格式,如下:

1.  logging.pattern.console  :控制台的输出格式
2.  logging.pattern.file  :日志文件的输出格式

例如配置如下:

logging.pattern.console=%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n

logging.pattern.file=%d{yyyy/MM/dd-HH:mm} [%thread] %-5level %logger- %msg%n

上面的配置编码的含义如下:

%d{HH:mm:ss.SSS}——日志输出时间

%thread——输出日志的进程名字,这在Web应用以及异步任务处理中很有用

%-5level——日志级别,并且使用5个字符靠左对齐

%logger- ——日志输出者的名字

%msg——日志消息

%n——平台的换行符

如何自定义日志配置文件?

Spring Boot官方文档指出,根据不同的日志系统,可以按照如下的日志配置文件名就能够被正确加载,如下:

  1. Logback :logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
  2. Log4j :log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
  3. Log4j2 :log4j2-spring.xml, log4j2.xml
  4. JDK (Java Util Logging) :logging.properties

Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置。因此只需要在 src/resources 文件夹下创建 logback-spring.xml 即可,配置文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <property name="logPath" value="spring-boot-03-loging/logs"/><!-- 定义日志存放目录 -->
    <property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t-
%L] %-5level %logger{36} %L %M - %msg%xEx%n"/><!--   日志输出的格式-->
    <contextName>logback</contextName>

    <!--输出到控制台 ConsoleAppender-->
    <appender name="consoleLog"
              class="ch.qos.logback.core.ConsoleAppender">

        <!--展示格式 layout-->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>${PATTERN}</pattern>
        </layout>
        <!--过滤器,只有过滤到指定级别的日志信息才会输出,如果level为ERROR,那么控制台只会输出ERROR日志-->
        <!--
         <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
          </filter>
         -->
    </appender>

    <!--正常的日志文件,输出到文件中-->
    <appender name="fileDEBUGLog"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--如果只是想要 Info 级别的日志,只是过滤 info 还是会输出 Error 日志,因为 Error 的级别高,
        所以我们使用下面的策略,可以避免输出 Error 的日志-->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>Error</level><!--过滤 Error-->
            <onMatch>DENY</onMatch><!--匹配到就禁止-->
            <onMismatch>ACCEPT</onMismatch><!--没有匹配到就允许-->
        </filter>
        <!--日志名称,如果没有File 属性,那么只会使用FileNamePattern的文件
        路径规则如果同时有<File>和<FileNamePattern>,那么当天日志是<File>,明天
        会自动把今天的日志改名为今天的日期。即,<File> 的日志都是当天的。
        -->
        <File>${logPath}/log_demo.log</File>

        <!--滚动策略,按照时间滚动 TimeBasedRollingPolicy-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--文件路径,定义了日志的切分方式——把每一天的日志归档到一个文件中,以防止日志填满整个磁盘空间-->
            <FileNamePattern>${logPath}/log_demo_%d{yyyy-MM- dd}.log</FileNamePattern>
            <!--只保留最近90天的日志-->
            <maxHistory>90</maxHistory>
            <!--用来指定日志文件的上限大小,那么到了这个值,就会删除旧的日志-->
            <!--<totalSizeCap>1GB</totalSizeCap>-->
        </rollingPolicy>

        <!--日志输出编码格式化-->
        <encoder>
            <charset>UTF-8</charset>
            <pattern>${PATTERN}</pattern>
        </encoder>
    </appender>

    <!--指定最基础的日志输出级别-->
    <root level="DEBUG">
        <!--appender将会添加到这个loger-->
        <appender-ref ref="consoleLog"/>
        <appender-ref ref="fileDEBUGLog"/>
        <appender-ref ref="fileErrorLog"/>
    </root>
    <!--   定义指定package的日志级别-->
    <logger name="org.springframework" level="DEBUG"></logger>
    <logger name="org.mybatis" level="DEBUG"></logger>
    <logger name="java.sql.Connection" level="DEBUG"></logger>
    <logger name="java.sql.Statement" level="DEBUG"></logger>
    <logger name="java.sql.PreparedStatement" level="DEBUG"></logger>
    <logger name="io.lettuce.*" level="INFO"></logger>
    <logger name="io.netty.*" level="ERROR"></logger>
    <logger name="com.rabbitmq.*" level="DEBUG"></logger>
    <logger name="org.springframework.amqp.*" level="DEBUG"></logger>
    <logger name="org.springframework.scheduling.*" level="DEBUG"></logger>
    <!--定义com.xxx..xx..xx包下的日志信息不上传,直接输出到fileDEBUGLog和fileErrorLog这个两个appender中,
    日志级别为DEBUG-->
    <logger name="com.xxx.xxx.xx" additivity="false" level="DEBUG">
        <appender-ref ref="fileDEBUGLog"/>
        <appender-ref ref="fileErrorLog"/>
    </logger>
</configuration>

当然,如果就不想用SpringBoot推荐的名字,想自己定制也行,只需要在配置文件中指定配置文件名即可,如下:

logging.config=classpath:logging-config.xml

懵逼了,一堆配置什么意思?别着急,下面一一介绍。

日志配置文件节点属性介绍
configuration节点

这是一个根节点,其中的各个属性如下:

1.  scan  :当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。

2.  scanPeriod  :设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。

3.  debug  :当此属性设置为true时,将打印出logback内部日志信息,
实时查看logback运行状态。默认值为false。
root节点

这是一个必须节点,用来指定基础的日志级别,只有一个 level 属性,默认值是 DEBUG 该节点可以包含零个或者多个元素,子节点是 appender-ref ,标记这个 appender 将会添加到这个 logger 中。

contextName节点

标识一个上下文名称,默认为 default,一般用不到

property节点

标记一个上下文变量,属性有name和value,定义变量之后可以使用 ${} 来获取。

appender节点

用来格式化日志输出节点,有两个属性 name 和 class ,class用来指定哪种输出策略,常用就是控制台输出策略文件输出策略

这个节点很重要,通常的日志文件需要定义三个appender,分别是控制台输出常规日志文件输出异常日志文件输出

该节点有几个重要的子节点,如下:

1.  filter  :日志输出拦截器,没有特殊定制一般使用系统自带的即可,但是如果要将日志分开,比如将ERROR级别的日志输出到一个文件中,将除了  ERROR  级别的日志输出到另外一个文件中,此时就要拦截  ERROR  级别的日志了。

2.  encoder  : 和pattern节点组合用于具体输出的日志格式和编码方式。

3.  file  : 节点用来指明日志文件的输出位置,可以是绝对路径也可以是相对路径

4.  rollingPolicy  : 日志回滚策略,在这里我们用了TimeBasedRollingPolicy,基于时间的回滚策略,有以下子节点fileNamePattern,必要节点,可以用来设置指定时间的日志归档。

5.  maxHistory  : 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,,例如设置为30的话,则30天之后,旧的日志就会被删除

6.  totalSizeCap  : 可选节点,用来指定日志文件的上限大小,例如设置为3GB的话,那么到了这个值,就会删除旧的日志
logger节点

可选节点,用来具体指明包的日志输出级别,它将会覆盖root的输出级别。 该节点有几个重要的属性如下:

1.  name  :指定的包名

2.  level  :可选,日志的级别

3.  addtivity  :可选,默认为true,将此logger的信息向上级传递,将有root节点定义日志打印。如果设置为false,将不会上传,此时需要定义一个  appender-ref  节点才会输出。

日志框架切换

什么是日志门面?

前面介绍的日志框架都是基于日志门面 SLF4j 即简单日志门面(Simple Logging Facade for Java),SLF4j 并不是一个真正的日志实现,而是一个抽象层,它允许你在后台使用任意一个日志实现。

使用了 slf4j 后,对于应用程序来说,无论底层的日志框架如何变,应用程序不需要修改任意一行代码,就可以直接上线了。如果对 SLF4j 比较感兴趣的可以去官网看看:SLF4j 官网

如何做到无感知切换?

SLF4j 是日志门面,无论什么日志框架都是基于 SLF4j 的 API 实现,因此无论是代码打印日志还是 Lombok 注解形式打印日志,都要使用的 SLF4j 的 API,而 API ,这样才能解耦,做到无感知。因为最终切换的框架只是对 SLF4j的实现,并不是切换SLF4j。其实这一条在阿里开发手册中也是明确指出了,如下:


Spring Boot默认是Logback日志框架,如果需要切换到其他的日志框架应该如何做?

首先我们先看官网的一张图,一切都在图中,如下:

日志框架
日志框架

SLF4j 只是一个门面,共有两大特性。一是静态绑定、二是桥接。

什么是静态绑定?:我们以 log4j 为例。首先我们的 application 中会使用 slf4j 的 api 进行日志记录。我们引入适配层的 jar 包 slf4j-log412.jar 及底层日志框架实现 log4j.jar 。简单的说适配层做的事情就是把 slf4j 的 api 转化成 log4j 的 api。通过这样的方式来屏蔽底层框架实现细节。

什么是桥接?:比如你的 application 中使用了 slf4j ,并绑定了 logback 。但是项目中引入了一个 A.jar , A.jar 使用的日志框架是 log4j 。那么有没有方法让 slf4j 来接管这个 A.jar 包中使用 log4j 输出的日志呢?这就用到了桥接包。你只需要引入 log4j-over-slf4j.jar 并删除 log4j.jar 就可以实现 slf4j 对 A.jar 中 log4j 的接管.听起来有些不可思议。你可能会想如果删除 log4j.jar 那 A.jar 不会报编译错误嘛?答案是不会。因为 log4j-over-slf4j.jar 实现了 log4j 几乎所有 public 的 API 。但关键方法都被改写了。不再是简单的输出日志,而是将日志输出指令委托给 slf4j 。

下面就以 log4j2 为例,切换 Spring Boot 的日志框架为 log4j2 ,替换默认日志框架 Logback 。

引入依赖

Spring Boot 默认是 Logback 日志框架,如果想要切换 log4j2 肯定是要将 Logback 的依赖移除,只需要排除 web 模块中的日志场景启动器即可,如下:

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<!-- 去掉springboot默认日志框架logback -->

<exclusions>

<exclusion>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-logging</artifactId>

</exclusion>

</exclusions>

</dependency>
排除默认的 logback 依赖

肯定是需要引入 log4j2 的依赖,其实 log4j2 为了与 Spring Boot 适配也做了个启动器,不需要在引入其他的jar包了,只需要添加如下依赖即可:

<!-- 引入log4j2依赖 -->

<dependency> 

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-log4j2</artifactId>

</dependency>
指定配置文件

Spring Boot 官方文档已经给出了默认两个 log4j2 的配置的名称,分别为:log4j2-spring.xml , log4j2.xml ,但是建议使用 log4j2-spring.xml ,因为 Spring Boot 会做一些扩展,行吧,就整这个放在 src/resources 文件夹下即可。

另外如果不使用默认的配置名称,则需要在 application.properties 指定配置文件,如下:

logging.config=classpath:logging-config.xml
日志配置文件如何配置?

其实 log4j2 的一些配置和 logback 很相似,这里就不再一一介绍,有兴趣的可以去官网查查,直接贴出一些即用的配置,如下:

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

<!--Configuration 后面的 status,这个用于设置 log4j2 自身内部的信息输出,可以不设置,当设置成 trace 时,你会看到 log4j2 内部各种详细输出-->

<!--monitorInterval:Log4j 能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration monitorInterval="5">
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->


<!--变量配置-->
<Properties>

    <!-- 格式化输出:%date 表示日期,%thread 表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
    <!-- %logger{36} 表示 Logger 名字最长36个字符 -->

    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS}[%thread] %-5level %logger{36} - %msg%n" />

    <!-- 定义日志存储的路径 -->

    <property name="FILE_PATH" value="更换为你的日志路径" />

    <property name="FILE_NAME" value="更换为你的项目名" />

</Properties>

<appenders>

<console name="Console" target="SYSTEM_OUT">

<!--输出日志的格式-->

<PatternLayout pattern="${LOG_PATTERN}"/>

<!--控制台只输出level及其以上级别的信息(onMatch),其他的直接

拒绝(onMismatch)-->

<ThresholdFilter level="info" onMatch="ACCEPT" 

onMismatch="DENY"/>

</console>

<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由

append属性决定,适合临时测试用-->

<File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">

<PatternLayout pattern="${LOG_PATTERN}"/>

</File>
<!-- 这个会打印出所有的 info 及以下级别的信息,每次大小超过 size,则这 size 大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->

<RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/info.log" 

filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-

dd}_%i.log.gz">

<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒

绝(onMismatch)-->

<ThresholdFilter level="info" onMatch="ACCEPT" 

onMismatch="DENY"/>

<PatternLayout pattern="${LOG_PATTERN}"/>

<Policies>

<!--interval属性用来指定多久滚动一次,默认是1 hour-->

<TimeBasedTriggeringPolicy interval="1"/>

<SizeBasedTriggeringPolicy size="10MB"/>

</Policies>

<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹

下7个文件开始覆盖-->

<DefaultRolloverStrategy max="15"/>

</RollingFile>

<!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则

这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为

存档-->

<RollingFile name="RollingFileWarn" 

fileName="${FILE_PATH}/warn.log" 

filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-

dd}_%i.log.gz">

<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒

绝(onMismatch)-->

<ThresholdFilter level="warn" onMatch="ACCEPT" 

onMismatch="DENY"/>

<PatternLayout pattern="${LOG_PATTERN}"/>

<Policies>

<!--interval属性用来指定多久滚动一次,默认是1 hour-->

<TimeBasedTriggeringPolicy interval="1"/>

<SizeBasedTriggeringPolicy size="10MB"/>

</Policies>

<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹

下7个文件开始覆盖-->

<DefaultRolloverStrategy max="15"/>

</RollingFile>

<!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则

这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为

存档-->

 

<RollingFile name="RollingFileError" 

fileName="${FILE_PATH}/error.log" 

filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-

dd}_%i.log.gz">

<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒

绝(onMismatch)-->

<ThresholdFilter level="error" onMatch="ACCEPT" 

onMismatch="DENY"/>

<PatternLayout pattern="${LOG_PATTERN}"/>

<Policies>

<!--interval属性用来指定多久滚动一次,默认是1 hour-->

<TimeBasedTriggeringPolicy interval="1"/>

<SizeBasedTriggeringPolicy size="10MB"/>

</Policies>

<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹

下7个文件开始覆盖-->

<DefaultRolloverStrategy max="15"/>

</RollingFile>

</appenders>

<!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不

同的日志级别等。-->

<!--然后定义loggers,只有定义了logger并引入的appender,appender才会

生效-->

<loggers>

<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->

<logger name="org.mybatis" level="info" additivity="false">

<AppenderRef ref="Console"/>

</logger>

<!--监控系统信息-->

<!--若是additivity设为false,则 子Logger 只会在自己的appender里输

出,而不会在 父Logger 的appender里输出。-->

<Logger name="org.springframework" level="info" additivity="false">

<AppenderRef ref="Console"/>

</Logger>

<root level="info">

<appender-ref ref="Console"/>

<appender-ref ref="Filelog"/>

<appender-ref ref="RollingFileInfo"/>

<appender-ref ref="RollingFileWarn"/>

<appender-ref ref="RollingFileError"/>

</root>

</loggers>

</configuration>

 

上面的配置中如果需要使用的话,需要改掉全局变量中的日志路径和项目名

称,如下部分:

<property name="FILE_PATH" value="更换为你的日志路径" />

<property name="FILE_NAME" value="更换为你的项目名" />
posted @ 2020-12-03 20:48  星命定轨  阅读(438)  评论(0编辑  收藏  举报