跳至主要內容

Logback配置

Jin大约 18 分钟

Logback配置

1、初始化时的配置

将日志请求插入应用程序代码需要大量的规划和努力。观察显示,大约 4% 的代码专用于日志记录。因此,即使是中等规模的应用程序也会在其代码中嵌入数千条日志语句。考虑到它们的数量,我们需要工具来管理这些日志语句。

Logback 可以通过编程方式配置,也可以使用以 XML、Groovy 或序列化模型格式表示的配置脚本进行配置。

1.1、初始化步骤

  1. logback 会在类路径下寻找名为 logback-test.xml 的文件。
  2. 如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
  3. 如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
  4. 如果没有找到,将会通过 JDK 提供的 ServiceLoaderopen in new window 工具在类路径下寻找文件 META-INFO/services/ch.qos.logback.classic.spi.Configurator,该文件的内容为实现了 Configuratoropen in new window 接口的实现类的全限定类名。
  5. 如果以上都没有成功,logback 会通过 BasicConfiguratoropen in new window 为自己进行配置,并且日志将会全部在控制台打印出来。

logback-test.xml放在src/test/resources文件夹下,Maven 将确保它不会包含在生成的工件中。因此,您可以在测试期间使用不同的配置文件logback-test.xml

在生产中使用另一个文件logback.xml。

1.2、使用logback-test.xml或者 logback.xml进行配置

Logback-test.xml放在src/test/resources文件夹下

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

java案例

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloJin {
    public static void main(String[] args) {

        Logger logger = LoggerFactory.getLogger(HelloJin.class);
        logger.trace("hello Jin, and test {}", "trace");
        logger.debug("hello Jin, and test {}", "debug");
        logger.info("hello Jin, and test {}", "info");
        logger.warn("hello Jin, and test {}", "warn");
        logger.error("hello Jin, and test {}", "error");

    }
}

输出

17:46:22.111 [main] DEBUG com.jin.HelloJin -- hello Jin, and test debug
17:46:22.111 [main] INFO  com.jin.HelloJin -- hello Jin, and test info
17:46:22.112 [main] WARN  com.jin.HelloJin -- hello Jin, and test warn
17:46:22.112 [main] ERROR com.jin.HelloJin -- hello Jin, and test error

2、配置结构和语法

参考文档:https://logback.qos.ch/manual/configuration.html#syntax

配置文件的最基本结构可以描述为<configuration>元素,包含零个或多个<appender>元素,后跟零个或多个<logger>元素,后跟最多一个<root>元素。下图说明了这个基本结构。

basicSyntax
basicSyntax

2.1、标签名称大小写

在 logback 版本 0.9.17 之后,显示规定的标签名不区分大小写。例如:<logger><Logger<LOGGER> 这些都是有效的标签名。xml 风格的规则仍然适用。如果你有一个开始标签为 <xyz>,那么必须要有一个结束标签 </xyz></XyZ> 则是错误的。根据默认规则open in new window,标签名字是大小写敏感的,除了第一个字母。所以,<xyz><Xyz> 是一样的,但是 <xYz> 是错误的。默认规则遵循驼峰命名法。很难说清楚一个标签遵循什么规则,如果你不知道给定的标签遵循哪种规则,那么使用驼峰命名法总是正确的。

2.2、修改后自动重新加载配置文件

<configuration scan="true" scanPeriod="30 seconds" >
  ...
</configuration> 

如果没有指定时间单位,则时间单位将假定为毫秒,这通常是不合适的。如果更改默认扫描周期,请不要忘记指定时间单位。

默认情况下,每分钟会扫描一次配置文件的更改。您可以通过设置元素的 scanPeriod属性来指定不同的扫描周期。值可以以毫秒、秒、分钟或小时为单位指定。以下是示例:

3、记录器<logger> 配置

此时你应该至少对级别继承和基本选择规则有所了解。否则,除非你是一位埃及古物学家,否则 logback 配置对你来说就和象形文字一样毫无意义。

