日志框架 Logback 官方手册(第三章:Configuration)

以下内容翻译整理自logback官方手册,地址:logback官方手册


logback 配置

将日志请求插入应用程序代码需要相当多的计划和工作。观察表明,大约有4%的代码用于日志记录。因此,即使是一个中等大小的应用程序,其代码中也会包含数千条日志语句。考虑到它们的数量,我们需要工具来管理这些日志语句。

可以通过编程方式配置logback,也可以使用XMLGroovy格式表示的配置脚本配置logback。顺便说一下,通过使用我们的 PropertiesTranslator web应用程序,现有的log4j用户可以将他们的log4j.properties文件转换为logback.xml

logback的初始化配置步骤:

  1. Logback尝试在类路径中找到一个名为logback -test.xml的文件。
  2. 如果没有找到这样的文件,logback将尝试在类路径中找到一个名为logback.groovy的文件。
  3. 如果没有找到这样的文件,它将检查类路径中的logback.xml文件。
  4. 如果没有找到这样的文件, service-provider loading facility
    (在 JDK 1.6 引入)通过在类路径中查找META-INF\services\ch. qs .logback.classic.spi.Configurator文件,用于解析com. qs .logback.classic.spi.Configurator接口的实现。它的内容应该指定所需配置程序的完全限定类名。
  5. 如果以上方法都不成功,logback将使用BasicConfigurator自动配置自己,这将导致日志输出定向到控制台。

最后一步是在没有配置文件的情况下提供默认(但非常基本)日志功能。

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

自动配置logback

配置logback的最简单方法是让logback使用其默认配置。

使用 BasicConfigurator 的简单示例

package com.wangbo.cto.logback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @date 2019/9/14 13:54
 * @auther wangbo
 */
public class MyApp1 {
    final static Logger logger = LoggerFactory.getLogger(MyApp1.class);

    public static void main(String[] args) {
        logger.info("Entering application.");
        Foo foo = new Foo();
        foo.doIt();
        logger.info("Exiting application.");
    }
}

该类定义一个静态日志记录器变量。然后实例化一个Foo对象。Foo类如下所示:

package com.wangbo.cto.logback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @date 2019/9/14 13:53
 * @auther wangbo
 */
public class Foo {
    static final Logger logger = LoggerFactory.getLogger(Foo.class);

    public void doIt(){
        logger.debug("Did it again!");
    }
}

假设不存在 logback-test.xmllogback.xml配置文件,logback将默认调用BasicConfigurator设置一个最小配置。这个最小配置由一个附加到根loggerConsoleAppender组成,使用PatternLayoutEncoder格式化输出。

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

此外,在默认情况下,根logger被指定为DEBUG级别。

因此,上面程序运行结果显示如下:

13:56:07.357 [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
13:56:07.359 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
13:56:07.359 [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.

MyApp1 应用程序通过调用org.slf4j.LoggerFactoryorg.slf4j.Logger链接到logback。获取到它希望使用的日志记录器,然后继续。注意,Foo类对logback的唯一依赖关系是通过org.slf4j.LoggerFactoryorg.slf4j.Logger的导入。除了配置logback的代码(如果存在这样的代码),客户端代码不需要依赖于logback。由于SLF4J允许在其抽象层下使用任何日志框架,所以将大量代码从一个日志框架迁移到另一个日志框架是很容易的。

使用 logback-test.xml 或 logback.xml 自动配置

如前所述,logback将尝试使用在类路径上找到的文件logback-test.xmllogback.xml来配置自己。这是一个配置文件,与我们刚才看到的BasicConfigurator所建立的配置文件等效。

基本配置文件:

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 默认使用 PatternLayoutEncoder 为编码器分配类型 -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

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

将上面的配置文件命名为logback.xml(或logback-test.xml)后,将其放入可以从类路径访问的目录中。运行 MyApp1 应用程序应该会得到与其前一次运行相同的结果。

如果发生警告或错误自动打印状态信息

如果在解析配置文件期间出现警告或错误,logback将自动在控制台上打印其内部状态消息。注意,为了避免重复,如果用户显式注册了状态侦听器(定义如下),则禁用自动状态打印。在没有警告或错误的情况下,如果仍然希望检查logback的内部状态,则可以通过调用StatusPrinter类的print()方法命令logback打印状态数据。只需要添加两行代码即可:

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);

下面我们可以新建一个类 MyApp2,运行观察日志信息。

package com.wangbo.cto.logback;

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

/**
 * @date 2019/9/14 13:54
 * @auther wangbo
 */
public class MyApp2 {
    final static Logger logger = LoggerFactory.getLogger(MyApp2.class);

    public static void main(String[] args) {
        //打印内部状态
        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        StatusPrinter.print(lc);
        logger.info("Entering application.");
        Foo foo = new Foo();
        foo.doIt();
        logger.info("Exiting application.");
    }
}

执行结果:

14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
14:18:25,824 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/Programmer/projects/mytest/target/classes/logback.xml]
14:18:25,918 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
14:18:25,919 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
14:18:25,923 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
14:18:25,929 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
14:18:25,965 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
14:18:25,965 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
14:18:25,965 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
14:18:25,966 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@4f933fd1 - Registering current configuration as safe fallback point

