第三章 Logback 配置

第三章 Logback 配置

        我们首先介绍配置 logback 的方法,以及许多示例配置脚本。logback所依赖的配置框架将在 下一章 中介绍。

logback 中的配置

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

        Logback 可以编程配置,也可以使用 XML 或 Groovy 格式的配置脚本进行配置。顺便说一下,现有的 log4j 用户可以将他们的log4j.properties 文件转换为logback.xml文件使用我们的PropertiesTranslator web应用程序。

        让我们从讨论 logback 尝试配置自身所遵循的初始化步骤开始:

  1. Logback 试图在 类路径中 找到一个名为 logback-test.xml 的文件。
  2. 如果没有找到这样的文件,logback会尝试在在类路径中 找到一个名为 logback.groovy 的文件。
  3. 如果上面的文件都没有找到,那么 Logback 会在类路径中查找 logback.xml 文件。
  4. 如果找不到上面的文件,服务提供商加载工具(在JDK1.6中引入)用于解决com.qos.logback.classic.spi.Configurator 接口的实现,通过查找类路径下的 META-INF\services\ch.qos.logback.classic.spi.Configurator 文件。它的内容应该指定所需的“Configurator”实现的完全限定类名。
  5. 如果上述操作均未成功,logback将使用BasicConfigurator自动配置自身这将导致日志输出被定向到控制台。

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

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

        快速启动Joran解析给定的logback配置文件大约需要100毫秒。为了在应用程序启动时减少这些毫秒,您可以使用服务提供商加载工具(上面的第4项)用 BasicConfigrator 加载您自己的自定义 Configurator 类作为一个良好的起点。

自动配置logback

        配置logback最简单的方法是让logback返回到其默认配置。让我们来看看如何在一个名为 MyApp1 的虚拟应用程序中实现这一点。

        示例:BasicConfigurator 用法的简单示例(logback examples/src/main/java/chapters/configuration/MyApp1.java)

package chapters.configuration;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
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 类如下所示:

        示例: 执行日志记录的小型类 (logback-examples/src/main/java/chapters/configuration/Foo.java)

package chapters.configuration;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class Foo {
  static final Logger logger = LoggerFactory.getLogger(Foo.class);
 
  public void doIt() {
    logger.debug("Did it again!");
  }
}

        为了运行本章中的示例,您需要确保某些jar文件存在于类路径中。请参阅设置页更多细节。

        假设配置文件 logback-test.xml 或者 logback.xml 文件如果不存在,则logback将默认为调用BasicConfigurator这将设置一个最小配置。此最小配置由附加到根记录器的 ConsoleAppender 组成。输出格式使用 PatternLayoutEncoder 设置格式为 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n。此外,默认情况下,根记录器被分配 DEBUG 级别。

        因此,执行命令 java chapters.configuration.MyApp1 的输出如下所示:

16:06:09.031 [main] INFO  chapters.configuration.MyApp1 - Entering application. 
16:06:09.046 [main] DEBUG chapters.configuration.Foo - Did it again!
16:06:09.046 [main] INFO  chapters.configuration.MyApp1 - Exiting application.

        除了配置logback的代码(如果存在这样的代码)之外,客户机代码不需要依赖于logback。这意思也就是说,我们无论在使用 Logger 的过程中,是不需要接触到 Logback的。使用logback作为日志框架的应用程序在编译时依赖于SLF4J,而不是logback。

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

自动配置 logback-test.xmllogback.xml

        如前所述,如果在类路径上找到 logback-test.xml 或者logback.xml 文件,logback将尝试使用这些文件配置自己。下面显示了一个配置文件,相当于我们刚才看到的由 BasicConfigurator 建立的配置文件。

示例: 基本配置示例 (logback-examples/src/main/resources/chapters/configuration/sample0.xml)

<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} - %msg%n</pattern>
    </encoder>
  </appender>
 
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

        将 sample0.xml 重命名为 logback.xml(或 logback-test.xml) 将其放入可从类路径访问的目录中。运行MyApp1应用程序会得到与上次运行相同的结果。

出现警告或错误时自动打印状态消息

        如果在解析配置文件时出现警告或错误,logback将在控制台上自动打印其内部状态消息。

        如果在解析配置文件期间出现警告或错误,logback将在控制台上自动打印其内部状态数据。请注意,为了避免重复,如果用户显式注册状态侦听器(定义如下),则会禁用自动状态打印。

        在没有警告或错误的情况下,如果仍希望检查logback的内部状态,则可以通过调用 StatusPrinter 类的 print() 方法来指示logback打印状态数据。下面显示的MyApp2应用程序与MyApp1相同,只是增加了两行用于打印内部状态数据的代码。

        示例: 打印logback的内部状态信息 (logback-examples/src/main/java/chapters/configuration/MyApp2.java)

public static void main(String[] args) {
  // assume SLF4J is bound to logback in the current environment
  LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
  // print logback's internal status
  StatusPrinter.print(lc);
  ...
}

        如果一切顺利,您应该在控制台上看到以下输出

17:44:58,578 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml]
17:44:58,671 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
17:44:58,671 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
17:44:58,687 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Popping appender named [STDOUT] from the object stack
17:44:58,812 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - root level set to DEBUG
17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[root]
 