3.1、属性

  1. name (必填)
  2. level (可选)
    • 值为:TRACE、DEBUG、INFO、WARN、 ERROR、ALL 或 OFF,不区分大小写
    • 特殊的不区分大小写值INHERITED或其同义词NULL将强制从层次结构中的更高级别继承记录器的级别。如果您设置了记录器的级别,后来又决定它应该继承其级别,那么这将非常方便。
  3. additivity (可选,值为 true或false)

3.2、元素

  1. <appender-ref>
    • <logger>可能包含零个或多个 <appender-ref>元素;每个引用的附加器都会添加到命名的记录器中

请注意,与 log4j 不同,在配置给定的记录器时,logback-classic 不会关闭或删除任何先前引用的附加器。

3.3、示例参考

    <logger name="com.jin.hello" level='info' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

4、根记录器或<root>配置

4.1、属性

  1. level
    • <root>元素配置根记录器。它支持单个属性,即 level 属性。它不允许任何其他属性,因为可加性标志不适用于根记录器。此外,由于根记录器已命名为“ROOT”,因此它也不允许 name 属性。level 属性的值可以是不区分大小写的字符串 TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF 之一。请注意,根记录器的级别不能设置为 INHERITED 或 NULL。

4.2、元素

  1. <appender-ref>
    • <logger>元素类似, <root>元素可以包含零个或多个 <appender-ref>元素;每个引用的附加器都会添加到根记录器中。

请注意,与 log4j 不同,logback-classic 在配置根记录器时不会关闭或删除任何先前引用的附加器。

4.3、示例参考

设置记录器或根记录器的级别非常简单,只需声明并设置其级别即可

