In symbols one observes an advantage in discovery which is greatest when they express the exact nature of a thing briefly and, as it were, picture it; then indeed the labor of thought is wonderfully diminished.

—GOTTFRIED WILHELM LEIBNIZ

Manual · Configuration · Configuration: Conditionals and advanced

Configuration: Conditionals and advanced

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 <condition>, <if>, <then> and <else> elements so that a single configuration file can adequately target several environments.

The general format for conditional statements is shown below.

   <!-- condition-if-then form -->
   <condition class="a.class.implementing.PropertyCondition">
      <!-- additional parameters if needed -->
   </condition> 
   <!-- if-then form -->
   <if>
    <then>
      ...
    </then>
  </if>

  <!-- condition-if-then-else form -->
  <condition class="a.class.implementing.ProppertyCondition">
      <!-- additional parameters if needed -->
  </condition> 
  <if>
    <then>
      ...
    </then>
    <else>
      ...
    </else>
  </if>

Since 1.5.20 Note that the <condition> element precedes the <if>/<then>/<else> elements. This syntax is similar to conditional expressions in Smalltalk. Conditional processing using the <condition> element was introduced in Logback version 1.5.20.

If the <condition> evaluates to true, the <then> part is activated and if false, the <else> part is activated, if present.

Conditional processing is supported anywhere within the <configuration> element. Nested condition-if-then-else statements are also supported. However, XML syntax is cumbersome and is ill-suited as the foundation of a general purpose programming language. Thus, conditionals should be used sparingly.

The class passed to the <condition> element must implement the PropertyCondition interface.

Logback-core ships with basic condition implementations derived from PropertyConditionBase in the ch.qos.logback.core.boolex package, namely PropertyEqualityCondition, IsPropertyDefinedCondition, IsPropertyNullCondition and last but not least ExpressionPropertyCondition.

Here is an example which checks if the HOSTNAME property equals "torino".

<configuration debug="true">

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

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

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

Requires a server call.

Requires a server call.

ProppertyConditionBase class defines the following helper methods: property(String key), p(String key), isDefined(String key), and isNull(String key).

For a key passed as argument, the property(String key) or its shorter equivalent p(String key) methods return the value of the property corresponding to key. 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.

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. See scopes and lookup order sections above for more details.

The isDefined(String key) 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").

ExpressionPropertyCondition evaluates boolean expressions

Since 1.5.24 ExpressionPropertyCondition can evaluate Java-like boolean expressions using the boolean operators !, &&, ||, as well as several pre-defined functions (see below). Parentheses are also supported. However, numerical literals are not supported. Thus, the expression "1 != 2" will result in an error.

The predefined functions are:

As mentioned earlier, 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. See scopes and lookup order sections above for more details.

Sub-classes of ExpressionPropertyCondition can easily define new functions.

Here is a boolean expression that will return true if the "environment" property is defined or if "HOSTNAME" property does not equal "192.168.1.2".

isDefined("environment") || !propertyEquals("HOSTNAME", "192.168.1.2")

Here is another boolean expression that will evaluate to true if the "environment" property is null and the "HOSTNAME" property contains "192.168".

isNull("environment") && propertyContains("HOSTNAME", "192.168")

XML quirk Given that the & character is special in XML, it needs to be written as &amp; in XML files.

Here is an example which enables console appender if the "environment" property is null and the HOSTNAME property equals "torino".

<configuration debug="true">

    <condition class="ch.qos.logback.core.boolex.ExpressionPropertyCondition">
    <!-- Note that the & character needs to be written as &amp; in XML files -->  
    <expression>isNull("environment") &amp;&amp; propertyEquals("HOSTNAME", "torino")</expression>
  </condition>
  <if>
    <then>
      <appender name="CON" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
          <pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
        </encoder>
      </appender>
      <root>
        <appender-ref ref="CON" />
      </root>
    </then>
    <else>
        <appender name="FILE" class="ch.qos.logback.core.FileAppender">
          <file>${randomOutputDir}/conditional.log</file>
          <encoder>
            <pattern>%d %-5level %logger{35} -%kvp- %msg %n</pattern>
          </encoder>
        </appender>
        
        <root level="ERROR">
          <appender-ref ref="FILE" />
        </root>
    </else>
  </if>
   

</configuration>

Requires a server call.

