欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

在Spring Boot项目中,如何将Logback的日志输出至控制台或文件

最编程 2024-02-23 21:50:16
...

这篇文章给大家介绍springboot项目使用日志工具Logback把日志输出到控制台,输出到文件的具体方法;介绍了Logback的xml配置文件中各个标签的具体内容;列出了常见的配置文件内容。

Logback简介:Logback是一个开源的日志组件,师出同门,与log4j一样,logback也是由Ceki Gülcü开发的开源日志组件,可以说是log4j的改进版;在现如今的项目中,logback的出现次数越来越多,是目前主流首选的日志记录工具。
logback分成三个模块:logback-core,logback- classic,logback-access(这个不常用)。

1.Logback日志组件各个模块的功能

  • logback-core:提供了LogBack的核心功能,是另外两个组件的基础。
  • logback-classic:实现了Slf4j的API,所以当想配合Slf4j使用时,需要引入logback-classic。

什么是Slf4j:简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用那个具体的日志系统。

  • logback-access:为了集成Servlet环境而准备的,可提供HTTP-access的日志接口。

2.Logback日志组件各个模块的maven依赖

  <!-- logback -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>${logback.version}</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>${logback.version}</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-access</artifactId>
        <version>${logback.version}</version>
    </dependency>

3.项目启动时Logback日志配置文件扫描规则

启动项目时,logback会按照如下顺序扫描配置文件:

  • 在系统配置文件System Properties中寻找是否有logback.configurationFile对应的value
  • 在classpath下寻找是否有logback.groovy(即logback支持groovy与xml两种配置方式)
  • 在classpath下寻找是否有logback-test.xml
  • 在classpath下寻找是否有logback.xml

以上任何一项找到了,就不进行后续扫描,按照对应的配置进行logback的初始化,可从控制台输出信息中查看加载的配置文件。

当所有以上四项都找不到的情况下,logback会调用ch.qos.logback.classic.BasicConfigurator的configure方法,构造一个ConsoleAppender用于向控制台输出日志,默认日志输出格式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n。

在Springboot项目中可以自定义logback配置文件名及文件位置


image.png

要想让Springboot项目识别到该logback配置文件,只需要在Springboot配置文件中定义好配置文件的加载路径即可如下所示:


image.png

4.Logback具体可实现的功能

  • 自动重新加载配置文件
    当配置文件修改了,Logback-classic能自动重新加载配置文件。扫描过程快且安全,它并不需要另外创建一个扫描线程。
  • 自动去除旧的日志文件
    通过设置TimeBasedRollingPolicy或者SizeAndTimeBasedFNATP的maxHistory属性,你可以控制已经产生日志文件的最大数量。如果设置maxHistory 12,那那些log文件超过12个月的都会被自动移除。
  • 堆栈树带有包版本
    Logback在打出堆栈树日志时,会带上包的数据。
  • 自动压缩已经打出来的log
    RollingFileAppender在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩是个异步过程,所以甚至对于大的日志文件,在压缩过程中应用不会受任何影响。

5.Logback配置文件详解

  1. 根节点<configuration>
<!-- 输出logback内部日志信息,每隔30s判断一下配置文件有没有更新,若更新,则重新加载 -->
<configuration scan="true" scanPeriod="30 seconds" debug="true">
    
<!-- 
    scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
    scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
  1. 设置变量<property>
<configuration>
    <!-- 定义日志文件的存储地址 -->
    <property name="LOG_HOME" value="log"/>
       <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
    <property name="LOG_PATTERN" value="%d{yyyyMMdd:HH:mm:ss.SSS} [%thread] %-5level  %msg%n"/>
</configuration>
<!--
name:变量的名称,可以随意起名,但建议名字要简明直译;
value:变量的值;
在配置文件中,我们可以用 ${} 的方式来使用,将变量引入到其他节点中去。
--> 
  1. <appender>

负责写日志的组件,有两个必要属性name和class

  • 常用的控制台输出:ConsoleAppender
<!-- name指定<appender>的名称
     class指定<appender>的全限定名 -->
<!-- 控制台输出 appender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <!-- <encoder>表示输出格式 -->
        <pattern>${LOG_PATTERN}</pattern>
        <!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
        <charset>utf8</charset>
    </encoder>
</appender>
  • 常用的滚动记录文件:RollingFileAppender
