Log4j Core Plugins

The log4j-tomcat artifact contains three Log4j Core plugins that help to split logs between web applications:

Generic installation

The Log4j Core Plugins can be installed in two different ways:

Global

If you use Log4j Core as Tomcat’s logging system, follow the Tomcat JULI to Log4j API bridge installation instructions and add the log4j-tomcat-3.0.0-beta1.jar artifact to you $CATALINA_BASE/bin/logging folder.

You can download the artifact from this Maven Central location.

Web application

If you want to add the plugins to a single web application, add this dependency to your dependency management system:

  • Maven

  • Gradle

<dependency>
  <groupId>eu.copernik</groupId>
  <artifactId>log4j-tomcat</artifactId>
  <version>3.0.0-beta1</version>
</dependency>
implementation 'eu.copernik:log4j-tomcat:3.0.0-beta1'

Tomcat Context Selectors

The Tomcat Context Selectors are a pair ContextSelector that splits loggers according to the value of the current context classloader. The name of the logger contexts is determined as follows:

  • if the context classloader is the classloader of a web application, the name is of the form /<engine>/<host>/<context>, where:

    <engine>

    The name of the Tomcat Engine container. See Tomcat Engine Container for more details.

    <host>

    The name of the Tomcat Host container. See Tomcat Host Container for more details.

    <context>

    The base file name of the Tomcat Context container. See Tomcat Context Naming for more details.

  • if the context classloader is not the classloader of a web application, the name is -tomcat.

Due to the Log4j Core automatic configuration procedure (see Configuration location) you can:

  • Provide a configuration file for a single web application by creating a file named:

    log4j2/<engine>/<host>/<context>.<extension>

    on the classpath of the application, e.g.

    log4j2/Catalina/localhost/app.xml

  • Provide a configuration file for the global Tomcat logger context by creating a log4j2-tomcat.xml file.

Loggers used by the libraries in Tomcat’s common classloader will be assigned to the web application that was active when the logger was created. The assignment will be appropriate only if the library uses instance field to store application-specific loggers.

Tomcat libraries use instance and static logger fields correctly to differentiate the log statements specific to a web application from the global ones. Most third-party libraries, however, only use static logger fields. The output of those libraries will be assigned to the global logger context or in the worst case scenario to the logger context of the first web application that initialized the library.

Installation

Using Tomcat Context Selectors only makes sense if you installed the Copernik.eu Log4j Core Plugins globally. In this section, we assume you followed the installation instructions above.

To use the Tomcat Context Selector, you need to set the log4j2.contextSelector Log4j configuration property to one of:

eu.copernik.log4j.tomcat.TomcatContextSelector

This selector creates synchronous loggers that append log events to the target resource on the calling thread.

eu.copernik.log4j.tomcat.TomcatAsyncContextSelector

This selector creates asynchronous loggers. See Asynchronous Loggers for more details.

The TomcatAsyncContextSelector required additional dependencies on the classpath.

Add disruptor-4.0.0.jar to the $CATALINA_BASE/bin/logging folder. You can download the artifact from this Maven Central location.

This can be easily achieved, by creating a log4j2.component.properties file in the $CATALINA_BASE/conf/logging folder with the following content:

log4j2.contextSelector = eu.copernik.log4j.tomcat.TomcatContextSelector

or

log4j2.contextSelector = eu.copernik.log4j.tomcat.TomcatAsyncContextSelector

See Log4j Property Sources for alternative ways to set a Log4j configuration property.

Tomcat Lookup

Syntax

${tomcat:<key>}

where <key> is one of the Tomcat Lookup supported keys.

The Tomcat Lookup is a Log4j Core Lookup that can be used in configuration files to retrieve parameters of the web application active on the current thread.

The lookup uses the context classloader, so it can be used eagerly (${tomcat:<key>}) in every value of the configuration file or lazily ($${tomcat:<key>}) in the configuration attributes that accept runtime evaluation. See Runtime Property Substitution for more details.

The keys supported by the lookup are:

Table 1. Tomcat Lookup supported keys
Key Description

context.name

The base name of the current Tomcat Context Container.

