Spring Boot中配置Logback

概述

Spring Boot 提供了开箱即用的日志功能logback,无需任何配置即可轻松使用logback。如果要提供自己的配置有两种方法,如果只需要简单的修改,可以将其添加到application.properties 等属性文件中;如果需要更复杂的配置,可以使用 XML 或 Groovy 来指定设置。在本教程中,我们将重点介绍使用 XML 定义自定义日志配置,并介绍一些基本操作。

Logback配置简介

在开始配置 Logback 之前,我们不妨先快速了解一下如何在类中向日志发送消息。

@Service
public class MyServiceImpl implements MyService {

  private static final Logger LOGGER = LoggerFactory.getLogger(MyServiceImpl.class);

  @Override
  public void doStuff(final String value) {
    LOGGER.trace("doStuff needed more information - {}", value);
    LOGGER.debug("doStuff needed to debug - {}", value);
    LOGGER.info("doStuff took input - {}", value);
    LOGGER.warn("doStuff needed to warn - {}", value);
    LOGGER.error("doStuff encountered an error with value - {}", value);
  }
}

LOGGER 允许使用代表各日志级别(trace, debug, info, warn, error)的方法将消息写入日志,随后写入消息。括号将由作为方法参数传递的值代替。

现在,我们可以从一个相对简单的示例开始了解 Logback 本身的配置。下面是 logback.xml 文件,它是 Logback 用来配置设置的文件之一。

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

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>
        %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n
      </pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="STDOUT"/>
  </root>

</configuration>

它会创建一个 ConsoleAppender 类的附加器,将日志信息输出到控制台,就像通常的 System.out.print 一样。日志信息将遵循一种模式,该模式包含一些符号,根据发送到日志记录器的信息,这些符号将被替换为生成的值。示例中包含了一些符号,下面将解释每个符号的作用。

创建的附加器将在根日志记录器中被引用。在上述示例中,日志级别被设置为 INFO(小写或大写均可)。这导致它只能输出日志级别为 INFO 或以上(INFO、WARN、ERROR)的信息。

Logback 中可用的日志记录级别有

回到上面显示的日志级别为 INFO 的代码段,只有级别为 INFO 或更高的(WARN 和 ERROR)信息才会输出到日志中。因此,如果我们调用 MyService.doStuff("value"),就会产生以下结果(在本示例和所有后续输出示例中,与 Spring 相关的日志已被删除)。

28-08-2017 13:32:18.549 [main] INFO  com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value
28-08-2017 13:32:18.549 [main] WARN  com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value
28-08-2017 13:32:18.549 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value

请注意,尽管 TRACE 和 DEBUG 级别的信息已发送到日志记录器,但由于它们低于 INFO 级别,因此并未显示。

如果您想在 application.properties 中编写与前面示例代码相同的代码,可以按如下方式进行。

logging.level.root=info
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n

以这种形式进行配置时,不需要 logback.xml 文件,而且正如你所看到的,配置会更简短,适用于更简单的设置。

使用 Spring Boot 时,会为 Logback 提供默认配置,当你添加自己的 logback.xml 时,该配置会被覆盖。如果希望包含 Spring Boot 的配置,可以在 标记内添加以下内容。

<include resource="org/springframework/boot/logging/logback/base.xml"/>

有关更多信息,请参考 Spring Boot 文档 - 配置 Logback 记录日志

自定义类日志记录器

如果您想将类的消息记录在与根级别不同的级别上,那么您可以为类定义自己的日志记录器。这样就可以为特定类设置日志记录级别,并指定该类独有的其他属性。以下是为单个类定义日志记录器的方法。

<logger name="com.lankydan.service.MyServiceImpl" level="debug">
  <appender-ref ref="STDOUT" />
</logger>

如果您继续运行这段代码,并且仍然定义了根日志记录器,那么它将产生以下输出。

27-08-2017 17:02:10.248 [main] DEBUG com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to debug - value
27-08-2017 17:02:10.248 [main] DEBUG com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to debug - value
27-08-2017 17:02:10.248 [main] INFO  com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value
27-08-2017 17:02:10.248 [main] INFO  com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value
27-08-2017 17:02:10.248 [main] WARN  com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value
27-08-2017 17:02:10.248 [main] WARN  com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value
27-08-2017 17:02:10.248 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value
27-08-2017 17:02:10.248 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value

