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:
At a really high level,
-
A
LoggerContext
, the composition anchor, gets created in combination with aConfiguration
. Both can be created either directly (i.e., programmatically) or indirectly at first interaction with Log4j. -
LoggerContext
createsLogger
s that users interact with for logging purposes. -
Appender
delivers aLogEvent
to a target (file, socket, database, etc.) and typically uses aLayout
to encode log events and anAbstractManager
to handle the lifecycle of the target resource. -
LoggerConfig
encapsulates configuration for aLogger
, asAppenderControl
andAppenderRef
forAppender
s. -
Configuration
is equipped withStrSubstitutor
et al. to allow property substitution inString
-typed values. -
A typical
log()
call triggers a chain of invocations through classesLogger
,LoggerConfig
,AppenderControl
,Appender
, andAbstractManager
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 Logger
s.
LoggerContext
and other directly related classesIn most cases, applications have a single global LoggerContext
.
Though in certain cases (e.g., Java EE applications), Log4j can be configured to accommodate multiple LoggerContext
s.
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 classesConfiguration 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
Logger
s switch to the new configuration, -
when should the old configuration be stopped.
Logger
Logger
s 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 classesThe hierarchy between LoggerConfig
s, implies the very same hierarchy between Logger
s 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, Logger
s may become associated with a different LoggerConfig
, thus causing their behavior to be modified.
Refer to configuring Logger
s 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 LoggerConfig
s is explained here.
Logger
s effectively interact with appenders, filters, etc. through corresponding LoggerConfig
s.
A LoggerConfig
essentially contains
LoggerConfig
and other directly related classesLogger hierarchy
Log4j Core has a hierarchical model of LoggerConfig
s, and hence Logger
s.
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.
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.
Logger name | Assigned LoggerConfig name |
Configured level | Effective level |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Logger name | Assigned LoggerConfig |
Configured level | Effective level |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Logger name | Assigned LoggerConfig |
Configured level | Effective level |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Logger name | Assigned LoggerConfig |
Configured level | Effective level |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Logger name | Assigned LoggerConfig |
Configured level | Effective level |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 Filter
s 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 classesRefer to Filters for further information.
Appender
Appender
s are responsible for delivering a LogEvent
to a certain target; console, file, database, etc.
While doing so, they typically use Layout
s to encode the log event.
See Appenders for the complete guide.
Appender
and other directly related classesAn 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 Logger
s 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 Appender
s of the LoggerConfig
's parents.
In other words, Appender
s 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
In Figure 10, the effective appenders for each logger configuration are as follows:
Appender |
Logger configuration |
|||||
---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
✅ |
❌ |
❌ |
❌ |
✅ |
❌ |
|
✅ |
❌ |
❌ |
❌ |
✅ |
❌ |
|
- |
✅ |
✅ |
✅ |
- |
- |
|
- |
- |
✅ |
✅ |
- |
- |
|
- |
- |
- |
✅ |
- |
- |
|
- |
- |
- |
- |
✅ |
❌ |
|
- |
- |
- |
- |
- |
✅ |
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 |
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 classesSee how property substitution works and the predefined lookups for further information.