////
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
    this work for additional information regarding copyright ownership.
    The ASF licenses this file to You under the Apache License, Version 2.0
    (the "License"); you may not use this file except in compliance with
    the License.  You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
////
= Architecture

Log4j Core is the reference implementation of xref:manual/api.adoc[] 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:

[#architecture-diagram]
.An overview of major classes and their relation
[plantuml]
....
@startuml

class LoggerContext {
  Configuration config
  Logger[] loggers
  Logger getLogger(String name)
}

note left of LoggerContext {
  Anchor for the logging system
}

LoggerContext --> "0..*" Logger

package "Configuration" as c {

    class Configuration {
      Appender[] appenders
      Filter filter
      LoggerConfig[] loggerConfigs
      LoggerConfig getLoggerConfig(String name)
      StrSubstitutor substitutor
    }

    note left of Configuration
      Encapsulates components compiled
      from a user-provided configuration
      file (e.g., `log4j2.xml`)
    end note

    Configuration --> Filter

    Configuration --> "0..*" Appender

    Configuration --> "0..*" LoggerConfig

    Configuration --> StrSubstitutor

    class Appender {
      AbstractManager manager
      Layout layout
      Filter filter
      void append(LogEvent)
    }

    Appender --> Layout

    Appender --> Filter

    class Layout {
      byte[] encode(LogEvent)
    }

    class Filter {
      Result filter(LogEvent)
    }

    note right of Filter
      Note that a `Filter` can
      be provided at 4 levels:
      1. `Configuration`
      2. `LoggerConfig`
      3. `AppenderControl`
      4. `Appender`
    end note

    class LoggerConfig {
      AppenderControl[] appenderControls
      Level level
      Filter filter
      void log(LogEvent)
    }

    LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl

    LoggerConfig --> Filter

    class AppenderControl {
      Appender appender
      Filter filter
      void append(LogEvent)
    }

    note right of AppenderControl
      Decorates an `Appender`
      with a `Filter`
    end note

    AppenderControl -[#green,thickness=6]-> Appender

    AppenderControl --> Filter

    class StrSubstitutor {
      Interpolator interpolator
      String replace(String input)
    }

    note right of StrSubstitutor
      Responsible for
      property substitution
      (e.g., `${env:USER}`)
    end note

    StrSubstitutor --> Interpolator

    class Interpolator {
      StrLookup[] lookups
      String lookup(String input)
    }

    Interpolator --> "0..*" StrLookup

    class StrLookup {
      String lookup(String input)
    }
}

LoggerContext --> Configuration

class Logger {
  void log(Level level, Message message)
}

note right of Logger
  The main API entry point
  users interact with
end note

Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()`

class AbstractManager {
}

Appender -[#green,thickness=6]-> AbstractManager

@enduml
....

At a high level,

* A <<LoggerContext>>, the composition anchor, gets created in combination with a <<Configuration>>.
Both can be created directly (i.e., programmatically) or indirectly at first interaction with Log4j.
* `LoggerContext` creates <<Logger>>s that users interact with for logging purposes.
* <<Appender>> delivers a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`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 ``Appender``s.
* <<Configuration>> is equipped with <<StrSubstitutor>> 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 xref:architecture-diagram[xrefstyle=short].

The following sections examine this interplay in detail.

[#LoggerContext]
== `LoggerContext`

The link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContext.html[`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-diagram]
.`LoggerContext` and other directly related classes
[plantuml]
....
@startuml

class LoggerContext #line.bold {
  Configuration config
  Logger[] loggers
  Logger getLogger(String name)
}

LoggerContext --> Configuration

LoggerContext --> "0..*" Logger

class Configuration {
  Appender[] appenders
  Filter filter
  LoggerConfig[] loggerConfigs
  LoggerConfig getLoggerConfig(String name)
  StrSubstitutor substitutor
}

class Logger {
  void log(Level level, Message message)
}

@enduml
....

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 ``LoggerContext``s.
Refer to xref:jakarta.adoc#log-separation[Log Separation] for details.

[#Configuration]
== `Configuration`

Every <<LoggerContext>> is associated with an active link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`].
It models the configuration of all appenders, layouts, filters, loggers, and contains the reference to <<StrSubstitutor>>.

[#Configuration-diagram]
.`Configuration` and other directly related classes
[plantuml]
....
@startuml

class LoggerContext {
  Configuration config
  Logger[] loggers
  Logger getLogger(String name)
}

LoggerContext --> Configuration

class Configuration #line.bold {
  Appender[] appenders
  Filter filter
  LoggerConfig[] loggerConfigs
  LoggerConfig getLoggerConfig(String name)
  StrSubstitutor substitutor
}

Configuration --> "0..*" Filter

Configuration --> "0..*" Appender

Configuration --> "0..*" LoggerConfig

Configuration --> StrSubstitutor

class Appender {
  Layout layout
  void append(LogEvent)
}

class Filter {
  Result filter(LogEvent)
}

class LoggerConfig {
  AppenderRef[] appenderRefs
  AppenderControl[] appenderControls
  Level level
  Filter filter
  void log(LogEvent)
}

class StrSubstitutor {
  Interpolator interpolator
  String replace(String input)
}
@enduml
....

Configuration of Log4j Core is typically done at application initialization.
The preferred way is by reading a xref:manual/configuration.adoc[configuration file], but it can also be done xref:manual/customconfig.adoc[programmatically].
This is further discussed in xref:manual/config-intro.adoc[].

[#reconfiguration]
=== 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 link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ReliabilityStrategy.html[`ReliabilityStrategy`] and it decides:

* when should ``Logger``s switch to the new configuration,
* when should the old configuration be stopped.

.Overview of the reconfiguration process
[plantuml]
....
@startuml
left to right direction

package LoggerContext {
    object Logger

    package "New Configuration" as c2 {
        object "LoggerConfig" as lc2
        object "AppenderControl" as ac2
        object "Appender" as app2
    }

    package "Old Configuration" as c1 {
        object "LoggerConfig" as lc1
        object "AppenderControl" as ac1
        object "Appender" as app1
    }
}

object AbstractManager

Logger ..> lc1
lc1 --> ac1
ac1 --> app1
app1 --> AbstractManager

Logger --> lc2
lc2 --> ac2
ac2 --> app2
app2 --> AbstractManager
@enduml
....

[#Logger]
== `Logger`

link:../javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`]s are the primary user entry point for logging.
They are created by calling one of the `getLogger()` methods of link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] – this is further documented in xref:manual/api.adoc[].
The `Logger` itself performs no direct actions.
It simply has a name and is associated with a <<LoggerConfig>>.

[#Logger-diagram]
.`Logger` and other directly related classes
[plantuml]
....
@startuml

class LoggerContext {
  Configuration config
  Logger[] loggers
  Logger getLogger(String name)
}

LoggerContext --> "0..*" Logger

class LoggerConfig {
  AppenderRef[] appenderRefs
  AppenderControl[] appenderControls
  Level level
  Filter filter
  void log(LogEvent)
}

class Logger #line.bold {
  void log(Level level, Message message)
}

Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()`

@enduml
....

The 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 xref:manual/configuration.adoc#configuring-loggers[configuring ``Logger``s] for further information.

[#LoggerConfig]
== `LoggerConfig`

link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`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 xref:manual/configuration.adoc#configuring-loggers[here].
``Logger``s effectively interact with appenders, filters, etc. through corresponding ``LoggerConfig``s.
A `LoggerConfig` essentially contains

* A reference to its parent (except if it is the root logger)
* A xref:manual/customloglevels.adoc[level] denoting the severity of messages that are accepted (defaults to `ERROR`)
* <<Filter>>s that must allow the `LogEvent` to pass before it will be passed to any <<Appender>>s
* References to <<Appender>>s that should be used to process the event

[#LoggerConfig-diagram]
.`LoggerConfig` and other directly related classes
[plantuml]
....
@startuml

class Configuration {
  Appender[] appenders
  Filter filter
  LoggerConfig[] loggerConfigs
  LoggerConfig getLoggerConfig(String name)
  StrSubstitutor substitutor
}

Configuration --> "0..*" LoggerConfig

class Filter {
  Result filter(LogEvent)
}

class LoggerConfig #line.bold {
  AppenderRef[] appenderRefs
  AppenderControl[] appenderControls
  Level level
  Filter filter
  void log(LogEvent)
}

LoggerConfig --> "0..*" AppenderRef

LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl

LoggerConfig --> Filter

class AppenderRef {
  String appenderName
  Level level
  Filter filter
}

class AppenderControl {
  Appender appender
  Filter filter
  void append(LogEvent)
}

class Logger {
  void log(Level level, Message message)
}

Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()`

@enduml
....

[#logger-hierarchy]
=== Logger 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 xref:#logger-hiearchy-diagram[xrefstyle=short] for an example.

[#logger-hiearchy-diagram]
.Example hierarchy of loggers named `X`, `X.Y`, `X.Y.Z`, and `X.YZ`
[plantuml]
....
@startmindmap
* root
** X
*** X.Y
**** X.Y.Z
*** X.YZ
@endmindmap
....

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
[%collapsible]
====
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.

.Only the root logger is configured with a level, and it is `DEBUG`
[%header,cols="1m,1m,1m,1m"]
|===
|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
|===

.All loggers are configured with a level
[%header,cols="1m,1m,1m,1m"]
|===
|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
|===

.All loggers are configured with a level, except the logger `X.Y`
[%header,cols="1m,1m,1m,1m"]
|===
|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
|===

.All loggers are configured with a level, except loggers `X.Y` and `X.Y.Z`
[%header,cols="1m,1m,1m,1m"]
|===
|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
|===

.All loggers are configured with a level, except the logger `X.YZ`
[%header,cols="1m,1m,1m,1m"]
|===
|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 xref:manual/customloglevels.adoc[].

[#Filter]
== `Filter`

In addition to <<LoggerConfig,the level-based filtering facilitated by `LoggerConfig`>>, Log4j provides link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[`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-diagram]
.`Filter` and other directly related classes
[plantuml]
....
@startuml

class Configuration {
  Appender[] appenders
  Filter filter
  LoggerConfig[] loggerConfigs
  LoggerConfig getLoggerConfig(String name)
  StrSubstitutor substitutor
}

Configuration --> Filter

Configuration --> "0..*" LoggerConfig

class Filter #line.bold {
  Result filter(LogEvent)
}

class LoggerConfig {
  AppenderRef[] appenderRefs
  AppenderControl[] appenderControls
  Level level
  Filter filter
  void log(LogEvent)
}

LoggerConfig --> "0..*" AppenderRef

LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl

LoggerConfig --> Filter

class AppenderRef {
  String appenderName
  Level level
  Filter filter
}

class AppenderControl {
  Filter filter
}

AppenderRef --> Filter

AppenderControl --> Filter

@enduml
....

Refer to xref:manual/filters.adoc[] for further information.

[#Appender]
== `Appender`

link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[`Appender`]s are responsible for delivering a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a certain target; console, file, database, etc.
While doing so, they typically use <<Layout>>s to encode the log event.
See xref:manual/appenders.adoc[] for the complete guide.

[#Appender-diagram]
.`Appender` and other directly related classes
[plantuml]
....
@startuml

class Configuration {
  Appender[] appenders
  Filter filter
  LoggerConfig[] loggerConfigs
  LoggerConfig getLoggerConfig(String name)
  StrSubstitutor substitutor
}

Configuration --> "0..*" Filter

Configuration --> "0..*" Appender

Configuration --> "0..*" LoggerConfig

class Appender #line.bold {
  Layout layout
  void append(LogEvent)
}

Appender -[#green,thickness=6]-> Layout

class Layout {
  byte[] encode(LogEvent)
}

class Filter {
  Result filter(LogEvent)
}

class LoggerConfig {
  AppenderRef[] appenderRefs
  AppenderControl[] appenderControls
  Level level
  Filter filter
  void log(LogEvent)
}

LoggerConfig --> "0..*" AppenderRef

LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl

LoggerConfig --> Filter

class AppenderRef {
  String appenderName
  Level level
  Filter filter
}

AppenderRef --> Filter

class AppenderControl {
  Appender appender
  Filter filter
  void append(LogEvent)
}

AppenderControl -[#green,thickness=6]-> Appender

AppenderControl --> Filter

@enduml
....

An `Appender` can be added to a <<Logger>> by calling the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html#addLoggerAppender(org.apache.logging.log4j.core.Logger,%20org.apache.logging.log4j.core.Appender)[`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]
=== 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 xref:manual/configuration.adoc#configuring-loggers[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
[%collapsible]
====
[#appender-additivity-diagram]
.Example hierarchy of logger configurations to demonstrate appender additivity
[plantuml]
....
@startmindmap
* root
** A
*** A.B1 (additivity=false)
**** A.B1.C
***** A.B1.C.D
*** A.B2.C
**** A.B2.C.D (additivity=false)
@endmindmap
....

In xref:#appender-additivity-diagram[xrefstyle=short], the effective appenders for each logger configuration are as follows:

.Effective appenders of logger configurations in xref:#appender-additivity-diagram[xrefstyle=short]
[cols="1c,1c,1c,1c,1c,1c,1c"]
|===
.2+^.^h| Appender
6+^.h|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]
=== `AbstractManager`

To multiplex the access to external resources (files, network connections, etc.), most appenders are split into an
link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/AbstractManager.html[`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
xref:manual/appenders/file.adoc[file appenders]s
with the same `fileName` attribute all share the same
link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/FileManager.html[`FileManager`].

[IMPORTANT]
====
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,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]
== `Layout`

An <<Appender>> uses a *layout* to encode a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] into a form that meets the needs of whatever will be consuming the log event.

[#Layout-diagram]
.`Layout` and other directly related classes
[plantuml]
....
@startuml

class Appender {
  Layout layout
  void append(LogEvent)
}

Appender -[#green,thickness=6]-> Layout

class Layout #line.bold {
  byte[] encode(LogEvent)
}

@enduml
....

Refer to xref:manual/layouts.adoc[] for details.

[#StrSubstitutor]
== `StrSubstitutor` et al.

link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrSubstitutor.html[`StrSubstitutor`] is a `String` interpolation tool that can be used in both configurations and components (e.g., appenders, layouts).
It accepts an link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/Interpolator.html[`Interpolator`] to determine if a key maps to a certain value.
`Interpolator` is essentially a facade delegating to multiple link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] (aka. _lookup_) implementations.

[#StrSubstitutor-diagram]
.`StrSubstitutor` et al. and other directly related classes
[plantuml]
....
@startuml

class Configuration {
  Appender[] appenders
  Filter[] filters
  LoggerConfig[] loggerConfigs
  LoggerConfig getLoggerConfig(String name)
  StrSubstitutor substitutor
}

Configuration --> StrSubstitutor

class StrSubstitutor #line.bold {
  Interpolator interpolator
  String replace(String input)
}

StrSubstitutor --> Interpolator

class Interpolator {
  StrLookup[] lookups
  String lookup(String input)
}

Interpolator --> "0..*" StrLookup

class StrLookup {
  String lookup(String input)
}

@enduml
....

See xref:manual/configuration.adoc#property-substitution[how property substitution works] and xref:manual/lookups.adoc[the predefined lookups] for further information.