17:44:58.828 [main] INFO  chapters.configuration.MyApp2 - Entering application.
17:44:58.828 [main] DEBUG chapters.configuration.Foo - Did it again!
17:44:58.828 [main] INFO  chapters.configuration.MyApp2 - Exiting application.

        在输出的最后,您可以识别上一个示例中打印的行。您还应该注意logback的内部消息,即 a.k.a.Status 对象,它允许方便地访问logback的内部状态。

状态数据

        在使用logback诊断问题时,启用状态数据的输出通常有很大的帮助。因此,强烈建议将其视为首选手段。

        即使我们的配置文件没有错误,日志也可以正常的打印,这时候我们也可以打印一下目前日志中的一些内部状态,我们不应该在代码中使用上面的那种方式打印状态信息,上面的那种方式是直接写在代码里使用 StatusPrinter 的方法来打印,这种方式是不推荐的,推荐使用配置文件中通过配置来打印内部的状态。要实现这一点,就需要将配置文件中的 configuration 元素的 debug 属性设置成 true,这个 configuration 元素是 Logback 配置文件的顶层元素,下面的示例向我们展示了这一点请注意这个 debug 仅仅和 Logback 的内部状态是否打印有关,和我们所知道的日志输出级别是两码事。所以,这个 debug 不会影响我们的日志记录的级别。

示例: 基本配置文件使用 debug 模式 (logback-examples/src/main/resources/chapters/configuration/sample1.xml)

<configuration debug="true">
 
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are  by default assigned the type
         ch.qos.logback.classic.encoder.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" , 加上这个属性以后,就可以打印 Logback 内部的一些信息了,现在我们假设:

  1. 我们已经配置了配置文件,也就是我们在工程中的 resources 文件夹下面创建了 logback-test.xml 或者其他logback 能够识别的配置文件,并且这个文件能够被 Logback 识别到
  2. 我们不止创建了这样的一个,按照 Logback 规定命名的配置文件,而且还按照 Logback 的配置要求进行了一定的配置,而且也符合 XML 的格式规范。

        如果上面列出来的两个条件中,有任意的一个条件没有满足,那么 Joran 不能够正确的解析上面在顶层元素中配置的 debug="true",因为如果不满足第一个条件的话,那么这个文件都找不到了,更不用说解析了,如果是第二个条件不满足,比如说我们的xml中有配置错误的情况,这个时候 Logback 是可以去帮助我们打印内部的状态的。以编程的方式调用 StatusPrinter.print() 方法来打印 Logback 的内部状态,在上面的例子中已经展示过了,使用这种编程的方式,是在任何情况下都会打印 Logback 的内部状态的。

        比如说有些情况下,我们的配置文件配置的位置或者是其他原因,我们即使配置了 debug="true" 也没有办法将 Logback 的状态输出,这时候如果我们还是想要输出内部的状态当然可以使用上面说的 StatusPrinter.print() 方法来打印内部状态,但是对于生产环境来说,一般来说我们是很难去做出上线变更的,或者是上线变更是非常谨慎和小心的,一般情况下,没有特殊的原因是要避免重复上线的,所以如何在不修改代码的情况下,还要打印内部状态就成了一个问题,这时候 Logback 配置文件又没有正常的运行,我们需要其他的手段来打印 Logback 的内部状态。为了帮助定位这些表现不是很正常的 Logback 的配置文件的位置,我们可以通过 "logback.statusListenerClass" 系统属性(defined below)的方式设置 StatusListener,这样就可以强制输出状态信息了。这个 "logback.statusListenerClass" 系统属性(defined below)也可以在出现错误的时候自动输出错误信息。

        顺便说一下,我们在 logback.xml 文件中顶层元素 configuration 中设置 debug="true" 和在 configuration 元素下面使用下面的方式效果是一样的,下面就展示了 OnConsoleStatusListener 的用法:

示例: 注册状态监听器 (logback-examples/src/main/resources/chapters/configuration/onConsoleStatusListener.xml)

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
 
  ... the rest of the configuration file
</configuration>

        如上面演示的,无论是通过在 logback.xml 中的 configuration 元素中开启 debug,还是使用 OnConsoleStatusListener 来进行状态的监听,他们所达到的效果都是一样的,就是输出 Logback 的内部状态,这有助于我们解决在使用 Logback 中遇到的一些问题。所以,强烈建议启用 Logback 的状态输出,不管用上面的哪种方式,最好是有这个内部状态的输出,这样可以很方便我们定位问题,我们应该将开启这个配置作为非常重要的首选配置。

Specifying the location of the default configuration file as a system property

You may specify the location of the default configuration file with a system property named "logback.configurationFile". The value of this property can be a URL, a resource on the class path or a path to a file external to the application.

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

Note that the file extension must be ".xml" or ".groovy". Other extensions are ignored. Explicitly registering a status listener may help debugging issues locating the configuration file.

Given that "logback.configurationFile" is a Java system property, it may be set within your application as well. However, the system property must be set before any logger instance is created.

import ch.qos.logback.classic.util.ContextInitializer;
 
public class ServerMain {
    public static void main(String args[]) throws IOException, InterruptedException {
       // must be set before the first call to  LoggerFactory.getLogger();
       // ContextInitializer.CONFIG_FILE_PROPERTY is set to "logback.configurationFile"
       System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, "/path/to/config.xml");
       ...
    }
}

