Configuring Log4J2

Configuring Log4J2

This page is dedicated to pax-logging-log4j2 backend which provides support for Log4J2 library.

Log4J2 may be configured either using properties or XML file. In theory, JSON and YAML formats are supported, but it’s not well tested.

properties file syntax to configure Log4J2 is the weirdest one in entire world.

As with Log4j1 we can configure org.ops4j.pax.logging PID using properties syntax or as with Logback we can configure it using XML syntax.

Configuration using XML

org.ops4j.pax.logging PID just needs single property:

org.ops4j.pax.logging.log4j2.config.file = /path/to/log4j2.xml

And then /path/to/log4j2.xml can use normal Log4J2 syntax, like this:

<Configuration id="builtin.rolling"> <Appenders> <Console name="console"> <PatternLayout pattern="%logger/%class [%level] %message%n" /> </Console> <RollingFile name="file" append="true"> <fileName>logs/service.log</fileName> <filePattern>logs/service.log.%d{yyyy-MM}</filePattern> <PatternLayout pattern="%logger/%class [%level] %mdc %message%n" /> <Policies> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> </Appenders> <Loggers> <Logger name="my.logger" level="info" additivity="false"> <AppenderRef ref="file" /> </Logger> <Root level="debug"> <AppenderRef ref="console" /> </Root> </Loggers> </Configuration>

Configuration using properties file

Source: http://logging.apache.org/log4j/2.x/manual/configuration.html#Properties

This syntax is the source of big confusion - especially if someone tries to migrate properties file from Log4J1 configuration. Apache Karaf provides template configuration file, but here’s another example. All properties must be prefixed with log4j2.

log4j2.appender.console.type = Console log4j2.appender.console.name = console log4j2.appender.console.layout.type = PatternLayout log4j2.appender.console.layout.pattern = %logger/%class [%level] %message%n log4j2.appender.file.type = File log4j2.appender.file.name = file log4j2.appender.file.fileName = logs/service.log log4j2.appender.file.layout.type = PatternLayout log4j2.appender.file.layout.pattern = %C | %M | %F | %L : [%p] %m%n log4j2.rootLogger.level = info log4j2.rootLogger.appenderRef.console.ref = console log4j2.logger.my.name = my.logger log4j2.logger.my.level = info log4j2.logger.my.appenderRef.file.ref = file log4j2.logger.my.additivity = false

SIFT logging

Sifting (segregating) logger can split incoming logging events and pass them to different or parameterized appenders. Segregation is done using configured key. This key is then looked up in MDC which always contains the following entries (provided by Pax Logging itself):

Other libraries may use/add other keys. Apache camel adds these, when Camel context is MDC-aware:

  • camel.exchangeId

  • camel.messageId

  • camel.contextId

  • camel.correlationId

  • camel.breadcrumbId

Here’s sample SIFT Logging Appender configuration in properties syntax (with explanation):

log4j2.appender.sift.name = sift # org.apache.logging.log4j.core.appender.routing.RoutingAppender log4j2.appender.sift.type = Routing # org.apache.logging.log4j.core.appender.routing.Routes log4j2.appender.sift.routes.type = Routes log4j2.appender.sift.routes.pattern = ${ctx:my.key} # "sift" is one of the Route elelemnts inside Routes. There's no key, thus the route is default log4j2.appender.sift.routes.sift.type = Route # dynamic declaration of the appender - to resolve file name using the map log4j2.appender.sift.routes.sift.appender.type = File log4j2.appender.sift.routes.sift.appender.name = dynamic-file log4j2.appender.sift.routes.sift.appender.fileName = target/logs-log4j2/${ctx:my.key}-file-appender.log log4j2.appender.sift.routes.sift.appender.layout.type = PatternLayout log4j2.appender.sift.routes.sift.appender.layout.pattern = %c/%C [%p] %m%n # another Route in Routes - with key "${ctx:my.key}" (thus non default from the point of view of RoutingAppender # but actually a default from user point of view - used when there's no my.key in MDC). It's a kind of trick when # "${ctx:my.key}" resolves back to "${ctx:my.key}" (when there's no key in MDC). log4j2.appender.sift.routes.default.type = Route log4j2.appender.sift.routes.default.key = ${ctx:my.key} # static reference to appender log4j2.appender.sift.routes.default.ref = file # normal appender that may be referenced from logger or from the route above log4j2.appender.file.type = File log4j2.appender.file.name = file log4j2.appender.file.fileName = target/logs-log4j2/default-file-appender.log log4j2.appender.file.layout.type = PatternLayout log4j2.appender.file.layout.pattern = %c/%C [%p] %m%n log4j2.rootLogger.level = info log4j2.rootLogger.appenderRef.sift.ref = sift