See Tomcat Context Naming for more details.

context.logger

Returns the name of the logger that captures all GenericServlet.log() method calls for the current web application, e.g.

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[ROOT]

host.name

The name of the current Tomcat Host Container.

See Tomcat Host Container for more details.

host.logger

Returns the name of the logger that captures all GenericServlet.log() method calls for the current Tomcat Host container, e.g.

org.apache.catalina.core.ContainerBase.[Catalina].[localhost]

engine.name

The name of the current Tomcat Engine Container.

See Tomcat Engine Container for more details.

engine.logger

Returns the name of the logger that captures all GenericServlet.log() method calls for the current Tomcat Engine container, e.g.

org.apache.catalina.core.ContainerBase.[Catalina]

classloader.webappName

An alias for context.name, it returns the same value as ${classloader.webappName} in a Tomcat default logging.properties file.

See Using java.util.logging in Tomcat’s documentation for details.

classloader.hostName

An alias for host.name, it returns the same value as ${classloader.hostName} in a Tomcat default logging.properties file.

See Using java.util.logging in Tomcat’s documentation for details.

classloader.serviceName

An alias for engine.name, it returns the same value as ${classloader.serviceName} in a Tomcat default logging.properties file.

See Using java.util.logging in Tomcat’s documentation for details.

Configuration examples

Since the Tomcat Lookup returns null, whenever we are outside a web application context, it is useful to give a default value to its keys in the Properties section of the configuration file.

For example, we can assign a default value to ${tomcat:context.name} with the snippet below:

  • XML

  • JSON

  • YAML

Snippet from an example log4j2.xml file
<Properties>
  <Property name="context.name" value="catalina"/>
</Properties>
Snippet from an example log4j2.json file
"Properties": {
  "Property": [
    {
      "name": "context.name",
      "value": "catalina"
    }
  ]
},
Snippet from an example log4j2.yaml file
Properties:
  Property:
    - name: "context.name"
      value: "catalina"

The examples below all contain this definition.

Single configuration file for multiple contexts

If we use Tomcat Context Selector to split the loggers into one logger context per-application, we can still use a single configuration file. To provide a different configuration to each context, we can use the Tomcat Lookup.

The snippet below provides a rolling file appender named after the Tomcat Context, as in the standard configuration file:

  • XML

  • JSON

  • YAML

Snippet from an example log4j2.xml file
<RollingFile name="FILE"
             filePattern="${logs.dir}/${tomcat:context.name}.%d{yyyy-MM-dd}.log"> (1)
  <PatternLayout pattern="${one.line.pattern}"/>
  <DirectWriteRolloverStrategy>
    <Delete basePath="${logs.dir}">
      <IfFileName regex="${tomcat:context.name}\.\d{4}-\d{2}-\d{2}\.log"/>
      <IfLastModified age="P90D"/>
    </Delete>
  </DirectWriteRolloverStrategy>
  <TimeBasedTriggeringPolicy/>
</RollingFile>
Snippet from an example log4j2.json file
"RollingFile": {
  "name": "FILE",
  "filePattern": "${logs.dir}/${tomcat:context.name}.%d{yyyy-MM-dd}.log", (1)
  "PatternLayout": {
    "pattern": "${one.line.pattern}"
  },
  "DirectWriteRolloverStrategy": {
    "Delete": {
      "basePath": "${logs.dir}",
      "IfFileName": {
        "regex": "${tomcat:context.name}\\.\\d{4}-\\d{2}-\\d{2}\\.log"
      },
      "IfLastModified": {
        "age": "P90D"
      }
    }
  },
  "TimeBasedTriggeringPolicy": {}
}
Snippet from an example log4j2.yaml file
RollingFile:
  name: "FILE"
  filePattern: "${logs.dir}/${tomcat:context.name}.%d{yyyy-MM-dd}.log" (1)
  PatternLayout:
    pattern: "${one.line.pattern}"
  DirectWriteRolloverStrategy:
    Delete:
      basePath: "${logs.dir}"
      IfFileName:
        regex: "${tomcat:context.name}\.\d{4}-\d{2}-\d{2}\.log"
      IfLastModified:
        age: "P90D"
  TimeBasedTriggeringPolicy: { }