Automatically reloading configuration file upon modification

Logback-classic can scan for changes in its configuration file and automatically reconfigure itself when the configuration file changes.

If instructed to do so, logback-classic will scan for changes in its configuration file and automatically reconfigure itself when the configuration file changes. In order to instruct logback-classic to scan for changes in its configuration file and to automatically re-configure itself set the scan attribute of the <configuration> element to true, as shown next.

Example: Scanning for changes in configuration file and automatic re-configuration (logback-examples/src/main/resources/chapters/configuration/scan1.xml)

View as .groovy

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

By default, the configuration file will be scanned for changes once every minute. You can specify a different scanning period by setting the scanPeriod attribute of the <configuration> element. Values can be specified in units of milliseconds, seconds, minutes or hours. Here is an example:

Example: Specifying a different scanning period (logback-examples/src/main/resources/chapters/configuration/scan2.xml)

View as .groovy

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

Note If no unit of time is specified, then the unit of time is assumed to be milliseconds, which is usually inappropriate. If you change the default scanning period, do not forget to specify a time unit.

Behind the scenes, when you set the scan attribute to true, a ReconfigureOnChangeTask will be installed. This task run in a separate thread and will check whether your configuration file has changed. ReconfigureOnChangeTask will automatically watch for any included files as well.

As it is easy to make errors while editing a configuration file, in case the latest version of the configuration file has XML syntax errors, it will fall back to a previous configuration file free of XML syntax errors.

Enabling packaging data in stack traces

While useful, packaging data is expensive to compute, especially in applications with frequent exceptions.

NOTE As of version 1.1.4, packaging data is disabled by default.

If instructed to do so, logback can include packaging data for each line of the stack trace lines it outputs. Packaging data consists of the name and version of the jar file whence the class of the stack trace line originated. Packaging data can be very useful in identifying software versioning issues. However, it is rather expensive to compute, especially in application where exceptions are thrown frequently. Here is a sample output:

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]

Packaging data is disabled by default but can be enabled by configuration:

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

Alternatively, packaging data can be enabled/disabled programmatically by invoking the setPackagingDataEnabled(boolean) method in LoggerContext, as shown next:

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

Invoking JoranConfigurator directly

Logback relies on a configuration library called Joran, part of logback-core. Logback's default configuration mechanism invokes JoranConfigurator on the default configuration file it finds on the class path. If you wish to override logback's default configuration mechanism for whatever reason, you can do so by invoking JoranConfigurator directly. The next application, MyApp3, invokes JoranConfigurator on a configuration file passed as a parameter.

Example: Invoking JoranConfigurator directly (logback-examples/src/main/java/chapters/configuration/MyApp3.java)

package chapters.configuration;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
 
public class MyApp3 {
  final static Logger logger = LoggerFactory.getLogger(MyApp3.class);
 
  public static void main(String[] args) {
    // assume SLF4J is bound to logback in the current environment
    LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
 
    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(context);
      // Call context.reset() to clear any previous configuration, e.g. default
      // configuration. For multi-step configuration, omit calling context.reset().
      context.reset();
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
      // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(context);
 
    logger.info("Entering application.");
 
    Foo foo = new Foo();
    foo.doIt();
    logger.info("Exiting application.");
  }
}

This application fetches the LoggerContext currently in effect, creates a new JoranConfigurator, sets the context on which it will operate, resets the logger context, and then finally asks the configurator to configure the context using the configuration file passed as a parameter to the application. Internal status data is printed in case of warnings or errors. Note that for multi-step configuration, context.reset() invocation should be omitted.

Viewing status messages

Logback collects its internal status data in a StatusManager object, accessible via the LoggerContext.

Given a StatusManager you can access all the status data associated with a logback context. To keep memory usage at reasonable levels, the default StatusManager implementation stores the status messages in two separate parts: the header part and the tail part. The header part stores the first H status messages whereas the tail part stores the last T messages. At present time H=T=150, although these values may change in future releases.

Logback-classic ships with a servlet called ViewStatusMessagesServlet. This servlet prints the contents of the StatusManager associated with the current LoggerContext as an HTML table. Here is sample output.

To add this servlet to your web-application, add the following lines to its WEB-INF/web.xml file.

  <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>

The ViewStatusMessages servlet will be viewable at the URL http://host/yourWebapp/lbClassicStatus

Listening to status messages

You may also attach a StatusListener to a StatusManager so that you can take immediate action in response to status messages, especially to messages occurring after logback configuration. Registering a status listener is a convenient way to supervise logback's internal state without human intervention.

Logback ships with a StatusListener implementation called OnConsoleStatusListener which, as its name indicates, prints all new incoming status messages on the console.

Here is sample code to register an OnConsoleStatusListener instance with the StatusManager.

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

Note that the registered status listener will only receive status events subsequent to its registration. It will not receive prior messages. Thus, it is usually a good idea to place status listener registration directives at top of the configuration file before other directives.

It is also possible to register one or more status listeners within a configuration file. Here is an example.

Example: Registering a status listener (logback-examples/src/main/resources/chapters/configuration/onConsoleStatusListener.xml)