<configuration>

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>${CONSOLE_LOG_PATTERN:-%clr(%d{HH:mm:ss.SSS}){red} %clr(${LOG_LEVEL_PATTERN:-%-5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%-15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}</pattern>
    </encoder>
  </appender>

    <logger name="com.jin.hello" level='info' additivity="true">
        <appender-ref ref="CONSOLE"/>
    </logger>
    
  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>

</configuration>

4.4、案例测试

java代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloJin {
    Logger logger = LoggerFactory.getLogger(HelloJin.class);

    @GetMapping("hello")
    public void jinTest(){
        logger.trace("hello Jin, and test {}", "trace");
        logger.debug("hello Jin, and test {}", "debug");
        logger.info("hello Jin, and test {}", "info");
        logger.warn("hello Jin, and test {}", "warn");
        logger.error("hello Jin, and test {}", "error");
    }
}

输出

18:34:11.242 INFO  14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test info
18:34:11.242 INFO  14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test info
18:34:11.242 WARN  14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test warn
18:34:11.242 WARN  14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test warn
18:34:11.242 ERROR 14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test error
18:34:11.242 ERROR 14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test error

4.5、additivity :覆盖默认累积行为

在 2.3.4 案例测试中发现存在重复数据,只需要将记录器com.jin.hello 的 additivity 属性改为 false

    <logger name="com.jin.hello" level='info' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

输出

18:35:51.248 INFO  14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test info
18:35:51.248 WARN  14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test warn
18:35:51.248 ERROR 14704 --- [train-member] [nio-8082-exec-1] com.jin.hello.HelloJin      : hello Jin, and test error

如果 com.jin.hello 包下的 类 由于日志级别低于 info 问题无法输出

可以新增记录器com.jin.hello.HelloJin ,指定日志级别为 debug

    <logger name="com.jin.hello" level='info' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>
    
     <logger name="com.jin.hello.HelloJin" level='debug' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

4.6、记录器和根记录器 日志级别传递

记录器名称指定级别有效级别
rootDEBUGDEBUG
com.jin.helloINFOINFO
com.jin.hello.HelloJinnullINFO
com.jin.hello.HelloDEBUGDEBUG

5、 附加器 Appender 配置

附加器<appender> ,该元素具有两个必需属性 name 和 class。 name 属性指定附加器的名称,而 class 属性指定要实例化的附加器类的完全限定名称。元素<appender>可以包含零个或一个<layout>元素,零个或多个 <encoder>元素和零个或多个 <filter>元素。除了这三个公共元素之外,元素还可以包含任意数量的元素,这些元素对应于附加器类的 JavaBean 属性。无缝支持给定 logback 组件的任何属性是Joran<appender>的主要优势之一,这将在后面的章节中讨论。下图说明了通用结构。请注意,下图未显示对属性的支持。

appenderSyntax
appenderSyntax

5.1、属性

  1. name(必要)
    • name 属性指定附加器的名称
  2. class(必要)
    • class 属性指定要实例化的附加器类的完全限定名称

5.2、元素

  1. <layout>元素(零个或一个)
  2. <encoder>元素(零个或多个)
  3. <filter>元素(零个或多个)

5.3、附加器配置示例

    <appender name="HTML-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="ch.qos.logback.classic.html.HTMLLayout">
                <pattern>%date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n</pattern>
            </layout>
            <charset>UTF-8</charset>
        </encoder>
        <file>${LOG_FILE_PATH}/info.html</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE_PATH}/info-%d{yyyy-MM-dd}.%i.html</fileNamePattern>
            <cleanHistoryOnStart>false</cleanHistoryOnStart>
            <maxFileSize>10MB</maxFileSize>
            <totalSizeCap>0</totalSizeCap>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

pattern 解析

 <!-- 输出日期、级别、线程名称、记录器名称、文件名和日志请求所在的行号、消息和行分隔符的编码器 -->
<pattern>%date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n</pattern>

5.4、Appender 累积

默认情况下,附加器是累积的:记录器将记录到附加到其自身的附加器(如果有)以及附加到其祖先的所有附加器。因此,将同一个附加器附加到多个记录器将导致记录输出重复。

    <logger name="com.jin.hello" level='DEBUG'>
        <appender-ref ref="CONSOLE"/>
    </logger>
    
  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>

CONSOLE 的附加程序 附加到两个记录器,即 root 和 com.jin.hello,由于 root 记录器是所有记录器的祖先,而com.jin.hello是com.jin.hello.Hello 和 com.jin.hello.HelloJin 的父级,因此使用这两个记录器发出的每个日志请求都将输出两次,一次是因为STDOUT 附加到com.jin.hello,一次是因为它附加到 root。

Appender 可加性并非旨在吸引新用户。它是一种非常方便的 logback 功能。例如,您可以配置日志记录,使日志消息显示在控制台上(对于系统中的所有记录器),而只有来自某些特定记录器的消息才会流入特定的附加器。

5.5、覆盖默认累积行为

如果默认累积行为不适合您的需求,您可以通过将可加性标志设置为 false 来覆盖它。因此,记录器树中的分支可能会将输出定向到一组与树其余部分的附加程序不同的附加程序。

additivity="false"

    <logger name="com.jin.train.member.hello" level='info' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

6、设置上下文名称

每个记录器都附加到记录器上下文。默认情况下,记录器上下文称为“默认”。但是,您可以借助配置指令设置不同的名称<contextName>。请注意,一旦设置,记录器上下文名称就无法更改。设置上下文名称是一种简单直接的方法,用于区分记录到同一目标的多个应用程序。

<configuration>
  <contextName>myAppName</contextName>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d %contextName [%t] %level %logger{36} -%kvp- %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

7、变量替换

变量替换可以在配置文件中可以指定值的任何位置发生。

变量替换可以在配置文件中任何可以指定值的地方进行。变量替换的语法与 Unix shell 的语法类似。开头${ and closing }之间的字符串被解释为对属性 值的引用。对于属性aName,字符串“${aName}”将被替换为aName属性所持有的值。

7.1、定义变量

变量可以在配置文件中一次定义一个,也可以从外部属性文件或外部资源中批量加载。由于历史原因,定义变量的 XML 元素是,<property>尽管在 logback 1.0.7 及更高版本中,该元素<variable>可以互换使用。

7.2、简单变量替换

如下所示

    <!--    自定义配置输出路径-->
    <springProperty scope="context" name="LOG_FILE" source="logging.file.path" defaultValue="D:\log"/>
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="Jin"/>
    <timestamp key="DATETIME" datePattern="yyyy-MM-dd"/>
<!--    <property name="LOG_FILE_PATH" value="${LOG_FILE}/${APP_NAME}/${DATETIME}"/>-->
    <variable  name="LOG_FILE_PATH" value="${LOG_FILE}/${APP_NAME}/${DATETIME}"/>

7.3、系统变量替换

java -DUSER_HOME="/home/jin" JinApp

XML配置

<configuration>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${USER_HOME}/jinApp.log</file>
    <encoder>
      <pattern>%kvp %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="FILE" />
  </root>
</configuration>

7.4、变量文件

当需要多个变量时,创建一个包含所有变量的单独文件可能更方便。以下是如何进行此类设置。

    <variable resource="logback/logging.properties"/>

logging.properties

CONTEXT_NAME=Jin
# 文件默认路径配置,优先Spring配置:logging.file.path
LOG_DEFAULT_PATH=D:/log
# 文件默认路径配置,优先Spring配置: spring.application.name
APP_DEFAULT_NAME=${CONTEXT_NAME}
#LOG_PATH = ${LOG_FILE}/${APP_NAME}/${DATETIME}
LOG_FILE_PATH=${LOG_DEFAULT_PATH}/${APP_DEFAULT_NAME}/${DATETIME}
# 控制台日志格式配置
CONSOLE_LOG_PATTERN=%clr(%d{HH:mm:ss.SSS}){red} %clr(${LOG_LEVEL_PATTERN:-%-5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%-15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx
CONSOLE_LOG_CHARSET=UTF-8
CONSOLE_LOG_LEVEL=trace
# 文件 DEBUG 日志格式配置
DEBUG_FILE_LOG_PATTERN=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n
DEBUG_FILE_LOG_CHARSET=UTF-8
# 文件 INFO 日志格式配置
INFO_FILE_LOG_PATTERN=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n
INFO_FILE_LOG_CHARSET=UTF-8
# 文件 WARN 日志格式配置
WARN_FILE_LOG_PATTERN=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n
WARN_FILE_LOG_CHARSET=UTF-8
# 文件 ERROR 日志格式配置
#ERROR_FILE_LOG_PATTERN = %d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n ${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
ERROR_FILE_LOG_PATTERN=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n%wEx
ERROR_FILE_LOG_CHARSET=UTF-8
HTML_FILE_LOG_PATTERN=%d{yyyy-MM-dd HH:mm:ss.SSS}%thread%5level%logger{50}%msg%n
HTML_FILE_LOG_CHARSET=UTF-8

LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:true
LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:10KB
LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:0
LOGBACK_ROLLINGPOLICY_MAX_HISTORY:150

变量读取

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

<!--
Default logback configuration provided for import
-->
<included>
    <contextName>${CONTEXT_NAME}</contextName>
    <!--    自定义配置输出路径-->

    <springProperty scope="context" name="LOG_FILE" source="logging.file.path" defaultValue="${LOG_DEFAULT_PATH}"/>
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="${APP_DEFAULT_NAME}"/>
    <timestamp key="DATETIME" datePattern="yyyy-MM-dd"/>
<!--    <property name="LOG_FILE_PATH" value="${LOG_FILE}/${APP_NAME}/${DATETIME}"/>-->
    <property name="LOG_FILE_PATH" value="${LOG_FILE_PATH:-${LOG_FILE}/${APP_NAME}/${DATETIME}}"/>

    <conversionRule conversionWord="applicationName"
                    converterClass="org.springframework.boot.logging.logback.ApplicationNameConverter"/>
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="correlationId"
                    converterClass="org.springframework.boot.logging.logback.CorrelationIdConverter"/>
    <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:- %contextName %clr(%d{HH:mm:ss.SSS}){red} %clr(${LOG_LEVEL_PATTERN:-%-5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%-15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <property name="CONSOLE_LOG_CHARSET" value="${CONSOLE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>
    <property name="CONSOLE_LOG_THRESHOLD" value="${CONSOLE_LOG_THRESHOLD:-TRACE}"/>
    <property name="CONSOLE_LOG_LEVEL" value="${CONSOLE_LOG_LEVEL:-TRACE}"/>

    <property name="DEBUG_FILE_LOG_PATTERN"
              value="${DEBUG_FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <property name="DEBUG_FILE_LOG_CHARSET" value="${DEBUG_FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>

    <property name="INFO_FILE_LOG_PATTERN"
              value="${INFO_FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <property name="INFO_FILE_LOG_CHARSET" value="${INFO_FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>

    <property name="WARN_FILE_LOG_PATTERN"
              value="${WARN_FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <property name="WARN_FILE_LOG_CHARSET" value="${WARN_FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>

    <property name="ERROR_FILE_LOG_PATTERN"
              value="${ERROR_FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
    <property name="ERROR_FILE_LOG_CHARSET" value="${ERROR_FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>

    <property name="HTML_FILE_LOG_PATTERN"
              value="${HTML_FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss.SSS}%thread%5level%logger{50}%msg%n}"/>
    <property name="HTML_FILE_LOG_CHARSET" value="${HTML_FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>


    <logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR"/>
    <logger name="org.apache.catalina.util.LifecycleBase" level="ERROR"/>
    <logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN"/>
    <logger name="org.apache.sshd.common.util.SecurityUtils" level="WARN"/>
    <logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN"/>
    <logger name="org.eclipse.jetty.util.component.AbstractLifeCycle" level="ERROR"/>
    <logger name="org.hibernate.validator.internal.util.Version" level="WARN"/>
    <logger name="org.springframework.boot.actuate.endpoint.jmx" level="WARN"/>

    <!--子节点可以控制我们的代码、框架中日志输出级别等-->
    <logger name="org.springframework.boot.autoconfigure.logging" level='error' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

    <logger name="com.jin.train.member.hello" level='info' additivity="false">
        <appender-ref ref="CONSOLE"/>
    </logger>

</included>

8、作用域

可以定义属性以将其插入到本地范围、上下文范围或系统范围中。本地范围是默认范围。虽然可以从 OS 环境中读取变量,但无法将其写入 OS 环境。

  1. 本地作用域( local )
    • 具有本地作用域的属性从其在配置文件中定义时起一直存在,直到解释/执行该配置文件结束。因此,每次解析和执行配置文件时,都会重新定义本地作用域中的变量。
  2. 上下文范围( context )
    • 具有上下文范围的属性将插入到上下文中,并持续存在,直至上下文消失或被清除。一旦定义,上下文范围内的属性就是上下文的一部分。因此,它可用于所有日志记录事件,包括通过序列化发送到远程主机的事件。
  3. 系统范围( system )
    • 具有系统范围的属性被插入到 JVM 的系统属性中,并且与 JVM 一样长久存在或直到被清除。

示例

    <!--    自定义配置输出路径-->
    <springProperty scope="context" name="LOG_FILE" source="logging.file.path"  defaultValue="D:\log"/>
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="Jin"/>

9、变量的默认值

在某些情况下,如果某个变量没有被声明,或者为空,默认值则非常有用。在 bash shell 中,默认值可以通过 ":-" 来指定。例如:假设变量 aName 没有被定义,"${aNme:-Jin}" 会被解释成 "Jin" 。

9.1、语法

:-

例如,假设名为CONSOLE_LOG_THRESHOLD的变量未定义,则将被解释为 TRACE${CONSOLE_LOG_THRESHOLD:-TRACE}

<property name="CONSOLE_LOG_THRESHOLD" value="${CONSOLE_LOG_THRESHOLD:-TRACE}"/>

10、变量的嵌套

变量的名字、默认值、以及值都可以引用其它的变量。

10.1、嵌套值

一个变量的值可以包含对其它变量的引用。

    <springProperty scope="context" name="LOG_FILE" source="logging.file.path" defaultValue="${LOG_DEFAULT_PATH}"/>
    <springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="${APP_DEFAULT_NAME}"/>
    <timestamp key="DATETIME" datePattern="yyyy-MM-dd"/>
    <property name="LOG_FILE_PATH" value="${LOG_FILE_PATH:-${LOG_FILE}/${APP_NAME}/${DATETIME}}"/>

如上所示,LOG_FILE_PATH 的值就引入 LOG_FILEAPP_NAMEDATETIME 变量

10.2、名字嵌套

变量的名字可以包含对其它变量的引用。

例如:如果变量 user=Jin,那么 "${${user}.age}" 就是对变量名为 "Jin.age" 的引用。

    <property name="user" value="Jin"/>
    <property name="Jin.age" value="18"/>
    <property name="age" value="${${user}.age}"/>

10.3、默认值嵌套

一个变量的默认值可以引用另一个变量。

例如:假设变量 "id" 没有被定义,变量 "userid" 的值为 "alice",那么表达式 "${id:-${userid}}" 的值为 "alice"。

	<property name="Jin.id" value="18"/>
    <property name="ID" value="$ID:-${Jin.id}"/>

11、HOSTNAME 属性

HOSTNAME 在配置期间会被自动定义为上下文范围内。

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${HOSTNAME} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

12、CONTEXT_NAME 属性

通过名字可以看出来,CONTEXT_NAME 属性对应当前上下文的名字。

<configuration>
    <contextName>Jin</contextName>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONTEXT_NAME} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

13、动态定义属性

可以通过 <define> 元素动态的定义变量。这个元素需要两个强制的属性:nameclassname 属性用来定义变量的名字,classs 属性用来引用实现了 PropertyDefineropen in new window 接口的类。PropertyDefiner 实例的 getPropertyValue() 的返回值就是变量的值。还可以通过 scope 属性指定变量的作用域open in new window

参考:logback自定义日志格式open in new window

xml

    <define name="ip" class="com.jin.logging.configuration.IpProperty"/>

java

import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.PropertyDefiner;
import ch.qos.logback.core.status.Status;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class IpProperty implements PropertyDefiner {


    @Override
    public String getPropertyValue() {
        try {

            InetAddress address = InetAddress.getLocalHost();
            return address.getHostAddress();

        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return null;
    }

......
}

pattern

CONSOLE_LOG_PATTERN= ${ip} %clr(%d{HH:mm:ss.SSS}){red} %clr(${LOG_LEVEL_PATTERN:-%-5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%-15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx

输出效果

198.18.0.1 18:33:01.697 INFO  19900 --- [logback-config] [restartedMain  ] com.jin.LogbackConfigApp                 : 启动成功 ┗( ▔, ▔ )

14、配置文件的条件处理

语法

 <if condition="条件表达式">
        <then>
            ...
        </then>
	</if>
	
	<if condition="条件表达式">
        <then>
            ...
        </then>
        <else>
            ...
        </else>
	</if>

示例

<configuration debug="true">

  <if condition='property("HOSTNAME").contains("torino")'>
    <then>
      <appender name="CON" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
          <pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
        </encoder>
      </appender>
      <root>
        <appender-ref ref="CON" />
      </root>
    </then>
  </if>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${randomOutputDir}/conditional.log</file>
    <encoder>
      <pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
   </encoder>
  </appender>

  <root level="ERROR">
     <appender-ref ref="FILE" />
  </root>
</configuration>

条件表达式只能是上下文变量或者系统变量。因为值是通过参数传递的,property() 方法或者其等价的 p() 方法属性的值。例如:如果要获取变量 "k" 的值,可以通过 property("k") 或者 p("k") 来获取。如果 "k" 没有定义,那么方法将会返回空字符串。所以不需要去判断是否为 null。

isDefined() 方法可以用来判断变量是否已经被定义。例如:可以通过 isDefined("k") 来判断 k 是否已经定义。还可以通过 isNull() 方法来判断变量是否为 null。例如:isNull("k")

15、引入文件

通过 <include> 元素可以引入外部的配置文件。

示例

    <include resource="logback/default.xml"/>
    <include resource="logback/appender/console-appender.xml"/>
<!--    如果 logback 没有通过 include 元素找到指定的配置文件,会在控制台打印出内部状态信息。如果引入的外部配置文件是可选的,可以设置 optional=true。-->
	<include url="http://some.host.com/includedConfig.xml" optional="true"/>

16、序列化模型

从 1.3.9/1.4.9版本开始,logback-classic 可以创建与 XML 配置文件匹配的序列化版本的配置模型。

这是通过添加<serializeModel> 元素并指定序列化模型文件的路径来实现的。

<configuration debug="false">

  <serializeModel file="path/to/logback.scmo"/>
  ...
</configuration>

SerializedModelConfigurator 请注意,在 logback-classic 初始化期间会创建并调用一个实例。实例可以从序列化配置模型 (.scmo) 文件中读取和配置 logback-classic。

17、添加上下文监听器

贡献者: Jin