14:18:25.968 [main] INFO  com.wangbo.cto.logback.MyApp2 - Entering application.
14:18:25.969 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
14:18:25.969 [main] INFO  com.wangbo.cto.logback.MyApp2 - Exiting application.

状态数据

您可以指示配置文件转储状态数据,而不是从代码中以编程方式调用StatusPrinter,即使在没有错误的情况下也是如此。要实现这一点,您需要设置configuration元素的debug属性,即配置文件中最上面的元素,如下所示。请注意,这个debug属性只与状态数据相关。它不会影响logback关于日志程序级别的配置。

使用调试模式的基本配置文件:

<configuration debug="true"> 
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- 默认使用 PatternLayoutEncoder 为编码器分配类型 -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

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

<configuration>元素中设置debug="true"将输出状态信息。我们假定:

  1. 找到配置文件。
  2. 配置文件是格式良好的 XML。

如果这两个条件都没有满足,Joran 无法解释debug属性,因为配置文件无法读取。如果找到配置文件但格式不正确,然后logback将检测错误条件并在控制台上自动打印其内部状态。但是,如果找不到配置文件,logback不会自动打印其状态数据,因为这并不一定是一个错误条件。这种情况下以编程方式调用StatusPrinter.print()可以确保在每种情况下都打印状态信息。

强制状态输出

在没有状态消息的情况下,跟踪一个异常的logback.xml配置文件可能很困难,特别是在生产环境中,应用程序源代码不容易修改。为了帮助识别恶意配置文件的位置,你可以通过logback.statusListenerClass系统属性设置一个StatusListener来强制输出状态信息。logback.statusListenerClass系统属性还可用于在出现错误时自动生成的输出静默。

顺便说一下,设置debug="true"严格地等同于安装OnConsoleStatusListener。下面将进一步讨论状态侦听器。接下来展示OnConsoleStatusListener的安装。

注册状态侦听器:

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  

  ... 配置文件的其余部分 
</configuration>

通过设置debug属性或安装OnConsoleStatusListener,将在很大程度上帮助您诊断logback问题。因此,强烈建议启用logback状态数据,并应将其视为首选资源。

将默认配置文件的位置指定为系统属性

您可以使用名为“logback.configurationFile”的系统属性指定默认配置文件的位置。此属性的值可以是一个URL、类路径上的一个资源或应用程序外部的一个文件的路径。

java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1

注意,文件扩展名必须是.xml或者.groovy。忽略其他扩展。显式注册状态侦听器可能有助于调试定位配置文件的问题。

鉴于“logback.configurationFile”是一个Java系统属性,它也可以在您的应用程序中设置。但是,必须在创建任何记录器实例之前设置系统属性。

import ch.qos.logback.classic.util.ContextInitializer;

public class ServerMain {
    public static void main(String args[]) throws IOException, InterruptedException {
        // 必须在第一次调用 LoggerFactory.getLogger(); 之前设置
        System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, /path/to/config.xml);
        ...
    }   
}
public static final String CONFIG_FILE_PROPERTY = "logback.configurationFile";

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

如果指示这样做,logback-classic将扫描配置文件中的更改,并在配置文件更改时自动重新配置自己。为了指示logback-classic扫描配置文件中的更改并自动重新配置自己,将<configuration>元素的scan属性设置为true,如下所示。

扫描配置文件中的更改和自动重新配置:

<configuration scan="true"> 
  ... 
</configuration>

默认情况下,配置文件将每分钟扫描一次更改。您可以通过设置<configuration>元素的scanPeriod属性来指定不同的扫描周期。

指定不同的扫描周期:

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

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

在幕后,当您将scan属性设置为true时,将安装一个ReconfigureOnChangeTask,此任务在单独的线程中运行,并将检查配置文件是否已更改。ReconfigureOnChangeTask将自动监视文件的任何改动。

由于在编辑配置文件时很容易出错,如果配置文件的最新版本有XML语法错误,它将返回到没有XML语法错误的前一个配置文件。

启用堆栈跟踪中的打包数据

注意:从1.1.4版本开始,默认情况下将禁用打包数据。

如果指示这样做,logback可以包含它输出的堆栈跟踪行的每一行的打包数据。打包数据由jar文件的名称和版本组成,该jar文件是堆栈跟踪行类的起源。打包数据对于识别软件版本问题非常有用。然而,计算非常耗费资源,特别是在经常抛出异常的应用程序中。下面是一个输出示例:

14:28:48.835 [btpool0-7] INFO  c.q.l.demo.prime.PrimeAction - 99 is not a valid value
java.lang.Exception: 99 is invalid
  at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
  at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
  at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
  at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
  at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
  at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
  at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]

打包数据在默认情况下是禁用的,但可以通过配置启用:

<configuration packagingData="true">
  ...
</configuration>

