Plugins
Log4j plugin system is the de facto extension mechanism embraced by various Log4j Core components. Plugins make it possible for extensible components to receive feature implementations without any explicit links in between. It is analogous to a dependency injection framework, but curated for Log4j-specific needs.
| Log4j plugin system is implemented by Log4j Core, the logging implementation. It is deliberately not a part of the Log4j API to keep the logging API footprint small. | 
| Did you know about Plugin reference, the documentation extracted from the source code of all predefined Log4j plugins? Like Javadoc, but specialized for plugins! | 
In this section we will give an overview of the Log4j plugin system by answering certain questions:
Declaring plugins
A class can be declared as a plugin by adding a @Plugin annotation, which is essentially composed of following attributes:
- name
- 
Name of the plugin. It is recommended to be distinct among plugins sharing the same category.namematching is case-insensitive.
- category(optional)
- 
A name used for grouping a set of plugins. categorymatching is case-sensitive.
- elementType(deprecated)
- 
We don’t recommend the usage of elementTypeanymore. Existing usages are kept for backward compatibility reasons with the legacy configuration syntax:<appender type="ConsoleAppender".
See LowerLookup.java (a lookup for lower-casing its input) for a simple example.
Click to read more on name collision and overriding an existing plugin
The name attribute of plugins of a certain category is recommended to be distinct and this matching is case-insensitive.
In case of a name collision, a warning will be emitted, and the plugin discovery order will determine the effective plugin.
For example, to override the File plugin which is provided by the built-in File Appender, you would need to place your plugin in a JAR file in the classpath ahead of Log4j Core JAR.
In an OSGi environment, the order that bundles are scanned for plugins generally follows the same order that bundles were installed into the framework; see
getBundles()
and
SynchronousBundleListener.
In short, name collisions are even more unpredictable in an OSGi environment.
Declaring plugins represented in a configuration file
If your plugin needs to be represented by an element in a configuration file (such as an appender, layout, logger, or filter), following requirements must be met:
- 
The categoryattribute of the@Pluginannotation must be set toNode.CATEGORY(Core)
- 
It must have a plugin factory 
See JsonTemplateLayout.java for an example and notice these details:
- 
There are two plugin declarations: JsonTemplateLayoutandJsonTemplateLayout.EventTemplateAdditionalField
- 
Both plugin declarations - 
Set the categoryattribute toNode.CATEGORY
- 
Provide a @PluginBuilderFactory-annotated static method
 
- 
Declaring plugin factories
A plugin factory is responsible for
- 
Creating an instance of the plugin 
- 
Receiving values ( Configurationinstance, configuration attributes, etc.) available in the context
Every plugin that needs to be represented by an element in a configuration file must declare a plugin factory using one of the following:
- a @PluginFactory-annotated static method
- 
What is expected to be received is modelled as method arguments. Intended for simple plugins that receive less than a handful of values. See CsvParameterLayout.javafor an example on@PluginFactoryusage.
- a @PluginBuilderFactory-annotated static method of return typeBuilder<T>
- 
What is expected to be received is modelled as fields of a builder class. Intended for more sophisticated wiring needs. Click for advantages of builder class over factory method- 
Attribute names don’t need to be specified, if they match the field name 
- 
Default values can be specified in code rather than through an annotation. This also allows a runtime-calculated default value, which isn’t allowed in annotations. 
- 
Default values are specified via code rather than relying on reflection and injection, so they work programmatically as well as in a configuration file. 
- 
Adding new optional parameters doesn’t require existing programmatic configuration to be refactored. 
- 
Easier to write unit tests using builders rather than factory methods with optional parameters. 
 See JsonTemplateLayout.javafor an example on@PluginBuilderFactoryusage.
