Architecture

Log4j Core is the reference implementation of Log4j API and composed of several components. In this section we will try to explain major pillars its architecture stands on. An overview these major classes can be depicted as follows:

An overview of major classes and their relation
Figure 1. An overview of major classes and their relation

At a really high level,

  • A LoggerContext, the composition anchor, gets created in combination with a Configuration. Both can be created either directly (i.e., programmatically) or indirectly at first interaction with Log4j.

  • LoggerContext creates Loggers that users interact with for logging purposes.

  • Appender delivers a LogEvent to a target (file, socket, database, etc.) and typically uses a Layout to encode log events and an AbstractManager to handle the lifecycle of the target resource.

  • LoggerConfig encapsulates configuration for a Logger, as AppenderControl and AppenderRef for Appenders.

  • Configuration is equipped with StrSubstitutor et al. to allow property substitution in String-typed values.

  • A typical log() call triggers a chain of invocations through classes Logger, LoggerConfig, AppenderControl, Appender, and AbstractManager in order – this is depicted using green arrows in Figure 1.

Following sections examine this interplay in detail.

LoggerContext

The LoggerContext acts as the anchor point for the logging system. It is associated with an active Configuration and is primarily responsible for instantiating Loggers.

`LoggerContext` and other directly related classes
Figure 2. LoggerContext and other directly related classes

In most cases, applications have a single global LoggerContext. Though in certain cases (e.g., Java EE applications), Log4j can be configured to accommodate multiple LoggerContexts. Refer to Log Separation for details.

Configuration

Every LoggerContext is associated with an active Configuration. It models the configuration of all appenders, layouts, filters, loggers, and contains the reference to StrSubstitutor et al..

`Configuration` and other directly related classes
Figure 3. Configuration and other directly related classes

Configuration of Log4j Core is typically done at application initialization. The preferred way is by reading a configuration file, but it can also be done programmatically. This is further discussed in Configuration.

Reconfiguration reliability

The main motivation for the existing architecture is the reliability to configuration changes. When a reconfiguration event occurs, two Configuration instances are active at the same time. Threads that already started processing a log event will either:

  • continue logging to the old configuration, if execution already reached the LoggerConfig class,

  • or switch to the new configuration.

The service that manages the reconfiguration process is called ReliabilityStrategy and it decides:

  • when should Loggers switch to the new configuration,

  • when should the old configuration be stopped.

Overview of the reconfiguration process
Figure 4. Overview of the reconfiguration process

Logger

Loggers are the primary user entry point for logging. They are created by calling one of the getLogger() methods of LogManager – this is further documented in Log4j API. The Logger itself performs no direct actions. It simply has a name and is associated with a LoggerConfig.

`Logger` and other directly related classes
Figure 5. Logger and other directly related classes

The hierarchy between LoggerConfigs, implies the very same hierarchy between Loggers too. You can use LogManager.getRootLogger() to get the root logger. Note that Log4j API has no assumptions on a Logger hierarchy – this is a feature implemented by Log4j Core.

When the Configuration is modified, Loggers may become associated with a different LoggerConfig, thus causing their behavior to be modified. Refer to configuring Loggers for further information.

LoggerConfig

LoggerConfig binds Logger definitions to their associated components (appenders, filters, etc.) as declared in the active Configuration. The details of mapping a Configuration to LoggerConfigs is explained here. Loggers effectively interact with appenders, filters, etc. through corresponding LoggerConfigs. A LoggerConfig essentially contains

  • A reference to its parent (except if it is the root logger)

  • A level denoting the severity of messages that are accepted (defaults to ERROR)

  • Filters that must allow the LogEvent to pass before it will be passed to any Appenders

  • References to Appenders that should be used to process the event

`LoggerConfig` and other directly related classes
Figure 6. LoggerConfig and other directly related classes

Logger hierarchy

Log4j Core has a hierarchical model of LoggerConfigs, and hence Loggers. A LoggerConfig called child is said to be parented by parent, if parent has the longest prefix match on name. This match is case-sensitive and performed after tokenizing the name by splitting it from . (dot) characters. For a positive name match, tokens must match exhaustively. See Figure 7 for an example.

Example hierarchy of loggers named `X`, `X.Y`, `X.Y.Z`, and `X.YZ`
Figure 7. Example hierarchy of loggers named X, X.Y, X.Y.Z, and X.YZ

If a LoggerConfig is not provided an explicit level, it will be inherited from its parent. Similarly, if a user programmatically requests a Logger with a name that doesn’t have a directly corresponding LoggerConfig configuration entry with its name, the LoggerConfig of the parent will be used.

Click for examples on LoggerConfig hierarchy

Below we demonstrate the LoggerConfig hierarchy by means of level inheritance. That is, we will examine the effective level of a Logger in various LoggerConfig settings.

Table 1. Only the root logger is configured with a level, and it is DEBUG
Logger name Assigned LoggerConfig name Configured level Effective level

root

root

DEBUG

DEBUG

X

root

DEBUG

X.Y

root

DEBUG

X.Y.Z

