Plugins
Log4j 1.x allowed for extension by requiring class attributes on most of the configuration declarations. In the case of some elements, notably the PatternLayout, the only way to add new pattern converters was to extend the PatternLayout class and add them via code. One goal of Log4j 2 is to make extending it extremely easy through the use of plugins.
In Log4j 3.x, a plugin is declared by adding a @Plugin
and @Namespace
annotation to the class declaration.
During initialization the
Configuration
will invoke the PluginRegistry
to load the built-in Log4j plugins as well as any custom plugins. The
Injector
locates plugins by looking in the following places:
-
Plugin collection classes on the classpath that are loaded by java.util.ServiceLoader. These classes are generated automatically during the build (more details below).
-
(OSGi only) Serialized plugin listing files in each active OSGi bundle. A
BundleListener
is added on activation to continue checking new bundles afterlog4j-plugins
has started. Bundles must register their plugin collection class as an OSGi service. -
Serialized plugin listing files on the classpath. These files were generated by the plugin annotation processor in Log4j 2 2.x. These are processed to allow compatibility.
When multiple plugins use the same case-insensitive name
within the same plugin category, then which one is selected is determined first by the presence of @PluginOrder
annotations and then by the previously described plugin loading order.
For example, to override the File
plugin which is provided by the built-in FileAppender
class, you would need to place your plugin in a JAR file in the CLASSPATH ahead of`log4j-core.jar`.
This is not recommended; plugin name collisions will cause a warning to be emitted.
Note that 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 without additional @PluginOrder
usage.
Plugin collection classes are generated by an annotation processor contained in the log4j-plugins artifact which will automatically scan your code for Log4j 2 plugins and generate a Java source file that references all the located plugins. It will also generate a META-INF/services/org.apache.logging.log4j.plugins.model.PluginService file in compliance with java.util.ServiceLoader. There is nothing extra that needs to be done to enable this; the Java compiler will automatically pick up the annotation processor on the class path unless you explicitly disable it.
If annotation processing is disabled plugins may still be registered by either
-
manually providing a class that extends
org.apache.logging.log4j.plugins.model.PluginService
and identifies all the plugins and also declaring aMETA-INF/services/org.apache.logging.log4j.plugins.model.PluginService
file that provides the fully qualified name of the implemented class or -
adding another compiler pass to the build process that only handles annotation processing using the Log4j 2 annotation processor class,
org.apache.logging.log4j.plugin.processor.PluginProcessor
. To do this using Apache Maven, add the following execution to your maven-compiler-plugin (version 2.2 or higher) build plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>log4j-plugin-processor</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<proc>only</proc>
<annotationProcessors>
<annotationProcessor>org.apache.logging.log4j.plugin.processor.PluginProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</execution>
</executions>
</plugin>
As the configuration is processed the appropriate plugins will be automatically configured and initialized.
Log4j 3 utilizes a few different namespaces of plugins which are described in the following sections.
Core
Core plugins are those that are directly represented by an element in a
configuration file and correspond to @Configurable
namespaced @Plugin
classes such as an Appender
, Layout
, Logger
or Filter
.
Custom plugins that conform to the rules laid out in the next paragraph may simply be referenced in the configuration.
Core plugins support a few different dependency injection rules for binding configuration node data and other injectable classes into a plugin instance.
Each plugin must declare a static method annotated with @Factory
or @PluginFactory
or contain an @Inject
-annotated constructor or no-args constructor.
These static factory method or @Inject
constructor parameters are used for initial dependency injection.
When a static factory method returns a type that implements java.util.function.Supplier<T>
such as through Builder<T>
, then that supplier instance first has its members injected before invoking Supplier::get
to obtain the resulting plugin instance.
See Dependency Injection for further details on how dependency injection works.
Plugins are configured using additional plugin qualifier annotations such as @PluginAttribute
, @PluginBuilderAttribute
, @PluginElement
, @PluginValue
, etc., as described below.
There are dozens of plugins in Log4j Core that can be
used as examples for more complex scenarios including hierarchical
builder classes (e.g., see FileAppender
). See
Extending Log4j with Plugin
Builders for more details.
Attribute Types
PluginAttribute
-
The parameter must be convertible from a String using a TypeConverter. Most built-in types are already supported, but custom
TypeConverter
plugins may also be provided for more type support. Note thatPluginBuilderAttribute
can be used in builder class fields as an easier way to provide default values. PluginElement
-
The parameter may represent a complex object that itself has parameters that can be configured. This also supports injecting an array of elements.
PluginConfiguration
-
The current
Configuration
object will be passed to the plugin as a parameter. PluginNode
-
The current
Node
being parsed will be passed to the plugin as a parameter. PluginValue
-
The value of the current
Node
or its attribute namedvalue
.
Constraint Validators
Plugin factory fields, methods, and parameters can be automatically validated at
runtime using constraint validators inspired by the
Bean Validation spec. The following
annotations are bundled in Log4j, but custom
ConstraintValidators
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: emptyCharSequence
objects, empty arrays, emptyCollection
instances, and emptyMap
instances. RequiredClass
-
This annotation validates that a class name can be loaded. This is useful for plugins that should only be loaded when an optional class is present.
ValidHost
-
This annotation validates that a value corresponds to a valid hostname. This uses the same validation as
InetAddress::getByName
. ValidPort
-
This annotation validates that a value corresponds to a valid port number between 0 and 65535.
Converters
Converters are used by PatternLayout
to render the elements identified by the conversion pattern. Every
converter must specify its category as "Converter" on the @Plugin
annotation, have a static newInstance
method that accepts an array of
String
as its only parameter and returns an instance of the
Converter, and must have a @ConverterKeys
annotation present that
contains the array of converter patterns that will cause the Converter
to be selected. Converters that are meant to handle LogEvent
must
extend the
LogEventPatternConverter
class and must implement a format method that accepts a LogEvent
and a
StringBuilder
as arguments. The Converter should append the result of
its operation to the StringBuilder
.
A second type of Converter is the FileConverter - which must have
"FileConverter" specified in the category attribute of the @Plugin
annotation. While similar to a LogEventPatternConverter
, instead of a
single format method these Converters will have two variations; one that
takes an Object
and one that takes an array of Object
instead of
the LogEvent
. Both append to the provided StringBuilder
in the same
fashion as a LogEventPatternConverter
. These Converters are typically
used by the RollingFileAppender
to construct the name of the file to
log to.
If multiple Converters specify the same ConverterKeys
, then the load
order above determines which one will be used. For example, to override
the %date
converter which is provided by the built-in
DatePatternConverter
class, you would need to place your plugin in a
JAR file in the CLASSPATH ahead of log4j-core.jar
. This is not
recommended; pattern ConverterKeys collisions will cause a warning to be
emitted. Try to use unique ConverterKeys for your custom pattern
converters.
KeyProviders
Some components within Log4j may provide the ability to perform data
encryption. These components require a secret key to perform the
encryption. Applications may provide the key by creating a class that
implements the
SecretKeyProvider
interface.
Lookups
Lookups are perhaps the simplest plugins of all. They must declare their
type as "Lookup" on the plugin annotation and must implement the
StrLookup
interface. They will have two methods; a lookup
method that accepts a
String
key and returns a String
value and a second lookup
method that
accepts both a LogEvent
and a String
key and returns a String
. Lookups
may be referenced by specifying $\{name:key} where name is the name
specified in the Plugin annotation and key is the name of the item to
locate.
TypeConverters
TypeConverter
s
are a sort of meta-plugin used for converting strings into other types
in a plugin factory method parameter. Other plugins can already be
injected via the @PluginElement
annotation; now, any type supported by
the type conversion system can be used in a @PluginAttribute
parameter. Conversion of enum types are supported on demand and do not
require custom TypeConverter
classes. A large number of built-in Java
classes are already supported; see
TypeConverters
and
CoreConverters
for a more exhaustive listing.
Unlike other plugins, the plugin name of a TypeConverter
is purely
cosmetic. Appropriate type converters are looked up via the Type
interface rather than via Class<?>
objects only. Do note that
TypeConverter
plugins must have a default constructor.
When multiple converters match for a type, the first will be returned.
If any extends from Comparable<TypeConverter<?>>
, it will be used for determining the order.
Developer Notes
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
.