1 We name the log file after the context name. For the global logger context we provide the fallback name catalina.

Single context with dynamic appenders

If we have a single logger context for the entire JVM, we can still split application-specific logs into separate log files. To do that we just need to wrap the example above in a Routing Appender.

  • XML

  • JSON

  • YAML

Snippet from an example log4j2.xml file
<Routing name="ROUTING">
  <Routes pattern="$${tomcat:context.name}">
    <Route>
      <RollingFile name="${tomcat:context.name}"
                   filePattern="${logs.dir}/${tomcat:context.name}.%d{yyyy-MM-dd}.log">
        <PatternLayout pattern="${one.line.pattern}"/>
        <DirectWriteRolloverStrategy>
          <Delete basePath="${logs.dir}">
            <IfFileName regex="${tomcat:context.name}\.\d{4}-\d{2}-\d{2}\.log"/>
            <IfLastModified age="P90D"/>
          </Delete>
        </DirectWriteRolloverStrategy>
        <TimeBasedTriggeringPolicy/>
      </RollingFile>
    </Route>
  </Routes>
</Routing>
Snippet from an example log4j2.json file
"Routing": {
  "name": "ROUTING",
  "Routes": {
    "pattern": "$${tomcat:context.name}",
    "Route": {
      "RollingFile": {
        "name": "${tomcat:context.name}",
        "filePattern": "${logs.dir}/${tomcat:context.name}.%d{yyyy-MM-dd}.log",
        "PatternLayout": {
          "pattern": "${one.line.pattern}"
        },
        "DirectWriteRolloverStrategy": {
          "Delete": {
            "basePath": "${logs.dir}",
            "IfFileName": {
              "regex": "${tomcat:context.name}\\.\\d{4}-\\d{2}-\\d{2}\\.log"
            },
            "IfLastModified": {
              "age": "P90D"
            }
          }
        },
        "TimeBasedTriggeringPolicy": {}
      }
    }
  }
}
Snippet from an example log4j2.yaml file
Routing:
  name: "ROUTING"
  Routes:
    pattern: "$${tomcat:context.name}"
    Route:
      RollingFile:
        name: "${tomcat:context.name}"
        filePattern: "${logs.dir}/${tomcat:context.name}.%d{yyyy-MM-dd}.log" #
        PatternLayout:
          pattern: "${one.line.pattern}"
        DirectWriteRolloverStrategy:
          Delete:
            basePath: "${logs.dir}"
            IfFileName:
              regex: "${tomcat:context.name}\.\d{4}-\d{2}-\d{2}\.log"
            IfLastModified:
              age: "P90D"
        TimeBasedTriggeringPolicy: { }

Tomcat Context Data Provider

If log events from multiple web applications are written to the same target resource, we can tag the events to determine their origin. See Fish tagging for more information.

The Tomcat Context Data Provider uses the Log4j ContextDataProvider extension point to inject the key/value pairs below into the context data map of each log event.

Table 2. Tomcat context data key/value pairs
Key Value

context.name

The base name of the current Tomcat Context Container.

See Tomcat Context Naming for more details.

host.name

The name of the current Tomcat Host Container.

See Tomcat Host Container for more details.

engine.name

The name of the current Tomcat Engine Container.

See Tomcat Engine Container for more details.

The Tomcat Context Data Provider is only useful if you installed the Copernik.eu Log4j Core Plugins using the global installation method.

The provider must be explicitly enabled by setting the log4j2.tomcatContextDataEnabled configuration property to true. This can be done by adding to the $CATALINA_BASE/conf/logging/log4j2.component.properties file the following snippet:

log4j2.tomcatContextDataEnabled = true

Configuration properties

The Copernik.eu Log4j Core Plugins recognize the following Log4j configuration properties:

log4j2.tomcatContextDataEnabled

Env. variable

LOG4J_TOMCAT_CONTEXT_DATA_ENABLED

Type

boolean

Default value

false

If set to true, the context data map of each log event is enriched by Tomcat-specific properties. See Tomcat Context Data Provider for more information.