如您所见,每条日志信息都生成了两次,这可能不是您想要的。要解决这个问题,需要使用 additivity="false。如果不使用 additivity="false,由于根日志附加器和类级别附加器都会向日志写入信息,因此会导致信息打印两次。即使根级别是 ERROR,将类级别设置为 DEBUG 也会在全局范围内覆盖它,并导致根应用程序也将 MyServiceImpl 类写入 DEBUG 级别。以下是包含此属性后的代码。

<logger name="com.lankydan.service.MyServiceImpl" additivity="false" level="debug">
  <appender-ref ref="STDOUT" />
</logger>

另一种解决方案是只为类设置日志级别,而不向日志中写入内容(因为没有定义附加器),这等同于上面的版本,但需要另一个日志附加器(在本例中为根附加器)正在向日志中写入内容,这样才能起作用

<logger name="com.lankydan.service.MyServiceImpl" level="debug"/>

使用这两种解决方案中的任何一种,日志输出就会恢复正常。

27-08-2017 16:30:47.818 [main] DEBUG com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to debug - value
27-08-2017 16:30:47.834 [main] INFO  com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value
27-08-2017 16:30:47.834 [main] WARN  com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value
27-08-2017 16:30:47.834 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value

类级日志记录也可通过在 application.properties 中定义。

logging.level.com.lankydan.service.MyServiceImpl=debug

同样也可以定义包级别的日志记录,使用包名替换上述定义的类名即可。

<logger name="com.lankydan.service" additivity="false" level="debug">
  <appender-ref ref="STDOUT" />
</logger>

类级日志记录也可通过在 application.properties 中定义。

logging.level.com.lankydan.service=debug

在配置文件中定义属性

可以在配置文件中定义属性,以便重复使用,这在需要标记日志输出文件夹时非常方便。

<property name="LOG_PATH" value="logs"/>

这个名为 LOG_PATH 的属性将在后续示例中使用,在实际项目中,这可能不是保存日志的最佳位置,本教程仅作为示例演示。LOG_PATH 是一个对 Spring Boot 默认日志设置非常重要的属性,但也可以创建任何名称的属性。通过添加 ${LOG_PATH} 可以在配置的其余部分中访问 LOG_PATH 的值。

保存日志文件的路径也可以通过 application.properties 实现。

logging.path=logs

application.properties 中也可以引用已经定义的变量:

propertyA=value
propertyB=${propertyA} # extra configuration if required

${propertyA} 将被属性 A 的值替换,从而允许属性 B 使用它。

将日志保存到文件

要将日志保存到文件,可以使用 FileAppender。这是一个简单的文件附加器,会将所有日志保存到一个单独的文件中,而该文件可能会变得非常大,因此您更有可能使用 RollingFileAppender,我们稍后将对此进行介绍。

<appender name="SAVE-TO-FILE" class="ch.qos.logback.core.FileAppender">

  <file>${LOG_PATH}/log.log</file>

  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <Pattern>
      %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
    </Pattern>
  </encoder>

</appender>

它的结构与 ConsoleAppender 大同小异,只是增加了一个文件名,用于保存日志信息。值得注意的是,我删除了保存到文件时添加到编码器模式中的颜色,因为其中包含的字符并不是用来显示的,而且会使日志文件变得杂乱无章。然后,就可以用与前面显示的 STDOUT 附加器相同的方式引用该附加器,以便实际使用。

<logger name="com.lankydan.service.MyServiceImpl" additivity="false" level="debug">
  <appender-ref ref="SAVE-TO-FILE"/>
</logger>

复杂的日志输出文件配置

RollingFileAppender 会根据不同的滚动策略将日志保存到不同的文件中。这很方便,因为它可以将日志输出分割成各种形式,由你来控制。例如,你可以根据日期将日志文件分开,这样你就可以查看过去在特定日期发生的错误;也可以根据文件大小分开,这样你就不需要在一个永无止境的大文件中搜索;还可以两者兼顾,根据日期和大小分开。