<!-- INFO日志 appender: 按照每天生成日志文件 -->
<appender name="INFO-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!-- 过滤器,只记录 info 级别以上的日志 -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    <!-- 写入的日志文件名,可以使相对目录也可以是绝对目录,如果上级目录不存在则自动创建 -->
    <file>${LOG_HOME}/iot-sdk-info.log</file>
    <!-- 如果为true表示日志被追加到文件结尾,如果是false表示清空文件 -->
    <append>true</append>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <!-- 日志文件输出的文件名: 
            %d:可以包含一个Java.text.SimpleDateFormat指定的时间格式
            %i:自增数字
        -->
        <fileNamePattern>${LOG_HOME}/iot-sdk-info.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
        <!-- 日志文件保存历史数量:控制保留的归档文件的最大数量,如果超出数量就删除旧文件 -->
        <maxHistory>30</maxHistory>
        <!-- 文件大小超过100MB归档 -->
        <maxFileSize>100MB</maxFileSize>
    </rollingPolicy>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${LOG_PATTERN}</pattern>
        <charset>utf8</charset>
    </encoder>
</appender>

4.<encoder>

encoder节点负责两件事情:

  • 把日志信息转换为字节数组
  • 把字节数组写到输出流

以下是一个常用配置:

<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 用来设置日志的输入格式,使用“%+转换符”的方式,如果要输出”%”则必须使用”\”进行转义。-->
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <!-- 格式化输出:https://logback.qos.ch/manual/layouts.html
             %d 表示日期,
             %thread 表示线程名,
             %level 日志级别从左显示5个字符宽度,
             %t 线程名
             %file:%line 文件名+行号,
             %m 日志消息,%n是换行符
             %X{traceId}:自定义设置的参数,后面会说。
        -->
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %t %file:%line %X{traceId} -| %m%n</pattern>
        <charset>utf8</charset>
    </encoder>
</appender>

5.<filter>

配合appender使用,<filter>是<appender>的一个子节点,表示在当前给到的日志级别下再进行一次过滤

  • Level 级别过滤器
    根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath和onMismatch接收(ACCEPT)或拒绝(DENY)日志。
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 只记录error级别的日志 -->
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
    </filter>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${LOG_PATTERN}</pattern>
        <charset>utf8</charset>
    </encoder>
</appender>
  • ThresholdFilter: 临界值过滤器
    将日志级别低于<level>的全部进行过滤。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 记录info级别以上的日志 -->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>INFO</level>
    </filter>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>${LOG_PATTERN}</pattern>
        <charset>utf8</charset>
    </encoder>
</appender>
  1. <logger>与<root>

<logger>用来设置某一个包或者具体某一个类的日志打印级别、以及指定appender。
<logger>可以包含零个或者多个<appender-ref>元素,标识这个appender将会添加到这个logger。
<root>也是<logger>元素,但它是根logger,只有一个level属性,因为它的name就是ROOT

<!-- name:必填,指定受此logger约束的某一个包或者具体的某一个类。这个name表示的是LoggerFactory.getLogger(XXX.class),XXX的包路径,包路径越少越是父级
     level:用来设置打印级别,五个常用打印级别从低至高依次为TRACE、DEBUG、INFO、WARN、ERROR,如果未设置此级别,那么当前logger会继承上级的级别
     additivity:是否向上级logger传递打印信息,默认为true -->
<logger name="com.example.iot" level="DEBUG" additivity="false">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="INFO-APPENDER"/>
    <appender-ref ref="ERROR-APPENDER"/>
</logger>

<!-- 没有配置<appender-ref>,表示此<logger>不会打印出任何信息 -->
<logger name="com.example.iot.demo1" level="DEBUG" additivity="true" />

<!-- 没有配置level,即继承父级的level,<logger>的父级为<root>,那么level=info -->
<logger name="com.example.iot.demo2" additivity="false" />

<!-- 控制台输出日志级别 -->
<root level="INFO">
    <appender-ref ref="STDOUT"/>
</root>

