Logback

基于 Logback 的 Java 日志处理

Logback 是一个日志框架,它与 Log4j 可以说是同出一源,都出自 Ceki Gülcü 之手。Slf4j 的全称是 Simple Logging Facade for Java,即简单日志门面;实现了日志框架一些通用的接口,结合日志框架一起使用,最终日志的格式、记录级别、输出方式等都是通过绑定具体的日志框架实现的。

Logback 主要由 logback-core, logback-classic, logback-access 三个模块组成,logback-core 是其它模块的基础设施,提供了一些关键的通用机制。logback-classic 的地位和作用等同于 Log4J,它也被认为是 Log4J 的一个改进版,并且它实现了简单日志门面 Slf4j;而 logback-access 主要作为一个与 Servlet 容器交互的模块,比如说 tomcat 或者 jetty,提供一些与 HTTP 访问相关的功能。

基础配置

我们可以通过 logback-spring.xml 来配置 Logback,基础配置如下:

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 引入 Spring Boot 基础配置 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 定义变量 -->
    <property name="APP_NAME" value="test"/>
    <property name="LOG_PATH" value="${user.home}/${APP_NAME}/logs"/>
    <property name="LOG_FILE" value="${LOG_PATH}/application.log"/>

    <!-- 日志输出级别变量 @see application-xxx.properties-->
    <springProperty scope="context" name="logLevel" source="log.level"/>

    <appender>
        // xxxx
    </appender>

    <logger>
        // xxxx
    </logger>

    <root level="${logLevel}">
        // xxxx
    </root>
</configuration>

property 用来定义变量值的标签,property 标签有两个属性,name 和 value;其中 name 的值是变量的名称,value 的值时变量定义的值。通过 property 定义的值会被插入到 logger 上下文中。定义变量后,可以使 ${name} 来使用变量。

<included>
	<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
	<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
	<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
	<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
	<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

	<appender name="DEBUG_LEVEL_REMAPPER" class="org.springframework.boot.logging.logback.LevelRemappingAppender">
		<destinationLogger>org.springframework.boot</destinationLogger>
	</appender>

	<logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
	...
	<logger name="org.springframework.boot.actuate.endpoint.jmx" additivity="false">
		<appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
	</logger>
	<logger name="org.thymeleaf" additivity="false">
		<appender-ref ref="DEBUG_LEVEL_REMAPPER"/>
	</logger>
</included>

然后在 Java 代码中可以通过 Slf4j 来获取到日志记录器对象:

// 通过 Slf4j LoggerFactory 获取 logger 对象
private static final Logger logger = LoggerFactory.getLogger(DemoTest.class);

// 通过 Slf4j 注解注入
@Slf4j
logger.info("info")

Appender

Appender 是一个日志打印的组件,这里组件里面定义了打印过滤的条件、打印输出方式、滚动策略、编码方式、打印格式等等。但是它仅仅是一个打印组件,如果我们不使用一个 logger 或者 root 的 appender-ref 指定某个具体的 appender 时,它就没有什么意义。上文定义的 root 本质上是根 logger,只不过 root 中不能有 name 和 additivity 属性,是有一个 level。

Appender 主要包含以下三类:

  • ConsoleAppender:把日志添加到控制台
  • FileAppender:把日志添加到文件
  • RollingFileAppender:滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件。它是 FileAppender 的子类

ConsoleAppender

最常用的日志输出就是输出到 Console 中,我们可以定义 Console 专用的 Appender:

<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
    <pattern>logback: %d {HH:mm:ss.SSS} %logger{36} - %M - %msg%n</pattern>
    <!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n</pattern>-->
    <!-- 使用 Spring Boot 预定义的 Pattern -->
    <!--<pattern>${CONSOLE_LOG_PATTERN}</pattern>-->
  </encoder>
</appender>

<root level="${logLevel}">
    <appender-ref ref="console"/>
</root>

RollingFileAppender

<appender name="APPLICATION"
          class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_FILE}</file>
    <encoder>
        <pattern>${FILE_LOG_PATTERN}</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxHistory>7</maxHistory>
        <maxFileSize>50MB</maxFileSize>
        <totalSizeCap>20GB</totalSizeCap>
    </rollingPolicy>
</appender>

其中 rollingPolicy 子标签用来描述滚动策略,仅在 RollingFileAppender 中才需要配置。而该标签中常用的滚动策略是 TimeBasedRollingPolicy,它根据时间来制定滚动策略,既负责滚动也负责触发滚动。其他还有,FixedWindowRollingPolicy 根据固定窗口算法重命名文件的滚动策略。