TimeBasedRollingPolicy 将根据日期创建新文件。下面的代码将每天创建一个新文件,并使用 %d 符号将日期附加到日志文件名中。%d 符号的格式很重要,因为可以从中推断出滚动时间段。下面的示例将每天滚动,但如果要按月滚动,可以使用不同的 %d{MM-yyyy} 格式,将日期中的日部分去掉。只要 %d 符号内的格式与 SimpleDateFormat 允许的格式一致,就可以使用不同的滚动时间段,而不仅仅是按日或按月推断的时间段。

<appender name="SAVE-TO-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

  <file>${LOG_PATH}/log.log</file>

  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <Pattern>
      %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
    </Pattern>
  </encoder>

  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>
      ${LOG_PATH}/archived/log_%d{dd-MM-yyyy}.log
    </fileNamePattern>
    <maxHistory>10</maxHistory>
    <totalSizeCap>100MB</totalSizeCap>
  </rollingPolicy>

</appender>

我在上述示例中列出了基于时间的滚动策略(TimeBasedRollingPolicy)的一些可用属性。 maxHistory 指定归档日志文件在自动删除前的保存时间。保存时间取决于文件名中指定的滚动时间段,因此在上述示例中,滚动时间段为每天,允许在删除归档日志前最多保存 10 天的时间。totalSizeCap 限制了所有归档日志文件的最大大小,它要求设置 maxHistory 属性,在删除归档文件时,maxHistory 优先于 totalSizeCap。

如果只根据文件大小进行滚动,则需要使用固定窗口滚动策略(FixedWindowRollingPolicy)和基于大小的触发策略(SizeBasedTriggeringPolicy)。在上一个示例中,日志在滚动时被保存到一个归档文件夹中,但在此策略中,我并没有将日志保存为归档文件夹,因为将日志分开主要是为了让它们更容易浏览,因为文件大小较小。

<appender name="SAVE-TO-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

  <file>${LOG_PATH}/log.log</file>

  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <Pattern>
      %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
    </Pattern>
  </encoder>

  <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    <fileNamePattern>
      ${LOG_PATH}/log_%i.log
    </fileNamePattern>
    <minIndex>2</minIndex>
    <maxIndex>3</maxIndex>
  </rollingPolicy>

  <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <maxFileSize>1KB</maxFileSize>
  </triggeringPolicy>

</appender>

FixedWindowRollingPolicy 中的 minIndex 和 maxIndex 这两个可选属性指定了 %i 在日志文件名中的最小值和最大值。因此,在上面的示例中,当日志被滚动时,它们的名称可以是 log_2.log 和 log_3.log(虽然从 2 开始是很奇怪的,只是为了清晰起见,通常会从 1 开始)。生成日志文件的过程如下(以上述代码片段为例):log.log 文件将接收所有新的日志输入,当达到 maxFileSize 时,log.log 将重命名为存档文件 log_2.log,并创建一个新的 log.log 文件,当新的log.log文件也达到maxFileSize时,所有日志文件都将重命名并移动(log_2.log重命名为log_3.log,log.log命名为log_2.log),同时再次创建一个新的 log.log 文件。如果没有设置 maxIndex,这个过程将继续,但如果设置了 maxIndex,则会在创建另一个存档文件时删除具有指定最大索引的日志文件(它包含最旧的信息)。按照上面的示例,这意味着应创建 log_4.log 时,会删除 log_3.log,所有其他日志也会相应重命名。

更多信息,可以参考Logback官方文档 - FixedWindowRollingPolicy

基于大小和时间的滚动策略(SizeAndTimeBasedRollingPolicy)采用了上述两个示例的部分内容,允许根据大小和时间进行滚动。请注意,它同时使用了 %d 和 %i 符号,在文件名中分别包含日期和日志编号。