或者,可以通过调用LoggerContext中的setPackagingDataEnabled(boolean)方法以编程方式启用/禁用打包数据,如下所示:

LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);

直接调用 JoranConfigurator

logback依赖于一个名为Joran的配置库,logback-core的一部分。logback的默认配置机制在类路径上找到的默认配置文件上调用JoranConfigurator。无论出于什么原因,如果您希望覆盖logback的默认配置机制,您可以通过直接调用JoranConfigurator来实现这一点。

直接调用JoranConfigurator

package com.wangbo.cto.logback;

import ch.qos.logback.access.joran.JoranConfigurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @date 2019/9/14 22:01
 * @auther wangbo
 */
public class MyApp3 {
    final static Logger logger = LoggerFactory.getLogger(MyApp3.class);

    public static void main(String[] args) {
        //假设SLF4J绑定到当前环境中的logback
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        try {
            JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(context);
            //调用context.reset()清除任何以前的配置,比如默认配置。
            //对于多步骤配置,省略调用context.reset()。
            context.reset();
            configurator.doConfigure(args[0]);
        } catch (JoranException e) {
            //StatusPrinter将处理这个问题
        }
        StatusPrinter.printInCaseOfErrorsOrWarnings(context);

        logger.info("Entering application.");
        Foo foo = new Foo();
        foo.doIt();
        logger.info("Exiting application.");
    }
}

这个应用程序获取当前有效的LoggerContext,创建一个新的JoranConfigurator,设置将在其上操作的上下文,重置记录器上下文,然后最后要求配置器使用作为参数传递给应用程序的配置文件来配置上下文。如果出现警告或错误,将打印内部状态数据。注意,对于多步骤配置,应该省略context.reset()调用。

查看状态信息

Logback将其内部状态数据收集到StatusManager对象中,通过LoggerContext进行访问。

给定StatusManager,您可以访问与logback上下文关联的所有状态数据。为了使内存使用保持在合理的水平,默认的StatusManager实现将状态消息存储在两个单独的部分:头部和尾部。头部分存储第一个H状态消息,而尾部分存储最后一个T消息。目前H=T=150,尽管这些值在将来的版本中可能会改变。

Logback-classic附带一个名为ViewStatusMessagesServlet的 servlet。这个 servlet 将StatusManager中与当前LoggerContext关联的内容打印为HTML表。这是样本输出。

lbClassicStatus.jpg

要将此 servlet 添加到 web 应用程序中,请将以下行添加到其 WEB-INF/web.xml 文件中。

  <servlet>
    <servlet-name>ViewStatusMessages</servlet-name>
    <servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ViewStatusMessages</servlet-name>
    <url-pattern>/lbClassicStatus</url-pattern>
  </servlet-mapping>

ViewStatusMessages servlet 可以通过 URL http://host/yourWebapp/lbClassicStatus访问。

监听状态消息

您还可以将StatusListener附加到StatusManager,以便您可以立即响应状态消息,特别是在回退配置之后发生的消息。注册状态侦听器是一种方便的方法,可以在不需要人工干预的情况下监视登录的内部状态。

Logback附带一个名为OnConsoleStatusListenerStatusListener实现,顾名思义,它将在控制台打印所有新的传入状态消息。

下面是向StatusManager注册OnConsoleStatusListener实例的示例代码。

   LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); 
   StatusManager statusManager = lc.getStatusManager();
   OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
   statusManager.add(onConsoleListener);

注意,已注册的状态侦听器只会在注册之后接收状态事件。它将不会接收先前的消息。因此,通常最好将状态侦听器注册指令放在配置文件的顶部,然后才是其他指令。

还可以在配置文件中注册一个或多个状态侦听器。这里有一个例子。
注册状态侦听器:

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  

  ... 配置文件的其余部分 
</configuration>

logback.statusListenerClass 系统特性

还可以通过设置“logback”来注册状态侦听器。将Java系统属性设置为要注册的侦听器类的名称。例如:

java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...

Logback附带几个状态侦听器实现。OnConsoleStatusListener在控制台打印输入状态消息,即在System.out上打印。OnErrorConsoleStatusListenerSystem.err上打印传入的状态消息。NopStatusListener删除传入的状态消息。

注意,如果在配置期间注册了任何状态侦听器,特别是如果用户通过logback.statusListenerClass指定了一个状态侦听器,则禁用自动状态打印(以防出现错误)。因此,通过将NopStatusListener设置为状态侦听器,可以完全禁用内部状态打印。

java -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener ...

停止 logback-classic

为了释放logback-classic使用的资源,停止logback上下文总是一个好主意。停止上下文将关闭附加到上下文定义的日志记录器的所有附加程序,并以有序的方式停止任何活动线程。请阅读下面关于“关机挂钩”的部分。

import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...

// 假设SLF4J在当前环境中绑定到logback-classic
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();

在web应用程序中,可以从ServletContextListenercontextDestroyed方法中调用上面的代码,以停止logback-classic并释放资源。从1.1.10版本开始,将自动为您安装适当的ServletContextListener(见下面)。