And here’s XML version - this time with standard bundle.name key:

<Configuration> <Appenders> <Routing name="sift"> <!-- "ctx" is one of the prefixes supported by org.apache.logging.log4j.core.lookup.StrSubstitutor.variableResolver which is org.apache.logging.log4j.core.lookup.Interpolator which maps: strLookupMap = {java.util.HashMap@5274} size = 13 "date" -> {org.apache.logging.log4j.core.lookup.DateLookup@5319} "ctx" -> {org.apache.logging.log4j.core.lookup.ContextMapLookup@5321} "main" -> {org.apache.logging.log4j.core.lookup.MainMapLookup@5323} "env" -> {org.apache.logging.log4j.core.lookup.EnvironmentLookup@5325} "sys" -> {org.apache.logging.log4j.core.lookup.SystemPropertiesLookup@5327} "sd" -> {org.apache.logging.log4j.core.lookup.StructuredDataLookup@5329} "java" -> {org.apache.logging.log4j.core.lookup.JavaLookup@5331} "marker" -> {org.apache.logging.log4j.core.lookup.MarkerLookup@5333} "jndi" -> {org.apache.logging.log4j.core.lookup.JndiLookup@5335} "jvmrunargs" -> {org.apache.logging.log4j.core.lookup.JmxRuntimeInputArgumentsLookup@5337} "map" -> {org.apache.logging.log4j.core.lookup.MapLookup@5339} "bundle" -> {org.apache.logging.log4j.core.lookup.ResourceBundleLookup@5341} "log4j" -> {org.apache.logging.log4j.core.lookup.Log4jLookup@5343} --> <Routes pattern="${ctx:bundle.name}"> <Route> <File name="file-${ctx:bundle.name}"> <fileName>logs/${ctx:bundle.name}-service.log</fileName> <append>true</append> <PatternLayout> <pattern>%logger/%class [%level] %mdc %message%n</pattern> </PatternLayout> </File> </Route> </Routes> </Routing> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="sift" /> </Root> </Loggers> </Configuration>

Customizing Log4J2

Users are allowed to extend the functionality of Logback through mechanisms provided by Pax Logging.

Using whiteboard pattern, users may register services implementing interfaces from org.ops4j.pax.logging.spi package. PaxOsgi element is a Pax Logging special plugin for Log4J2 that maybe used wherever an appender is expected in Log4j2 configuration. Here’s sample XML configuration with comments.

<Configuration id="custom"> <Appenders> <Console name="console"> <PatternLayout pattern="%logger/%class [%level] %message%n" /> </Console> <!-- Will translate to "(&(objectClass=org.ops4j.pax.logging.spi.PaxAppender)(org.ops4j.pax.logging.appender.name=custom))" --> <PaxOsgi name="custom" filter="custom" /> </Appenders> <Loggers> <Logger name="my.logger" level="info" additivity="false"> <AppenderRef ref="custom" /> </Logger> <Root level="debug"> <AppenderRef ref="console" /> </Root> </Loggers> </Configuration>

As referred to from Configuring Log4J1 and Configuring Logback, users can also extend Log4J2 using fragment bundles. Why is it needed? When Log4j2 library itself parses the configuration it has to instantiate classes for appenders, layouts, filters, … pax-logging-log4j2 itself doesn’t provide enough elements on its Import-Package manifest header, so it won’t be able to instantiate everything. That’s where fragments come into play.

https://github.com/ops4j/org.ops4j.pax.logging/tree/paxlogging-1.11.x/pax-logging-samples/fragment-log4j2is a sample fragment bundle which shows how to prepare a fragment that can:

  • attach to pax-logging-log4j2 bundle

  • provide custom filter, appender and trigger

  • declare them as Log4J2 plugins

In order to inform Log4J2 about new plugins, we need this POM fragment:

<plugin> <artifactId>maven-compiler-plugin</artifactId> <executions> <execution> <id>log4j2-plugin-processor</id> <goals> <goal>compile</goal> </goals> <phase>process-classes</phase> <configuration> <proc>only</proc> <annotationProcessors> <annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</annotationProcessor> </annotationProcessors> </configuration> </execution> </executions> </plugin>

which will generated correct /META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat descriptor with cached plugin information. For example, this above fragment contains these plugins (by name):

  • Always - implementing org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy

  • PaxDebug - extending org.apache.logging.log4j.core.filter.AbstractFilter

  • List - extending org.apache.logging.log4j.core.appender.AbstractAppender

These plugins may then be used in Log4J2 configuration, simply:

log4j2.appender.l.type = List log4j2.appender.l.name = custom-appender log4j2.logger.my.name = my.logger log4j2.logger.my.level = info log4j2.logger.my.additivity = false log4j2.logger.my.appenderRef.l.ref = custom-appender