<appender name="SAVE-TO-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">

    <file>${LOG_PATH}/log.log</file>

    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
      <Pattern>
        %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
      </Pattern>
    </encoder>

    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <fileNamePattern>
        ${LOG_PATH}/archived/log_%d{dd-MM-yyyy}_%i.log
      </fileNamePattern>
      <maxFileSize>10MB</maxFileSize>
      <maxHistory>10</maxHistory>
      <totalSizeCap>100MB</totalSizeCap>
    </rollingPolicy>

</appender>

正如你所看到的,它包含 maxFileSize、maxHistory 和 totalSizeCap,可以控制单个文件和文件集合的大小。因此,上述示例将保留 10 天的历史记录,分成 10MB 的文件,当所有文件的总大小达到 100MB 时,最旧的文件将被删除。

既然我们已经了解了如何定义可以输出到控制台或文件的附加器,那么我们就可以将它们组合起来,同时输出日志到文件和到控制台。只需在日志记录器中引用多个附加程序即可。

<logger name="com.lankydan.service.MyServiceImpl" additivity="false" level="debug">
  <appender-ref ref="STDOUT"/>
  <appender-ref ref="SAVE-TO-FILE"/>
</logger>

因此,现在这个日志记录器可以通过 STDOUT 附加器向控制台输出,也可以通过 SAVE-TO-FILE 附加器向文件输出。

通过 application.properties 也可以实现类似的配置。

logging.path=logs
logging.file=${logging.path}/log.log
logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n

如果不想向控制台输出日志,可以设置

logging.pattern.console=
logging.path=logs
logging.file=${logging.path}/log.log
logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n

根据不同环境指定配置文件

在使用 Logback 时,Spring Boot 提供了一个有用的功能,那就是在不同环境之间分离配置。因此,如果您想在开发环境中保存到文件并打印到控制台,而在生产环境中只打印到文件,那么这就可以轻松实现。

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

  <!-- config for STDOUT and SAVE-TO-FILE -->

  <springProfile name="dev">
    <root level="info">
      <appender-ref ref="STDOUT"/>
      <appender-ref ref="SAVE-TO-FILE"/>
    </root>
    <logger name="com.lankydan.service.MyServiceImpl" additivity="false" level="debug">
      <appender-ref ref="STDOUT"/>
      <appender-ref ref="SAVE-TO-FILE"/>
    </logger>
  </springProfile>

  <springProfile name="prod">
    <root level="info">
      <appender-ref ref="SAVE-TO-FILE"/>
    </root>
    <logger name="com.lankydan.service.MyServiceImpl" additivity="false" level="error">
      <appender-ref ref="SAVE-TO-FILE"/>
    </logger>
  </springProfile>

</configuration>

实现这一功能的第一步是将 logback.xml 文件重命名为 logback-spring.xml,以便使用 springProfile 标签。在该标签中,可以提供一个名称,该名称可通过属性、环境变量或虚拟机选项进行设置。下面是如何将 springProfile 名称设置为 dev,用来表示开发环境。

在应用程序属性中设置或作为环境变量设置

spring.profiles.active=dev

或作为虚拟机选项

-Dspring.profiles.active=dev

现在,当程序运行时,将使用开发版的 SpringProfile,从而将日志输出到控制台和文件。如果要将其推送到生产环境,则需要将属性设置为 prod,这样就能将配置更改为合适的配置,例如只将日志写入文件,并可能更改所有或某些类/包的日志级别。

类似的配置也可以通过 application.properties 提供。实际上不是 application.properties,而是 application-dev.properties 和 application-prod.properties 文件,它们是针对每个环境的独立属性文件。它们遵循 application-{environment}.properties 的命名约定,其中 {environment} 替换为环境名称。根据虚拟机选项或环境变量,可以选择其中之一,就像通过 logback-spring.xml 中的 springProfile 所做的那样。以下是上述代码片段的相应配置。

application-dev.properties

logging.level.root=info
logging.level.com.lankydan.service=debug
logging.path=logs
logging.file=${logging.path}/log.log
logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n

application-prod.properties

logging.level.root=info
logging.level.com.lankydan.service=error
logging.path=logs
logging.file=${logging.path}/log.log
logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
logging.pattern.console=

原文:

https://www.codingame.com/playgrounds/4497/configuring-logback-with-spring-boot