通过关闭挂钩停止 logback-classic

安装JVM关机挂钩是关闭登录和释放相关资源的一种方便方法。

<configuration debug="true">
   <!-- 在没有class属性的情况下,假定 ch.qos.logback.core.hook.DefaultShutdownHook -->
   <shutdownHook/>
  .... 
</configuration>

请注意,您可以通过将 class 属性设置为与关机钩子的类名相对应来安装自己制作的关机钩子。

默认的关闭钩子,即 DefaultShutdownHook,将在指定的延迟(默认为0)之后停止logback上下文。停止上下文将允许在后台运行的任何日志文件压缩任务在30秒内完成。在独立的 Java 应用程序中,向配置文件中添加<shutdownHook/>指令是确保允许在 JVM 退出之前完成任何正在进行的压缩任务的简单方法。在Web服务器中的应用程序中,webShutdownHook将自动安装,使<shutdownHook/>指令相当冗余和不必要。

web应用程序中的WebShutdownHookstop logback-classic

自1.1.10以后, Logback-classic将自动要求web服务器安装一个LogbackServletContainerInitializer来实现ServletContainerInitializer接口(可在servlet-api 3.x和更高的版本中获得)。

通过在 web 应用程序的web.xml文件中设置一个名为logbackDisableServletContainerInitializer<context-param>。下面是相关的代码片段。

<web-app>
    <context-param>
        <param-name>logbackDisableServletContainerInitializer</param-name>
        <param-value>true</param-value>
    </context-param>
    .... 
</web-app>

注意,logbackDisableServletContainerInitializer变量也可以设置为 OS 环境变量的 Java 系统属性。最本地的设置具有优先级,即 web-app 第一,系统属性第二,操作系统环境最后。

配置文件的语法

正如您在手册中所看到的,还有很多例子需要跟随,logback允许您重新定义日志记录行为,而不需要重新编译代码。事实上,您可以很容易地配置logback以禁用应用程序的某些部分日志,或直接输出到UNIX 系统日志守护进程,到一个数据库,到一个日志可视化工具,或将日志事件转发给远程logback服务器,根据本地服务器策略进行日志记录,例如,通过将日志事件转发到第二个logback服务器。

本节的其余部分介绍配置文件的语法。

我们将一遍又一遍地演示,logback配置文件的语法非常灵活。因此,不可能使用DTD文件或XML模式指定允许的语法。然而,配置文件的基本结构可以描述为,<configuration>元素,包含零个或多个<appender>元素,然后是零个或多个<logger>元素,随后是<root>元素,下图说明了这个基本结构。

标签名称的大小写敏感性

自从logback 0.9.17版本之后,显式规则下标签名称不区分大小写。例如,<logger><logger><logger>都是有效的配置元素,将以相同的方式解释。注意,XML格式规则仍然适用,如果你打开一个标签为<xyz>你必须关闭它为</xyz>,而不能使用</XyZ>作为关闭标签。对于隐式规则,除了第一个字母外,标记名是区分大小写的。因此,<xyz>和<Xyz>是等价的,但不等价<xYz>。隐式规则通常遵循驼峰命名规则,这在 Java 世界中很常见。由于很难区分标记何时与显式操作关联,何时与隐式操作关联,XML 标记对于第一个字母是区分大小写的还是不区分大小写的,这一点很重要。如果您不确定对给定的标记名使用哪种情况,请遵循驼峰命名规则,它几乎总是正确的约定。

配置日志记录器,或<logger>元素

此时,您至少应该对级别继承和基本选择规则有一些了解。日志程序是使用<logger>元素配置的。一个<logger>元素只接受一个强制的name属性、一个可选的level属性和一个可选的additivity属性。level属性的值允许不区分大小写的字符串值:TRACE、DEBUG、INFO、WARN、ERROR、ALL 或 OFF。不区分大小写的特殊值 INHERITED,或其同义词NULL,将强制从层次结构的较高层次继承日志程序的级别。如果您设置了日志程序的级别,然后决定它应该继承其级别,那么这将非常方便。

<logger>元素可以包含零个或多个<appender-ref>元素;这样引用的每个追加器都被添加到指定的日志程序中。注意,与log4j不同,logback-classic在配置给定的日志程序时不会关闭或删除任何以前引用的附加程序。

配置根记录器,或<root>元素

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

与<logger>元素类似,<root>元素可以包含零个或多个<appender-ref>元素;这样引用的每个追加器都被添加到根日志记录器中。注意,与log4j不同,logback-classic在配置根日志程序时不会关闭或删除任何以前引用的附加程序。

示例

设置日志程序或根日志程序的级别与声明和设置其级别一样简单,正如下一个例子所示。假设我们不再有兴趣去看属于com.wangbo.cto.logback包的任何组件的任何调试信息。下面的配置文件展示了如何实现这一点。