Requires a server call.

Conditional configuration using Janino has been deprecated

Note Due to potential vulnerabilities associated with dynamic, i.e. runtime, java code compilation and execution (using Janino), the condition attribute within the <if> element has been deprecated and was removed in version 1.5.37. An online migration service is provided to help with the transition.

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

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

Requires a server call. Please wait a few seconds.

Requires a server call.

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.

Serialize model

Since 1.3.9/1.4.9 As for version 1.3.9/1.4.9, logback-classic can create a serialized version of configuration model matching the XML configuration file.

This is accompished by adding a <serializeModel> element and specifying the path to the serialized model file.

<configuration debug="false">

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

Note that an instance of SerializedModelConfigurator is created and invoked during logback-classic initialization. SerializedModelConfigurator instances can read and configure logback-classic from serialized configuration model (.scmo) files. See the earlier section on configuration at initialization.

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>

Requires a server call.

Requires a server call.

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 -%kvp- %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.

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" ..../>

PropertiesConfigurator

Since 1.5.8 The PropertiesConfigurator offers useful albeit limited functionality: it can read .properties files to set logger levels. Variable definition as well as variable substitution are also supported.

As the scope of PropertiesConfigurator is limited to setting levels of loggers, the syntax of the properties file is simple. Here is a short synopsis of the supported format:

    # defines a variable named "aKey" set to "aValue"
    aKey=aValue
    # sets the level of the root logger to 'LEVEL'
    logback.root=LEVEL
    # sets the logger named 'LOGGER.NAME' to 'LEVEL'
    logback.logger.LOGGER.NAME=LEVEL

where LOGGER.NAME is a logger name and LEVEL is one of the level values for configuring loggers.

Note that variable substitution is allowed.

Here is a sample .properties file:

    # set the root logger to level INFO
    logback.root = INFO

    # define variable 
    com_foo_level = DEBUG    

    # set levels for loggers
    logback.logger.org.xyz.http = TRACE
    logback.logger.org.xyz.database = ERROR
    logback.logger.com.foo = ${com_foo_level}

The location of .properties file is declared via the <propertiesConfiguration> element. The .properties file can be specified as a file, a resource or a URL (see file inclusion above).

<configuration>
    <!-- file, resource, or URL. See file inclusion above .... -->
    <propertiesConfigurator file="path/To/Properties/File.properties"/>

     <!-- rest of the configuration file .... -->
</configuration>
    

Requires a server call.

Requires a server call.

Note that configuration files in properties format are special in the sense that they can be watched for changes on web-servers using HTTP or HTTPS protocols whereas, security reasons, XML configuration files cannot be loaded via HTTP or HTTPS.

Here is a sample configuration file including a remote properties file. The file will be checked for changes every 10 minutes and reloaded upon modification.

<configuration scan="true" scanPeriod="10 minutes">
    <!-- the url must end with .properties .... -->
    <propertiesConfigurator url="http://some.server/foo.properties"/>

     <!-- rest of the configuration file .... -->
</configuration> 
    

Requires a server call.

Requires a server call.

.properties suffix The url must end with the string ".properties" to be watched for changes.

As of version 1.5.28, the <propertiesConfigurator> element admits the scan attribute in case the scan attribute of the <configuration> element needs to be overridden

Adding a context listener

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

Note The ch.qos.logback.classic.jmx package, which previously provided JMXConfigurator (a LoggerContextListener exposing logback over JMX) and the <jmxConfigurator> configuration element, was removed in logback 1.3.0 for security reasons and for lack of use.

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"/>
   <!-- rest of the configuration file .... -->
</configuration>

Requires a server call.

Requires a server call.

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>
   <!-- rest of the configuration file .... -->
</configuration>

Requires a server call.

Requires a server call.

Stating the obvious, if your application is modular and presumably also uses java.util.logging, then your application's module-info.java file will need to declare 'requires java.util'.

SequenceNumberGenerator

Since 1.3.0Logback supports a sequence number field which is automatically populated at event creation. This field is fed by a sequence number generator attached to the logging context.

A sequence number generator is set as follows:

<configuration>
    <sequenceNumberGenerator class="ch.qos.logback.core.spi.BasicSequenceNumberGenerator"/>

     <!-- rest of the configuration file .... -->
</configuration>
    

Requires a server call.

Requires a server call.