View as .groovy

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
 
  ... the rest of the configuration file
</configuration>

"logback.statusListenerClass" system property

One may also register a status listener by setting the "logback.statusListenerClass" Java system property to the name of the listener class you wish to register. For example,

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

Logback ships with several status listener implementations. OnConsoleStatusListener prints incoming status messages on the console, i.e. on System.out. OnErrorConsoleStatusListener prints incoming status messages on System.err. NopStatusListener drops incoming status messages.

Note that automatic status printing (in case of errors) is disabled if any status listener is registered during configuration and in particular if the user specifies a status listener via the "logback.statusListenerClass" system. Thus, by setting NopStatusListener as a status listener, you can silence internal status printing altogether.

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

Stopping logback-classic

In order to release the resources used by logback-classic, it is always a good idea to stop the logback context. Stopping the context will close all appenders attached to loggers defined by the context and stop any active threads in an orderly way. Please also read the section on "shutdown hooks" just below.

import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...
 
// assume SLF4J is bound to logback-classic in the current environment
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();

In web-applications the above code could be invoked from within the contextDestroyed method of ServletContextListener in order to stop logback-classic and release resources. Starting with version 1.1.10, the appropriate ServletContextListener is installed automatically for you (see just below).

Stopping logback-classic via a shutdown hook

Installing a JVM shutdown hook is a convenient way for shutting down logback and releasing associated resources.

<configuration debug="true">
   <!-- in the absence of the class attribute, assume
   ch.qos.logback.core.hook.DefaultShutdownHook -->
   <shutdownHook/>
  ....
</configuration>

Note that you may install a shutdown hook of your own making by setting the class attribute to correspond to your shutdown hook's class name.

The default shutdown hook, namely DefaultShutdownHook, will stop the logback context after a specified delay (0 by default). Stopping the context will allow up to 30 seconds for any log file compression tasks running in the background to finish. In standalone Java applications, adding a <shutdownHook/> directive to your configuration file is an easy way to ensure that any ongoing compression tasks are allowed to finish before JVM exit. In applications within a Web server, webShutdownHook will be installed automatically making <shutdownHook/> directive quite redundant and unnecessary.

WebShutdownHook or stopping logback-classic in web-applications

since 1.1.10 Logback-classic will automatically ask the web-server to install a LogbackServletContainerInitializer implementing the ServletContainerInitializer interface (available in servlet-api 3.x and later). This initializer will in turn install and instance of LogbackServletContextListener. This listener will stop the current logback-classic context when the web-app is stopped or reloaded.

You may disable the automatic the installation of LogbackServletContextListener by setting a <context-param> named logbackDisableServletContainerInitializer in your web-application's web.xml file. Here is the relevant snippet.

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

Note that logbackDisableServletContainerInitializer variable can also be set as a Java system property an OS environment variable. The most local setting has priority, i.e. web-app first, system property second and OS environment last.

Configuration file syntax

As you have seen thus far in the manual with plenty of examples still to follow, logback allows you to redefine logging behavior without needing to recompile your code. Indeed, you can easily configure logback so as to disable logging for certain parts of your application, or direct output to a UNIX Syslog daemon, to a database, to a log visualizer, or forward logging events to a remote logback server, which would log according to local server policy, for example by forwarding the log event to a second logback server.

The remainder of this section presents the syntax of configuration files.

As will be demonstrated over and over, the syntax of logback configuration files is extremely flexible. As such, it is not possible to specify the allowed syntax with a DTD file or an XML schema. Nevertheless, the very basic structure of the configuration file can be described as, <configuration> element, containing zero or more <appender> elements, followed by zero or more <logger> elements, followed by at most one <root> element. The following diagram illustrates this basic structure.