设置日志程序的级别:

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- 默认使用 PatternLayoutEncoder 为编码器分配类型 -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="com.wangbo.cto.logback" level="INFO"/>

  <!-- 严格地说,level属性是不必要的,因为根级别的默认级别就是DEBUG -->
  <root level="DEBUG">          
    <appender-ref ref="STDOUT" />
  </root>  
  
</configuration>

当将上述配置文件作为 MyApp1 应用程序的参数时,它将产生以下输出:

23:44:53.876 [main] INFO  com.wangbo.cto.logback.MyApp1 - Entering application.
23:44:53.877 [main] INFO  com.wangbo.cto.logback.MyApp1 - Exiting application.

注意,只有info级别信息执行了。“Foo”类中的debug级别日志未打印。

您可以根据需要配置任意多的日志记录器的级别。在下一个配置文件中,我们设置了包的日志级别info,也设置了Foo类的日志级别debug,

设置多个日志记录器的级别:

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 默认使用 PatternLayoutEncoder 为编码器分配类型 -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.wangbo.cto.logback" level="INFO"/>
    <logger name="com.wangbo.cto.logback.Foo" level="DEBUG"/>
   
    <root level="DEBUG">
        <appender-ref ref="STDOUT" />
    </root>

</configuration>

执行 MyApp1,打印结果:

23:51:49.116 [main] INFO  com.wangbo.cto.logback.MyApp1 - Entering application.
23:51:49.118 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
23:51:49.118 [main] INFO  com.wangbo.cto.logback.MyApp1 - Exiting application.

下表列出了JoranConfigurator使用sample3.xml配置文件配置logback之后的日志记录器及其级别。

Logger name 指定级别 有效级别
root DEBUG DEBUG
com.wangbo.cto.logback INFO INFO
com.wangbo.cto.logback.MyApp1 null INFO
com.wangbo.cto.logback.Foo DEBUG DEBUG

因此,MyApp1类中的两个INFO日志语句以及Foo.doIt()中的DEBUG消息都是启用的。注意,根日志程序的级别总是设置为非空值,默认情况下为DEBUG

让我们注意,基本选择规则取决于被调用的日志程序的有效级别,不是附加程序所在的记录器级别。Logback将首先确定是否启用了日志语句,如果启用,它将调用logger层次结构中找到的appenders,而不管它们的级别如何。下面的配置文件就是一个例子:

<configuration>

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

  <logger name="com.wangbo.cto.logback" level="INFO" />

  <!-- 关闭所有日志记录(子记录器可以覆盖) -->
  <root level="OFF">
    <appender-ref ref="STDOUT" />
  </root>

</configuration>

sui
下表列出了应用 sample4.xml配置文件后的日志记录器及其级别。

Logger name 指定级别 有效级别
root OFF OFF
com.wangbo.cto.logback INFO INFO
com.wangbo.cto.logback.MyApp1 null INFO
com.wangbo.cto.logback.Foo null INFO

名为STDOUTConsoleAppender,是配置文件中唯一配置的追加器,附加到根记录器,其级别设置为OFF,但是,运行 MyApp1 将产生以下结果:

00:01:55.395 [main] INFO  com.wangbo.cto.logback.MyApp1 - Entering application.
00:01:55.397 [main] INFO  com.wangbo.cto.logback.MyApp1 - Exiting application.

因此,根日志记录器的级别没有明显的影响,因为在com.wangbo.cto.logback中有日志记录器。MyApp3 和 Foo 类的日志级别都是INFO

配置 Appenders

追加器使用<appender>元素配置,它接受两个强制属性nameclassname属性指定追加器的名称,class属性指定要实例化的appender类的完全限定名。<appender>元素可以包含零个或一个<layout>元素,零个或多个<encoder>元素,零个或多个<filter>元素。除了这三个通用的元素,<appender>元素可以包含与appender类的JavaBean属性相对应的任意数量的元素。无缝地支持给定logback组件的任何属性是Joran的主要优势之一,这将在后面的章节中讨论。下图说明了常见的结构。注意,对属性的支持是不可见的。

<layout> 元素接受一个强制性的class属性,该属性指定要实例化的布局类的完全限定名。与<appender>元素一样,<layout>可能包含与layout实例属性相对应的其他元素。因为这是很常见的情况,如果布局类是PatternLayout,可以省略class属性。按照默认的类映射规则指定。

登录到多个appenders就像定义不同的appenders并在日志程序中引用它们一样简单,如下面的配置文件所示:
多个日志实例:

<configuration>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myApp.log</file>

    <encoder>
      <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

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

上面的配置文件定义了两个appenders,分别称为 FILESTDOUTFILE 追加日志到一个myApp.log的文件。此附加程序的编码器是PatternLayoutEncoder,它输出日期、级别、线程名称、日志程序名称、文件名称和日志请求所在的行号、消息和行分隔符。第二个追加器称为STDOUT输出到控制台。此附加程序的编码器仅输出消息字符串和行分隔符。

通过在appender-ref元素中按名称引用附加程序,附加程序被附加到根日志程序。注意,每个附加器都有自己的编码器。编码器通常不设计为多个附加程序共享,布局也是如此,因此,logback配置文件不提供共享编码器或布局的任何语法方法。