- 
If a plugin class implements Collection or Map, then no factory method is used.
Instead, the class is instantiated using the default constructor, and all child configuration nodes are added to the Collection or Map.
Plugin factory attribute types
To allow the current Configuration to populate the correct arguments for the @PluginFactory-annotated method (or fields for the builder class), every argument to the method must be annotated using one of the following attribute types.
- @PluginAliases
- 
Identifies a list of aliases for a @Plugin,@PluginAttribute, or@PluginBuilderAttribute
- @PluginAttribute
- 
Denotes a configuration element attribute. The parameter must be convertible from a Stringusing aTypeConverter. Most built-in types are already supported, but customTypeConverterplugins may also be provided for more type support. Note thatPluginBuilderAttributecan be used in builder class fields as an easier way to provide default values.
- @PluginConfiguration
- 
The current Configurationobject will be passed to the plugin as a parameter.
-  @PluginElement
- 
The parameter may represent a complex object that itself has parameters that can be configured. This also supports injecting an array of elements. 
- @PluginNode
- 
The current Nodebeing parsed will be passed to the plugin as a parameter.
- @PluginValue
- 
The value of the current Nodeor its attribute namedvalue.
Each attribute or element annotation must include the name that must be present in the configuration in order to match the configuration item to its respective parameter. For plugin builders, the names of the fields will be used by default if no name is specified in the annotation.
Plugin factory attribute type converters
TypeConverters are a certain group of plugins for converting Strings read from configuration file elements into the types used in plugin factory attributes.
Other plugins can already be injected via the @PluginElement annotation; now, any type supported by TypeConverters can be used in a @PluginAttribute-annotated factory attribute.
Conversion of enum types are supported on demand and do not require custom TypeConverters.
A large number of built-in Java classes (int, long, BigDecimal, etc.) are already supported; see TypeConverters for a more exhaustive listing.
You can create custom TypeConverters as follows:
- 
Extend from the TypeConverterinterface
- 
Set the categoryattribute of the@Pluginannotation toTypeConverters.CATEGORY(TypeConverter). Unlike other plugins, the plugin name of aTypeConverteris purely cosmetic.
- 
Have a default constructor 
- 
Optionally, extend from Comparable<TypeConverter<?>>, which will be used for determining the order in case of multipleTypeConvertercandidates for a certain type
See TypeConverters.java for example implementations.
Plugin factory attribute validators
Plugin factory fields and parameters can be automatically validated at runtime using constraint validators inspired by Bean Validation.
The following annotations are bundled in Log4j, but custom ConstraintValidator can be created as well.
- @Required
- 
This annotation validates that a value is non-empty. This covers a check for null as well as several other scenarios: empty CharSequenceobjects, empty arrays, emptyCollectioninstances, and emptyMapinstances.
- @ValidHost
- 
This annotation validates that a value corresponds to a valid host name. This uses the same validation as InetAddress.getByName(String).
- @ValidPort
- 
This annotation validates that a value corresponds to a valid port number between 0 and 65535. 
Registering plugins
To properly work, each Log4j plugin needs:
- 
To be registered in the Log4j Plugin Descriptor (i.e., Log4j2Plugins.dat). This file is generated using thePluginProcessorannotation processor at compile-time.
- 
(Optionally) To be registered in the GraalVM reachability metadata descriptor, which will allow the plugin to be used in GraalVM native applications. The GraalVmProcessorannotation processor creates such a file at compile-time.
| The  | 
You need to configure your build tool as follows to use both plugin processors:
- 
Maven 
- 
Gradle 
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>${maven-compiler-plugin.version}</version>
  <configuration>
    <compilerArgs>
        <!-- Provide the project coordinates to `GraalVmProcessor`: -->
        <arg>-Alog4j.graalvm.groupId=${project.groupId}</arg>
        <arg>-Alog4j.graalvm.artifactId=${project.artifactId}</arg>
    </compilerArgs>
  </configuration>
  <executions>
    <execution>
        <!--
          ~ Explicitly list the annotation processors for the default compile execution.
          ~ This is required starting with JDK 23, where annotation processors are not enabled automatically.
          ~ Explicit configuration also improves build reliability and clarity.
          ~ For more details, see: https://inside.java/2024/06/18/quality-heads-up/
          -->
      <id>default-compile</id>
      <configuration>
        <annotationProcessorPaths>
          <!-- Include `log4j-core` providing
               1. `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` that generates `Log4j2Plugins.dat`
               2. `org.apache.logging.log4j.core.config.plugins.processor.GraalVmProcessor` that generates the GraalVM reachability metadata file -->
          <path>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.25.2</version>
          </path>
        </annotationProcessorPaths>
        <annotationProcessors>
          <!-- Process sources using `PluginProcessor` to generate `Log4j2Plugins.dat` -->
          <processor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</processor>
          <!-- Process sources using `GraalVmProcessor` to generate a GraalVM reachability metadata file -->
          <processor>org.apache.logging.log4j.core.config.plugins.processor.GraalVmProcessor</processor>
        </annotationProcessors>
      </configuration>
    </execution>
  </executions>
</plugin>compileJava {
  // Provide the project coordinates to the `GraalVmProcessor`:
  options.compilerArgs << '-Alog4j.graalvm.groupId=org.example'
  options.compilerArgs << '-Alog4j.graalvm.artifactId=example'
}
dependencies {
  // Process sources using:
  // * `PluginProcessor` to generate `Log4j2Plugins.dat`
  // * `GraalVmProcessor` to generate a GraalVM reachability metadata file
  annotationProcessor('org.apache.logging.log4j:log4j-core:2.25.2')
}Discovering plugins
PluginManager is responsible for discovering plugins and loading their descriptions.
It locates plugins by looking in following places in given order:
- 
Plugin descriptor files on the classpath (using the class loader that loaded the log4j-coreartifact). These files are generated automatically at compile-time by the Log4j plugin annotation processor. See Registering plugins for details.
- 
[OSGi only] Serialized plugin listing files in each active OSGi bundle. A BundleListeneris added on activation to continue checking new bundles after Log4j Core has started.
- 
[Deprecated] A comma-separated list of packages specified by the log4j.plugin.packagessystem property
- 
[Deprecated] Packages passed to the static PluginManager.addPackages()method before Log4j configuration takes place
- 
[Deprecated] The packagesattribute declared at the root element of your Log4j configuration file
Loading plugins
It is pretty common that a plugin uses other plugins; appenders accept layouts, some layouts accept key-value pairs, etc. You can do this as follows:
- 
If your plugin has a plugin factory (i.e., it is represented by a configuration file element), you can use the @PluginElementannotation to receive other plugins. See@PluginElement("EventTemplateAdditionalField")usage inJsonTemplateLayout.javafor an example.
- 
Otherwise, you can use PluginUtil, which is a convenient wrapper aroundPluginManager, to discover and load plugins. SeeTemplateResolverFactories.javafor example usages.