6.Logback日志常用配置

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

    <!-- 定义日志文件的存储地址 -->
    <property name="LOG_HOME" value="log"/>
    <!-- 格式化输出:
        %d          表示日期,
        %thread     表示线程名,
        %level      日志级别从左显示5个字符宽度,
        %thread     线程名
        %file:%line 文件名:行号,
        %m          日志消息,
        %n          换行符,
        %X{traceId} 自定义设置的参数
        %mdc        自定义参数
    -->
    <property name="LOG_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%5level) -- [%15.15t] %cyan(%-23.23logger{23}) : %m%n"/>


    <!-- appender是configuration的子节点,是负责写日志的组件 -->
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
            <!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- INFO日志 appender: 按照每天生成日志文件 -->
    <appender name="INFO-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,记录info级别以上的日志 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!-- 写入的日志文件名,可以使相对目录也可以是绝对目录,如果上级目录不存在则自动创建 -->
        <file>${LOG_HOME}/iot-sdk-info.log</file>
        <!-- 如果为true表示日志被追加到文件结尾,如果是false表示清空文件 -->
        <append>true</append>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件输出的文件名: %d可以包含一个Java.text.SimpleDateFormat指定的时间格式 -->
            <fileNamePattern>${LOG_HOME}/iot-sdk-info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 日志文件保存历史数量:控制保留的归档文件的最大数量,如果超出数量就删除旧文件 -->
            <maxHistory>30</maxHistory>
            <!-- 文件大小超过100MB归档 -->
            <maxFileSize>100MB</maxFileSize>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 错误日志 appender: 按照每天生成日志文件 -->
    <appender name="ERROR-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 过滤器,只记录error级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!-- 日志名称 -->
        <file>${LOG_HOME}/iot-sdk-error.log</file>
        <append>true</append>
        <!-- 每天生成一个日志文件,保存30天的日志文件 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 日志文件输出的文件名:按天回滚 daily -->
            <fileNamePattern>${LOG_HOME}/iot-sdk-error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 日志文件保留天数 -->
            <maxHistory>30</maxHistory>
            <!-- 文件大小超过100MB归档 -->
            <maxFileSize>100MB</maxFileSize>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- 指定项目中某个包,当有日志操作行为时的日志记录级别
        级别依次为【从高到低】:FATAL > ERROR > WARN > INFO > DEBUG > TRACE
        additivity=false 表示匹配之后,不再继续传递给其他的logger
    -->
    <logger name="com.example.iot" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="INFO-APPENDER"/>
        <appender-ref ref="ERROR-APPENDER"/>
    </logger>

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

7.Logback高级特性异步输出日志

之前的日志配置方式是基于同步的,每次日志输出到文件都会进行一次磁盘IO。采用异步写日志的方式而不让此次写日志发生磁盘IO,阻塞线程从而造成不必要的性能损耗。异步输出日志的方式很简单,添加一个基于异步写日志的appender,并指向原先配置的appender即可。
logback AsyncAppender 目前在logback 1.0.11及以上版本存在

示例:

<!-- 异步输出 -->
    <appender name="ASYNC-INFO-APPENDER" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>256</queueSize>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="INFO-APPENDER"/>
    </appender>

    <appender name="ASYNC-ERROR-APPENDER" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>256</queueSize>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="ERROR-APPENDER"/>
    </appender>

<!-- 设置开发环境日志级别为INFO(采用异步输出的方式) -->
<!-- springProfile 标签下面会讲 -->
<springProfile name="prod">
    <root level="INFO">
        <appender-ref ref="ASYNC-INFO-APPENDER"/>
    </root>
</springProfile>

8.Logback配置文件与Springboot配置文件的配合

1.springProfile标签,可实现根据不同的springboot配置文件进行不同级别的日志打印

该 <springProfile> 标签允许我们更加灵活配置文件,可选地包含或排除配置部分。元素中的任何位置均支持轮廓部分。使用该name属性指定哪个配置文件接受配置。可以使用逗号分隔列表指定多个配置文件。

<springProfile name="dev">
    <!-- 开发环境时激活 -->
</springProfile>

<springProfile name="dev,test">
    <!-- 开发,测试的时候激活-->
</springProfile>

<springProfile name="!prod">
    <!-- 当 "生产" 环境时,该配置不激活-->
</springProfile>
  • 案例
<!-- 开发环境日志级别为DEBUG -->
<springProfile name="dev">
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="INFO-APPENDER"/>
    </root>
</springProfile>

<!-- 测试环境日志级别为INFO -->
<springProfile name="test">
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="INFO-APPENDER"/>
    </root>

<!-- 生产环境日志级别为INFO -->
<springProfile name="prod">
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="INFO-APPENDER"/>
    </root>
</springProfile>

2.springProperty

  • 该 <springProperty> 标签允许我们从Spring中显示属性,Environment 以便在Logback中使用。如果你想将 application.properties在回读配置中访问文件中的值,这将非常有用。

  • 标签的工作方式与Logback的标准 <property> 标签类似,但不是直接value 指定source属性(从Environment)指定。scope 如果需要将属性存储在local范围之外的其他位置,则可以使用该属性。如果您需要一个后备值,以防该属性未设置,则Environment可以使用该defaultValue属性。

<!-- 从Spring环境变量中获取值, source属性是springboot配置文件中的key -->
<springProperty scope="context" name="LOGBACK_NAME" source="springboot.application.name" defaultValue="appName"/>

<!-- 应用 通过${name}引入-->
<appender name="EXAMPLE" class="ch.qos.logback.more.appenders.EXAMPLEAppender">
    <remoteHost>${LOGBACK_NAME}</remoteHost>
</appender>