Appenders 累积

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

重复的appender

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

  <logger name="com.wangbo.cto.logback">
    <appender-ref ref="STDOUT" />
  </logger>

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

修改配置文件为上面的配置,运行 MyApp1 程序,结果如下:

10:35:46.197 [main] INFO  com.wangbo.cto.logback.MyApp1 - Entering application.
10:35:46.197 [main] INFO  com.wangbo.cto.logback.MyApp1 - Entering application.
10:35:46.198 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
10:35:46.198 [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
10:35:46.198 [main] INFO  com.wangbo.cto.logback.MyApp1 - Exiting application.
10:35:46.198 [main] INFO  com.wangbo.cto.logback.MyApp1 - Exiting application.

注意重复的输出。名为STDOUT的附加程序被附加到两个日志记录器,rootcom.wangbo.cto.logback。因为根记录器是所有日志记录器的祖先,com.wangbo.cto.logback记录器又是com.wangbo.cto.logback.MyApp1com.wangbo.cto.logback.Foo的父级日志记录器,使用这两个日志记录器发出的每个日志请求都将输出两次,一次是com.wangbo.cto.logback输出的,另一次是root输出的。

附加程序的可累加性并不是为新用户设计的陷阱。这是一个非常方便的logback特性。例如,您可以配置日志记录,使日志消息出现在控制台上(对于系统中的所有日志记录程序),而只有来自特定日志记录程序集的消息流进特定的附加程序。

多个appender

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>myApp.log</file>
    <encoder>
      <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <logger name="com.wangbo.cto.logback">
    <appender-ref ref="FILE" />
  </logger>

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

在本例中,console appender将记录所有消息(对于系统中的所有日志记录器),而file appender只记录来自com.wangbo.cto.logback请求进入myApp.log文件。

覆盖默认的累积行为

如果默认的累积行为不适合您的需要,您可以通过将累加标志设置为false来覆盖它。因此,日志记录器树中的分支可以将输出直接输出到一组与树的其他部分不同的追加器。

累加性标志:

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>foo.log</file>
    <encoder>
      <pattern>%date %level [%thread] %logger{10} [%file : %line] %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

  <logger name="com.wangbo.cto.logback.Foo" additivity="false">
    <appender-ref ref="FILE" />
  </logger>

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

在本例中,名为FILE的附加器被附加到com.wangbo.cto.logback.Foo记录器。此外,com.wangbo.cto.logback.Foo记录器的可累加标记设置为false,这样它的日志输出将只会被发送到foo.log文件中,而不会发送到层次结构中更高的附录中(对于本例也就是说不会被打印在控制台上)。
运行 MyApp1,控制台打印结果如下:

Entering application.
Exiting application.

foo.log文件记录如下:

2019-09-15 10:54:13,667 DEBUG [main] c.w.c.l.Foo [Foo.java : 14] Did it again!

设置上下文名称

如前一章所述,每个日志程序都附加到一个日志程序上下文。默认情况下,日志记录器上下文称为“default”。但是,您可以使用<contextName>配置指令设置一个不同的名称。注意,一旦设置好,日志程序上下文名称将无法更改。设置上下文名称是一种简单而直接的方法,用于区分多个应用程序对同一目标的日志记录。

设置上下文名称并显示它:

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

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

最后一个示例演示了日志记录器上下文的命名。在layout的模式中添加contextName转换词将输出所述名称。

运行 MyApp1,结果如下:

2019-09-15 11:05:09,344 myAppName [main] INFO com.wangbo.cto.logback.MyApp1 - Entering application.
2019-09-15 11:05:09,345 myAppName [main] DEBUG com.wangbo.cto.logback.Foo - Did it again!
2019-09-15 11:05:09,345 myAppName [main] INFO com.wangbo.cto.logback.MyApp1 - Exiting application.

可以看出,上下文名称myAppName被输出了。

变量替换

该文档的早期版本使用了术语“属性替换”而不是术语“变量”。请考虑这两个术语是可以互换的,尽管后者表达了更清晰的含义。

与许多脚本语言一样,logback配置文件支持定义和替换变量。变量有一个作用域(见下面)。此外,变量可以在配置文件本身、外部文件、外部资源中定义,甚至可以动态计算和定义。

变量替换可以发生在配置文件中指定值的任何位置。变量替换的语法类似于 Unix shell 的语法。开始${和结束}之间的字符串被解释为对属性值的引用。对于属性aName,字符串“${aName}”将替换为aName属性所持有的值。

由于它们经常会派上用场,主机名HOSTNAMECONTEXT_NAME变量是自动定义的,并且具有上下文作用域。考虑到在某些环境中计算主机名可能需要一些时间,它的值是延迟计算的(仅在需要时)。此外,也可以直接在配置中设置主机名。

定义变量

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

下一个示例显示配置文件开头声明的变量。然后在文件的后面使用它来指定输出文件的位置。

简单的变量替换:

<configuration>
  <property name="USER_HOME" value="/home/sebastien" />

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

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

下一个示例展示了如何使用系统属性来实现相同的结果。该属性没有在配置文件中声明,因此logback将在系统属性中查找它。Java系统属性可以在命令行上设置,如下所示:

java -DUSER_HOME="/home/sebastien" MyApp2

系统变量替换:

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

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

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

使用单独的文件替换变量:

<configuration>

  <property file="src/main/java/chapters/configuration/variables1.properties" />

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

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

这个配置文件包含对一个名为variables1.properties文件的引用。该文件中包含的变量将被读取,然后在本地范围内定义。这是变量。属性文件可能看起来像下面这样。

USER_HOME=/home/sebastien

您还可以在类路径上引用资源,而不是引用文件。

<configuration>

  <property resource="resource1.properties" />

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

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

作用域

属性可以定义为插入到本地范围、上下文范围或系统范围中。本地范围是缺省值。虽然可以从 OS 环境中读取变量,但不可能写入 OS 环境。

局部作用域:具有局部作用域的属性从其定义开始存在于配置文件中,直到所述配置文件的解释/执行结束。因此,每次解析和执行配置文件时,局部范围内的变量都会重新定义。
上下文作用域:具有上下文作用域的属性被插入到上下文中,并持续到上下文被清除为止。一旦定义了上下文范围中的属性,它就是上下文的一部分。因此,它在所有日志事件中都可用,包括那些通过序列化发送到远程主机的日志事件。
系统作用域:具有系统范围的属性被插入到JVM的系统属性中,并在JVM运行期间或清除之前一直有效。

在替换期间,首先在本地范围内查找属性,然后在上下文范围内查找属性,最后在系统属性范围内查找属性,最后在 OS 环境中查找属性。

可以使用<property>元素的scope属性,<define>元素或<insertFromJNDI>元素的scope属性设置属性的范围。scope属性允许“local”、“context” 和 “system”字符串作为可能的值。如果没有指定,则默认为“local”

在“上下文”范围中定义的变量:

<configuration>

  <property scope="context" name="nodeId" value="firstNode" />

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

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

在上面的例子中,假定nodeId属性是在上下文范围中定义的,它将在每个日志事件中可用,即使是那些通过序列化发送到远程主机的日志事件。

变量的默认值

在某些情况下,如果变量没有声明或其值为null,则可能希望它具有默认值。与在Bash shell中一样,可以使用“:-”操作符指定默认值。例如,假设没有定义名为aName的变量,“${aName:-golden}”将被解释为“golden”

嵌套的变量

完全支持变量嵌套。变量的名称、默认值和值定义都可以引用其他变量。

值嵌套

变量的值定义可以包含对其他变量的引用。假设您希望使用变量不仅指定目标目录,还指定文件名,并将这两个变量组合到名为“destination”的第三个变量中。下面显示的属性文件给出了一个示例。

嵌套变量引用:

USER_HOME=/home/sebastien
fileName=myApp.log
destination=${USER_HOME}/${fileName}

注意,在上面的属性文件中,“destination”由另外两个变量组成,即“USER_HOME”“fileName”
配置文件如下:

<configuration>

  <property file="variables2.properties" />

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

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

名字嵌套

在引用变量时,变量名可能包含对另一个变量的引用。例如,如果名为“userid”的变量被赋值为“alice”,那么"${${userid}.password}"就是引用名为“alice.password”的变量。

默认值嵌套

变量的默认值可以引用另一个变量。例如,假设变量'id'未赋值,变量'userid'被赋值为"alice",那么表达式"${id:-${userid}}"将返回"alice"。

HOSTNAME 属性

由于HOSTNAME属性通常很方便,所以在配置过程中会使用上下文范围自动定义它。

CONTEXT_NAME 属性

正如其名称所示,CONTEXT_NAME属性对应于当前日志上下文的名称。

设置一个时间戳

timestamp元素可以根据当前日期和时间定义属性。时间戳元素将在下一章中解释。

动态定义属性

可以使用<define>元素动态定义属性。define元素接受两个强制属性:nameclassname属性指定要设置的属性的名称,而class属性指定实现PropertyDefiner接口的任何类。PropertyDefiner实例的getPropertyValue()方法返回的值将是命名属性的值。您还可以通过指定scope属性为指定的属性设定范围。

<configuration>

  <define name="rootLevel" class="a.class.implementing.PropertyDefiner">
    <shape>round</shape>
    <color>brown</color>
    <size>24</size>
  </define>
 
  <root level="${rootLevel}"/>
</configuration>

在上面的例子中,形状、颜色和大小是“a.class.implementing.PropertyDefiner”的属性。只要在PropertyDefiner实例的实现中有一个给定属性的setter方法,logback会将配置文件中指定的属性值注入到实例的属性中。

目前,logback附带了PropertyDefiner的两个相当简单的实现。

实现类 描述
CanonicalHostNamePropertyDefiner 将命名变量设置为本地主机的规范主机名。注意,获得规范主机名可能需要几秒钟。
FileExistsPropertyDefiner 如果path属性指定的文件存在,则将命名变量设置为“true”,否则设置为“false”
ResourceExistsPropertyDefiner 如果用户指定的资源在类路径上可用,则将命名变量设置为“true”,否则设置为“false”。

配置文件的条件处理

开发人员经常需要在几个针对不同环境(如开发、测试和生产)的logback配置文件之间来回切换。这些配置文件有很多共同之处,只是在一些地方有所不同。为了避免重复,logback<if><then><else>元素的帮助下支持配置文件的条件处理,这样一个配置文件就可以用于对多个环境。注意,条件处理需要Janino库。

条件语句的一般格式如下所示。

  <!-- if-then form -->
  <if condition="一些条件表达式">
    <then>
      ...
    </then>
  </if>
  
  <!-- if-then-else form -->
  <if condition="一些条件表达式">
    <then>
      ...
    </then>
    <else>
      ...
    </else>    
  </if>

该条件是一个 Java 表达式,其中只能访问上下文属性或系统属性。对于作为参数传递的键,property()或其简写p()方法返回该属性的字符串值。例如,要访问键为“k”的属性的值,您可以编写propert(“k”)或等价的p(“k”)。如果名称为"k"的属性未定义,属性方法将返回空字符串,而不是null。这避免了检查空值的需要。

isDefined()方法可用于检查是否定义了属性。例如,要检查属性“k”是否已定义,可以使用isDefined(“k”),如果需要检查属性是否为null,提供了isNull()方法。例如:isNull (“k”)

<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} - %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} - %msg %n</pattern>
   </encoder>
  </appender>

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