![basic Syntax](https://upload-images.jianshu.io/upload_images/25275979-7433216bb6226a24.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

If you are unsure which case to use for a given tag name, just follow the camelCase convention which is almost always the correct convention.

Case sensitivity of tag names

Since logback version 0.9.17, tag names pertaining to explicit rules are case insensitive. For example, <logger>, <Logger> and <LOGGER> are valid configuration elements and will be interpreted in the same way. Note that XML well-formedness rules still apply, if you open a tag as <xyz> you must close it as </xyz>, </XyZ> will not work. As for implicit rules, tag names are case sensitive except for the first letter. Thus, <xyz> and <Xyz> are equivalent but not <xYz>. Implicit rules usually follow the camelCase convention, common in the Java world. Since it is not easy to tell when a tag is associated with an explicit action and when it is associated with an implicit action, it is not trivial to say whether an XML tag is case-sensitive or insensitive with respect to the first letter. If you are unsure which case to use for a given tag name, just follow the camelCase convention which is almost always the correct convention.

Configuring loggers, or the <logger> element

At this point you should have at least some understanding of level inheritance and the basic selection rule. Otherwise, and unless you are an Egyptologist, logback configuration will be no more meaningful to you than are hieroglyphics.

A logger is configured using the <logger> element. A <logger> element takes exactly one mandatory name attribute, an optional level attribute, and an optional additivity attribute, admitting the values true or false. The value of the level attribute admitting one of the case-insensitive string values TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF. The special case-insensitive value INHERITED, or its synonym NULL, will force the level of the logger to be inherited from higher up in the hierarchy. This comes in handy if you set the level of a logger and later decide that it should inherit its level.

Note that unlike log4j, logback-classic does not close nor remove any previously referenced appenders when configuring a given logger.

The <logger> element may contain zero or more <appender-ref> elements; each appender thus referenced is added to the named logger. Note that unlike log4j, logback-classic does not close nor remove any previously referenced appenders when configuring a given logger.

Configuring the root logger, or the <root> element

The <root> element configures the root logger. It supports a single attribute, namely the level attribute. It does not allow any other attributes because the additivity flag does not apply to the root logger. Moreover, since the root logger is already named as "ROOT", it does not allow a name attribute either. The value of the level attribute can be one of the case-insensitive strings TRACE, DEBUG, INFO, WARN, ERROR, ALL or OFF. Note that the level of the root logger cannot be set to INHERITED or NULL.

Note that unlike log4j, logback-classic does not close nor remove any previously referenced appenders when configuring the root logger.

Similarly to the <logger> element, the <root> element may contain zero or more <appender-ref> elements; each appender thus referenced is added to the root logger. Note that unlike log4j, logback-classic does not close nor remove any previously referenced appenders when configuring the root logger.

Example

Setting the level of a logger or root logger is as simple as declaring it and setting its level, as the next example illustrates. Suppose we are no longer interested in seeing any DEBUG messages from any component belonging to the "chapters.configuration" package. The following configuration file shows how to achieve that.

Example: Setting the level of a logger (logback-examples/src/main/resources/chapters/configuration/sample2.xml)

View as .groovy

<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} - %msg%n</pattern>
    </encoder>
  </appender>
 
  <logger name="chapters.configuration" level="INFO"/>
 
  <!-- Strictly speaking, the level attribute is not necessary since -->
  <!-- the level of the root level is set to DEBUG by default.       -->
  <root level="DEBUG">   
    <appender-ref ref="STDOUT" />
  </root>
 
</configuration>

When the above configuration file is given as argument to the MyApp3 application, it will yield the following output:

17:34:07.578 [main] INFO  chapters.configuration.MyApp3 - Entering application.
17:34:07.578 [main] INFO  chapters.configuration.MyApp3 - Exiting application.

Note that the message of level DEBUG generated by the "chapters.configuration.Foo" logger has been suppressed. See also the Foo class.

You can configure the levels of as many loggers as you wish. In the next configuration file, we set the level of the chapters.configuration logger to INFO but at the same time set the level of the chapters.configuration.Foo logger to DEBUG.

Example: Setting the level of multiple loggers (logback-examples/src/main/resources/chapters/configuration/sample3.xml)

View as .groovy

<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="chapters.configuration" level="INFO" />
  <logger name="chapters.configuration.Foo" level="DEBUG" />
 
  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
 
</configuration>

Running MyApp3 with this configuration file will result in the following output on the console:

17:39:27.593 [main] INFO chapters.configuration.MyApp3 - Entering application. 17:39:27.593 [main] DEBUG chapters.configuration.Foo - Did it again! 17:39:27.593 [main] INFO chapters.configuration.MyApp3 - Exiting application.

The table below list the loggers and their levels, after JoranConfigurator has configured logback with the sample3.xml configuration file.

Logger name Assigned Level Effective Level
root DEBUG DEBUG
chapters.configuration INFO INFO
chapters.configuration.MyApp3 null INFO
chapters.configuration.Foo DEBUG DEBUG

It follows that the two logging statements of level INFO in the MyApp3 class as well as the DEBUG messages in Foo.doIt() are all enabled. Note that the level of the root logger is always set to a non-null value, DEBUG by default.

Let us note that the basic-selection rule depends on the effective level of the logger being invoked, not the level of the logger where appenders are attached. Logback will first determine whether a logging statement is enabled or not, and if enabled, it will invoke the appenders found in the logger hierarchy, regardless of their level. The configuration file sample4.xml is a case in point:

Example: Logger level sample (logback-examples/src/main/resources/chapters/configuration/sample4.xml)

View as .groovy

<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="chapters.configuration" level="INFO" />
 
  <!-- turn OFF all logging (children can override) -->
  <root level="OFF">
    <appender-ref ref="STDOUT" />
  </root>
 
</configuration>

The following table lists the loggers and their levels after applying the sample4.xml configuration file.

Logger name Assigned Level Effective Level
root OFF OFF
chapters.configuration INFO INFO
chapters.configuration.MyApp3 null INFO
chapters.configuration.Foo null INFO

The ConsoleAppender named STDOUT, the only configured appender in sample4.xml, is attached to the root logger whose level is set to OFF. However, running MyApp3 with configuration script sample4.xml will yield:

17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Entering application.
17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Exiting application.

Thus, the level of the root logger has no apparent effect because the loggers in chapters.configuration.MyApp3 and chapters.configuration.Foo classes are all enabled for the INFO level. As a side note, the chapters.configuration logger exists by virtue of its declaration in the configuration file - even if the Java source code does not directly refer to it.

Configuring Appenders

