Plugins
Introduction
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 2 a plugin is declared by adding a
@Plugin
annotation to the class declaration. During initialization the
Configuration
will invoke the
PluginManager
to load the built-in Log4j plugins as well as any custom plugins. The PluginManager
locates
plugins by looking in five places:
- Serialized plugin listing files on the classpath. These files 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-core
has started. - (Deprecated) A comma-separated list of packages specified by the
log4j.plugin.packages
system property. - (Deprecated) Packages passed to the static
PluginManager.addPackages
method (before Log4j configuration occurs). - (Deprecated) The packages declared in your log4j2 configuration file.
If multiple Plugins specify the same (case-insensitive) name
, then the load order above
determines which one will be used. 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 oflog4j-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.
Serialized plugin listing files are generated by an annotation processor contained in the
log4j-core artifact which will automatically scan your code for Log4j 2 plugins and output a metadata
file in your processed classes. 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. In that case, it would be important to add another compiler pass to your build process that
only handles annotation processing using the Log4j 2 annotation processor class,
org.apache.logging.log4j.core.config.plugins.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.core.config.plugins.processor.PluginProcessor</annotationProcessor> </annotationProcessors> </configuration> </execution> </executions> </plugin>
As the configuration is processed the appropriate plugins will be automatically configured and initialized. Log4j 2 utilizes a few different categories 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, 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, provided they are appropriate configured to be loaded by the PluginManager.
Every Core plugin must declare a static method annotated with @PluginFactory
or
@PluginBuilderFactory
. The former is used for static factory methods that provide all options
as method parameters, and the latter is used to construct a new Builder<T>
class whose
fields are used for injecting attributes and child nodes. To allow the Configuration
to pass
the correct parameters to the method, every parameter to the method must be annotated as one of the following
attribute types. 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. 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 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. - 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
s 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
s
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
s 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 oflog4j-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
TypeConverters
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
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
.