<configuration>元素中的任何位置都支持条件处理。还支持嵌套if-then-else语句。然而,XML语法非常麻烦,不适合作为通用编程语言的基础。因此,太多的条件将很快使后续读者(包括您自己)无法理解您的配置文件。

从JNDI获取变量

在某些情况下,您可能希望使用 JNDI 中存储的env-entries<insertFromJNDI>配置指令提取 JNDI 中存储的一个env-entry,并用as属性指定的键插入本地范围中的属性。所有属性,在scope属性的帮助下,可以将新属性插入不同的范围。

作为通过JNDI获得的属性env-entry插入:

<configuration>
  <insertFromJNDI env-entry-name="java:comp/env/appName" as="appName" />
  <contextName>${appName}</contextName>

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d ${CONTEXT_NAME} %level %msg %logger{50}%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>

在最后一个示例中,“java:comp/env/appName”作为appName属性插入。注意,<contextName>指令根据前面的<insertFromJNDI>指令插入的appName属性的值设置上下文名称。

文件包含

Joran 支持从另一个文件中包含配置文件的部分。这是通过声明一个<include>元素来实现的,如下所示:

<configuration>
  <include file="src/main/java/chapters/configuration/includedConfig.xml"/>

  <root level="DEBUG">
    <appender-ref ref="includedConsole" />
  </root>