An appender is configured with the <appender> element, which takes two mandatory attributes name and class. The name attribute specifies the name of the appender whereas the class attribute specifies the fully qualified name of the appender class to instantiate. The <appender> element may contain zero or one <layout> elements, zero or more <encoder> elements and zero or more <filter> elements. Apart from these three common elements, <appender> elements may contain any number of elements corresponding to JavaBean properties of the appender class. Seamlessly supporting any property of a given logback component is one of the major strengths of Joran as discussed in a later chapter. The following diagram illustrates the common structure. Note that support for properties is not visible.

![Appender Syntax](https://upload-images.jianshu.io/upload_images/25275979-2ce4fd8b339240bd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

The <layout> element takes a mandatory class attribute specifying the fully qualified name of the layout class to instantiate. As with the <appender> element, <layout> may contain other elements corresponding to properties of the layout instance. Since it's such a common case, if the layout class is PatternLayout, then the class attribute can be omitted as specified by default class mapping rules.

The <encoder> element takes a mandatory class attribute specifying the fully qualified name of the encoder class to instantiate. Since it's such a common case, if the encoder class is PatternLayoutEncoder, then the class attribute can be omitted as specified by default class mapping rules.

Logging to multiple appenders is as easy as defining the various appenders and referencing them in a logger, as the next configuration file illustrates:

Example: Multiple loggers (logback-examples/src/main/resources/chapters/configuration/multiple.xml)

View as .groovy

<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>

These configuration scripts define two appenders called FILE and STDOUT. The FILE appender logs to a file called myApp.log. The encoder for this appender is a PatternLayoutEncoder that outputs the date, level, thread name, logger name, file name and line number where the log request is located, the message and line separator character(s). The second appender called STDOUT outputs to the console. The encoder for this appender outputs only the message string followed by a line separator.

The appenders are attached to the root logger by referencing them by name within an appender-ref element. Note that each appender has its own encoder. Encoders are usually not designed to be shared by multiple appenders. The same is true for layouts. As such, logback configuration files do not provide any syntactical means for sharing encoders or layouts.

Appenders accumulate

By default, appenders are cumulative: a logger will log to the appenders attached to itself (if any) as well as all the appenders attached to its ancestors. Thus, attaching the same appender to multiple loggers will cause logging output to be duplicated.

Example: Duplicate appender (logback-examples/src/main/resources/chapters/configuration/duplicate.xml)

View as .groovy

<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="chapters.configuration">
    <appender-ref ref="STDOUT" />
  </logger>
 
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Running MyApp3 with duplicate.xml will yield the following output:

14:25:36.343 [main] INFO chapters.configuration.MyApp3 - Entering application. 14:25:36.343 [main] INFO chapters.configuration.MyApp3 - Entering application. 14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again! 14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again! 14:25:36.359 [main] INFO chapters.configuration.MyApp3 - Exiting application. 14:25:36.359 [main] INFO chapters.configuration.MyApp3 - Exiting application.

Notice the duplicated output. The appender named STDOUT is attached to two loggers, to root and to chapters.configuration. Since the root logger is the ancestor of all loggers and chapters.configuration is the parent of both chapters.configuration.MyApp3 and chapters.configuration.Foo, each logging request made with these two loggers will be output twice, once because STDOUT is attached to chapters.configuration and once because it is attached to root.

Appender additivity is not intended as a trap for new users. It is quite a convenient logback feature. For instance, you can configure logging such that log messages appear on the console (for all loggers in the system) while messages only from some specific set of loggers flow into a specific appender.

Example: Multiple appender (logback-examples/src/main/resources/chapters/configuration/restricted.xml)

View as .groovy

<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="chapters.configuration">
    <appender-ref ref="FILE" />
  </logger>
 
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

In this example, the console appender will log all the messages (for all loggers in the system) whereas only logging requests originating from the chapters.configuration logger and its children will go into the myApp.log file.

Overriding the default cumulative behaviour

In case the default cumulative behavior turns out to be unsuitable for your needs, you can override it by setting the additivity flag to false. Thus, a branch in your logger tree may direct output to a set of appenders different from those of the rest of the tree.

Example: Additivity flag (logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml)

View as .groovy

<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="chapters.configuration.Foo" additivity="false">
    <appender-ref ref="FILE" />
  </logger>
 
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

This example, the appender named FILE is attached to the chapters.configuration.Foo logger. Moreover, the chapters.configuration.Foo logger has its additivity flag set to false such that its logging output will be sent to the appender named FILE but not to any appender attached higher in the hierarchy. Other loggers remain oblivious to the additivity setting of the chapters.configuration.Foo logger. Running the MyApp3 application with the additivityFlag.xml configuration file will output results on the console from the chapters.configuration.MyApp3 logger. However, output from the chapters.configuration.Foo logger will appear in the foo.log file and only in that file.

Setting the context name

As mentioned in an earlier chapter, every logger is attached to a logger context. By default, the logger context is called "default". However, you can set a different name with the help of the <contextName> configuration directive. Note that once set, the logger context name cannot be changed. Setting the context name is a simple and straightforward method in order to distinguish between multiple applications logging to the same target.

Example: Set the context name and display it (logback-examples/src/main/resources/chapters/configuration/contextName.xml)

View as .groovy

<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>

This last example illustrates naming of the logger context. Adding the contextName conversion word in layout's pattern will output the said name.

Variable substitution

Note Earlier versions of this document used the term "property substitution" instead of the term "variable". Please consider both terms interchangeable although the latter term conveys a clearer meaning.

As in many scripting languages, logback configuration files support definition and substitution of variables. Variables have a scope (see below). Moreover, variables can be defined within the configuration file itself, in an external file, in an external resource or even computed and defined on the fly.

Variable substitution can occur at any point in a configuration file where a value can be specified.

Variable substitution can occur at any point in a configuration file where a value can be specified. The syntax of variable substitution is similar to that of Unix shells. The string between an opening *{* and closing *}* is interpreted as a reference to the *value* of the property. For property *aName*, the string "{aName}" will be replaced with the value held by the aName property.

As they often come in handy, the HOSTNAME and CONTEXT_NAME variables are automatically defined and have context scope. Given that in some environments it may take some time to compute the hostname, its value is computed lazily (only when needed). Moreover, HOSTNAME can be set from within the configuration directly.

Defining variables

Variables can be defined one at a time in the configuration file itself or loaded wholesale from an external properties file or an external resource. For historical reasons, the XML element for defining variables is <property> although in logback 1.0.7 and later the element <variable> can be used interchangeably.

The next example shows a variable declared at the beginning of the configuration file. It is then used further down the file to specify the location of the output file.

Example: Simple Variable substitution (logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml)

View as .groovy

<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>

The next example shows the use of a System property to achieve the same result. The property is not declared in the configuration file, thus logback will look for it in the System properties. Java system properties can be set on the command line as shown next:

java -DUSER_HOME="/home/sebastien" MyApp2

Example: System Variable substitution (logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml)

View as .groovy

<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>

When multiple variables are needed, it may be more convenient to create a separate file that will contain all the variables. Here is how one can do such a setup.

Example: Variable substitution using a separate file (logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml)

View as .groovy

<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>

This configuration file contains a reference to a file named variables1.properties. The variables contained in that file will be read and then defined within local scope. Here is what the variable.properties file might look like.

Example: Variable file (logback-examples/src/main/resources/chapters/configuration/variables1.properties)

USER_HOME=/home/sebastien

You may also reference a resource on the class path instead of a file.

<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>

Scopes

A property can be defined for insertion in local scope, in context scope, or in system scope. Local scope is the default. Although it is possible to read variables from the OS environment, it is not possible to write into the OS environment.

local scope A property with local scope exists from the point of its definition in a configuration file until the end of interpretation/execution of said configuration file. As a corollary, each time a configuration file is parsed and executed, variables in local scope are defined anew.

context scope A property with context scope is inserted into the context and lasts as long as the context or until it is cleared. Once defined, a property in context scope is part of the context. As such, it is available in all logging events, including those sent to remote hosts via serialization.

system scope A property with system scope is inserted into the JVM's system properties and lasts as long as the JVM or until it is cleared.

Properties are looked up in the the local scope first, in the context scope second, in the system properties scope third, and in the OS environment last.

During substitution, properties are looked up in the local scope first, in the context scope second, in the system properties scope third, and in the OS environment fourth and last.

The scope attribute of the <property> element, <define> element or the <insertFromJNDI> element can be used to set the scope of a property. The scope attribute admits "local", "context" and "system" strings as possible values. If not specified, the scope is always assumed to be "local".

Example: A variable defined in "context" scope (logback-examples/src/main/resources/chapters/configuration/contextScopedVariable.xml)

View as .groovy

<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>

In the above example, given that the nodeId property is defined in the context scope, it will be available in every logging event, even those sent to remote hosts via serialization.

Default values for variables

Under certain circumstances, it may be desirable for a variable to have a default value if it is not declared or its value is null. As in the Bash shell, default values can be specified using the ":-" operator. For example, assuming the variable named aName is not defined, "${aName**:-golden**}" will be interpreted as "golden".

Nested variables

Variable nesting is fully supported. Both the name, default-value and value definition of a variable can reference other variables.

value nesting

The value definition of a variable can contain references to other variables. Suppose you wish to use variables to specify not only the destination directory but also the file name, and combine those two variables in a third variable called "destination". The properties file shown below gives an example.

Example: Nested variable references (logback-examples/src/main/resources/chapters/configuration/variables2.properties)

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

Note that in the properties file above, "destination" is composed from two other variables, namely "USER_HOME" and "fileName".

Example: Variable substitution using a separate file (logback-examples/src/main/resources/chapters/configuration/variableSubstitution4.xml)

<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>

name nesting

When referencing a variable, the variable name may contain a reference to another variable. For example, if the variable named "userid" is assigned the value "alice", then "{{userid}.password}" references the variable with the name "alice.password".

default value nesting

The default value of a variable can reference a another variable. For example, assuming the variable 'id' is unassigned and the variable 'userid' is assigned the value "alice", then the expression "{id**:-**{userid}}" will return "alice".

HOSTNAME property

As it often comes in handy, the HOSTNAME property is defined automatically during configuration with context scope.

CONTEXT_NAME property

As its name indicates, the CONTEXT_NAME property corresponds to the name of the current logging context.

Setting a timestamp

The timestamp element can define a property according to current date and time. The timestamp element is explained in a subsequent chapter.

Defining properties on the fly

You may define properties dynamically using the <define> element. The define element takes two mandatory attributes: name and class. The name attribute designates the name of the property to set whereas the class attribute designates any class implementing the PropertyDefiner interface. The value returned by the getPropertyValue() method of the PropertyDefiner instance will be the value of the named property. You may also specify a scope for the named property by specifying a scope attribute.

Here is an example.

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

In the above example, shape, color and size are properties of "a.class.implementing.PropertyDefiner". As long as there is a setter for a given property in your implementation of the PropertyDefiner instance, logback will inject the appropriate values as specified in the configuration file.

At the present time, logback does ships with two fairly simple implementations of PropertyDefiner.

Implementation name Description
CanonicalHostNamePropertyDefiner Set the named variable to the canonical host name of the local host. Note that obtaining the canonical host name may take several seconds.
FileExistsPropertyDefiner Set the named variable to "true" if the file specified by path property exists, to "false" otherwise.
ResourceExistsPropertyDefiner Set the named variable to "true" if the resource specified by the user is available on the class path, to "false" otherwise.

Conditional processing of configuration files

Developers often need to juggle between several logback configuration files targeting different environments such as development, testing and production. These configuration files have substantial parts in common differing only in a few places. To avoid duplication, logback supports conditional processing of configuration files with the help of <if>, <then> and <else> elements so that a single configuration file can adequately target several environments. Note that conditional processing requires the Janino library.

The general format for conditional statements is shown below.

   <!-- if-then form -->
   <if condition="some conditional expression">
    <then>
      ...
    </then>
  </if>
 
  <!-- if-then-else form -->
  <if condition="some conditional expression">
    <then>
      ...
    </then>
    <else>
      ...
    </else>
  </if>

The condition is a Java expression in which only context properties or system properties are accessible. For a key passed as argument, the property() or its shorter equivalent p() methods return the String value of the property. For example, to access the value of a property with key "k", you would write property("k") or equivalently p("k"). If the property with key "k" is undefined, the property method will return the empty string and not null. This avoids the need to check for null values.

The isDefined() method can be used to check whether a property is defined. For example, to check whether the property "k" is defined you would write isDefined("k") Similarly, if you need to check whether a property is null, the isNull() method is provided. Example: 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>

Conditional processing is supported anywhere within the <configuration> element. Nested if-then-else statements are also supported. However, XML syntax is awfully cumbersome and is ill suited as the foundation of a general purpose programming language. Consequently, too many conditionals will quickly render your configuration files incomprehensible to subsequent readers, including yourself.

Obtaining variables from JNDI

Under certain circumstances, you may want to make use of env-entries stored in JNDI. The <insertFromJNDI> configuration directive extracts an env-entry stored in JNDI and inserts the property in local scope with key specified by the as attribute. As all properties, it is possible to insert the new property into a different scope with the help of the scope attribute.

Example: Insert as properties env-entries obtained via JNDI (logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml)

<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>

In this last example, the "java:comp/env/appName" env-entry is inserted as the appName property. Note that the <contextName> directive sets the context name based on the value of the appName property inserted by the previous <insertFromJNDI> directive.

File inclusion

Joran supports including parts of a configuration file from another file. This is done by declaring a <include> element, as shown below:

Example: File include (logback-examples/src/main/resources/chapters/configuration/containingConfig.xml)

<configuration>
  <include file="src/main/java/chapters/configuration/includedConfig.xml"/>
 
  <root level="DEBUG">
    <appender-ref ref="includedConsole" />
  </root>
 
</configuration>

The target file MUST have its elements nested inside an <included> element. For example, a ConsoleAppender could be declared as:

Example: File include (logback-examples/src/main/resources/chapters/configuration/includedConfig.xml)

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

Again, please note the mandatory <included> element.

The contents to include can be referenced as a file, as a resource, or as a URL.

  • As a file:
    To include a file use the file attribute. You can use relative paths but note that the current directory is defined by the application and is not necessarily related to the path of the configuration file.

  • As a resource:
    To include a resource, i.e a file found on the class path, use the resource attribute.

    <include resource="includedConfig.xml"/>
    
  • As a URL:
    To include the contents of a URL use the url attribute.

    <include url="http://some.host.com/includedConfig.xml"/>
    

If it cannot find the file to be included, logback will complain by printing a status message. In case the included file is optional, you can suppress the warning message by setting optional attribute to true in the <include> element.

<include optional="true" ..../>

Adding a context listener

Instances of the LoggerContextListener interface listen to events pertaining to the lifecycle of a logger context.

JMXConfigurator is one implementation of the LoggerContextListener interface. It is described in a subsequent chapter.

LevelChangePropagator

As of version 0.9.25, logback-classic ships with LevelChangePropagator, an implementation of LoggerContextListener which propagates changes made to the level of any logback-classic logger onto the java.util.logging framework. Such propagation eliminates the performance impact of disabled log statements. Instances of LogRecord will be sent to logback (via SLF4J) only for enabled log statements. This makes it reasonable for real-world applications to use the jul-to-slf4j bridge.

The contextListener element can be used to install LevelChangePropagator as shown next.

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

Setting the resetJUL property of LevelChangePropagator will reset all previous level configurations of all j.u.l. loggers. However, previously installed handlers will be left untouched.

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

推荐阅读更多精彩内容