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:
isDefined(String key)isNull(String key)propertyEquals(String key, String value)propertyContains(String key, String value)
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 & 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 & in XML files -->
<expression>isNull("environment") && 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.
- 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" ..../>
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.