</configuration>

目标文件必须将其元素嵌套在<include >元素中。例如,一个ConsoleAppender可以声明为:

<included>
  <appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>"%d - %m%n"</pattern>
    </encoder>
  </appender>
</included>

再次强调,必须使用<include>元素。

要包含的内容可以作为文件、资源或URL引用。

  • 作为一个文件:
    要包含文件,请使用file属性。您可以使用相对路径,但是请注意,当前目录是由应用程序定义的,并不一定与配置文件的路径相关。
  • 作为一个资源:
    为了包含资源,比如在类路径上找到一个文件,使用resource属性。
    <include resource="includedConfig.xml"/>
  • 作为一个 URL:
    要包含 URL 的内容,请使用 URL 属性。
    <include url="http://some.host.com/includedConfig.xml"/>

如果找不到要包含的文件,logback将通过打印状态消息来提示。如果包含的文件是可选的,您可以通过将<include>元素中的可选属性设置为true来抑制警告消息。
<include optional="true" ..../>

添加上下文侦听器

LoggerContextListener接口的实例侦听与日志记录器上下文的生命周期相关的事件。

JMXConfiguratorLoggerContextListener接口的一个实现。它将在下一章进行描述。

#######LevelChangePropagator
0.9.25版开始,logback-classic附带LevelChangePropagator,这是LoggerContextListener的实现,它将任何logback-classic日志程序级别的更改传播到java.util.logging框架。这种传播消除了禁用日志语句的性能影响。LogRecord实例将仅通过启用的日志语句发送到logback(通过SLF4J)。这使得实际应用程序可以合理地使用jul-to-slf4j桥接。

contextListener元素可用于安装LevelChangePropagator,如下所示。

<configuration debug="true">
  <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/>
  .... 
</configuration>

设置LevelChangePropagatorresetJUL属性将重置所有j.u.l.日志记录器的所有以前的级别配置。但是,以前安装的处理程序将保持不变。

<configuration debug="true">
  <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
    <resetJUL>true</resetJUL>
  </contextListener>
  ....
</configuration>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容