Filter

在真实场景下,我们可能会需要将不同级别的日志输出到不同的文件中,在不引入新的 Logger 的情况下,我们可以通过 Filter 来进行过滤:

filter 其实是 appender 里面的子元素。它作为过滤器存在,执行一个过滤器会有返回 DENY,NEUTRAL,ACCEPT 三个枚举值中的一个。

  • DENY:日志将立即被抛弃不再经过其他过滤器
  • NEUTRAL:有序列表里的下个过滤器过接着处理日志
  • ACCEPT:日志会被立即处理,不再经过剩余过滤器
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <!--要拦截的日志级别-->
        <level>ERROR</level>
        <!--如果匹配,则禁止-->
        <onMatch>DENY</onMatch>
        <!--如果不匹配,则允许记录-->
        <onMismatch>ACCEPT</onMismatch>
    </filter>
    <encoder>
        <pattern>%d -- %msg%n</pattern>
    </encoder>
    <!--滚动策略-->
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--路径-->
        <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    </rollingPolicy>
</appender>

<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--添加 范围 过滤-->
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>ERROR</level>
    </filter>
    <encoder>
        <pattern>%d -- %msg%n</pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
    </rollingPolicy>
</appender>

<root level="info">
    <appender-ref ref="fileInfoLog"/>
    <appender-ref ref="fileErrorLog"/>
</root>

其他的常用的 Filter 还有 ThresholdFilter 与 LevelFilter:

  • ThresholdFilter: 临界值过滤器,过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时,过滤器返回 NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

  • LevelFilter: 级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据 onMath(用于配置符合过滤条件的操作) 和 onMismatch(用于配置不符合过滤条件的操作)接收或拒绝日志。

Logger

我们可以通过自定义 Logger,来关联不同的包或者 Appender:

<logger name="wx.spring.boot.controller"
        level="${logging.level}" additivity="false">
    <appender-ref ref="console" />
</logger>

上面的这个配置文件描述的是:wx.spring.boot.controller 这个包下的 ${logging.level} 级别的日志将会使用 console 来打印;logger 有三个属性和一个子标签:

  • name: 用来指定受此 logger 约束的某一个包或者具体的某一个类。
  • level: 用来设置打印级别(TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF),还有一个值 INHERITED 或者同义词 NULL,代表强制执行上级的级别。如果没有设置此属性,那么当前 logger 将会继承上级的级别。
  • addtivity: 用来描述是否向上级 logger 传递打印信息。默认是 true。
<!-- fileControllerLog-->
<appender name="fileControllerLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${APP_LOG_FILE}</file>
     <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
        <level>${logging.level}</level>
    </filter>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} %t %logger{0} %m%n</pattern>
        <charset>utf8</charset>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${APP_LOG_FILE}.log.%d{yyyy-MM-dd}</fileNamePattern>
        <maxHistory>10</maxHistory>
    </rollingPolicy>
</appender>

<!--此logger约束将.controller 包下的日志输出到 fileControllerLog-->
<logger name="wx.spring.boot.controller" level="${logging.level}" additivity="false">
    <appender-ref ref="fileControllerLog" />
    <appender-ref ref="fileErrorLog" />
</logger>

<!--此logger约束将.service 包下的日志输出到 fileServiceLog-->
<logger name="wx.spring.boot.service" level="${logging.level}" additivity="false">
    <appender-ref ref="fileServiceLog" />
    <appender-ref ref="fileErrorLog" />
</logger>

我们也可以将日志维度固定到某个具体的类:

<!--这里指定到了具体的某一个类-->
<logger name="wx.spring.boot.task.TestLogTask" level="${logging.level}" additivity="true">
        <appender-ref ref="SCHEDULERTASKLOCK-APPENDER" />
        <appender-ref ref="fileErrorLog" />
    </logger>

或者自定义 logger 名称:

<logger name="dependency" level="info" additivity="false">
    <appender-ref ref="dependencyAppender" />
</logger>

然后在代码中显式获取:

private static Logger logger = LoggerFactory.getLogger("dependency");

如果我们希望打印出 MyBatis 的 SQL 语句,则在 logback-spring.xml 中添加如下配置:

 <!-- 将sql语句输出到具体的日志文件中 -->
<logger name="wx.dao" level="${logging.sql.level}" additivity="false">
    <appender-ref ref="sqlAppender"/>
</logger>

Links

上一页
下一页