root

DEBUG

Table 2. All loggers are configured with a level
Logger name Assigned LoggerConfig Configured level Effective level

root

root

DEBUG

DEBUG

X

X

ERROR

ERROR

X.Y

X.Y

INFO

INFO

X.Y.Z

X.Y.Z

WARN

WARN

Table 3. All loggers are configured with a level, except the logger X.Y
Logger name Assigned LoggerConfig Configured level Effective level

root

root

DEBUG

DEBUG

X

X

ERROR

ERROR

X.Y

X

ERROR

X.Y.Z

X.Y.Z

WARN

WARN

Table 4. All loggers are configured with a level, except loggers X.Y and X.Y.Z
Logger name Assigned LoggerConfig Configured level Effective level

root

root

DEBUG

DEBUG

X

X

ERROR

ERROR

X.Y

X

ERROR

X.Y.Z

X

ERROR

Table 5. All loggers are configured with a level, except the logger X.YZ
Logger name Assigned LoggerConfig Configured level Effective level

root

root

DEBUG

DEBUG

X

X

ERROR

ERROR

X.Y

X.Y

INFO

INFO

X.YZ

X

ERROR

For further information on log levels and using them for filtering purposes in a configuration, see Levels.

Filter

In addition to the level-based filtering facilitated by LoggerConfig, Log4j provides Filters to evaluate the parameters of a logging call (i.e., context-wide filter) or a log event, and decide if it should be processed further in the pipeline.

`Filter` and other directly related classes
Figure 8. Filter and other directly related classes

Refer to Filters for further information.

Appender

Appenders are responsible for delivering a LogEvent to a certain target; console, file, database, etc. While doing so, they typically use Layouts to encode the log event. See Appenders for the complete guide.

`Appender` and other directly related classes
Figure 9. Appender and other directly related classes

An Appender can be added to a Logger by calling the addLoggerAppender() method of the current Configuration. If a LoggerConfig matching the name of the Logger does not exist, one will be created, and the Appender will be attached to it, and then all Loggers will be notified to update their LoggerConfig references.

Appender additivity

Each enabled logging request for a given logger will be forwarded to all the appenders in the corresponding Logger's LoggerConfig, as well as to the Appenders of the LoggerConfig's parents. In other words, Appenders are inherited additively from the LoggerConfig hierarchy. For example, if a console appender is added to the root logger, then all enabled logging requests will at least print on the console. If in addition a file appender is added to a LoggerConfig, say LC, then enabled logging requests for LC and LC's children will print in a file and on the console. It is possible to override this default behavior so that appender accumulation is no longer additive by setting additivity attribute to false on the Logger declaration in the configuration file.

The output of a log statement of Logger L will go to all the appenders in the LoggerConfig associated with L and the ancestors of that LoggerConfig. However, if an ancestor of the LoggerConfig associated with Logger L, say P, has the additivity flag set to false, then L's output will be directed to all the appenders in L's LoggerConfig and it’s ancestors up to and including P but not the appenders in any of the ancestors of P.

Click for an example on appender additivity
Example hierarchy of logger configurations to demonstrate appender additivity
Figure 10. Example hierarchy of logger configurations to demonstrate appender additivity

In Figure 10, the effective appenders for each logger configuration are as follows:

Table 6. Effective appenders of logger configurations in Figure 10

Appender

Logger configuration

A

A.B1

A.B1.C

A.B1.C.D

A.B2.C

A.B2.C.D

root

A

A.B1

-

-

-

A.B1.C

-

-

-

-

A.B1.C.D

-

-

-

-

-

A.B2.C

-

-

-

-

A.B2.C.D

-

-

-

-

-

AbstractManager

To multiplex the access to external resources (files, network connections, etc.), most appenders are split into an AbstractManager that handles the low-level access to the external resource and an Appender that transforms log events into a format that the manager can handle.

Managers that share the same resource are shared between appenders regardless of the Configuration or LoggerContext of the appenders. For example file appenderss with the same fileName attribute all share the same FileManager.

Due to the manager-sharing feature of many Log4j appenders, it is not possible to configure multiple appenders for the same resource that only differ in the way the underlying resource is configured.

For example, it is not possible to have two file appenders (even in different logger contexts) that use the same file, but a different value of the append option. Since during a reconfiguration event multiple instances of the same appender exists, it is also not possible to toggle the value of the append option through reconfiguration.

Layout

An Appender uses a layout to encode a LogEvent into a form that meets the needs of whatever will be consuming the log event.

`Layout` and other directly related classes
Figure 11. Layout and other directly related classes

Refer to Layouts for details.

StrSubstitutor et al.

StrSubstitutor is a String interpolation tool that can be used in both configurations and components (e.g., appenders, layouts). It accepts an Interpolator to determine if a key maps to a certain value. Interpolator is essentially a facade delegating to multiple StrLookup (aka. lookup) implementations.

`StrSubstitutor` et al. and other directly related classes
Figure 12. StrSubstitutor et al. and other directly related classes

See how property substitution works and the predefined lookups for further information.