blob: 1f18c5c23b02b514c2e76dc3b694027276705f3d [file] [log] [blame]
////
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.
////
= 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 https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs.
[NOTE]
====
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.
====
[TIP]
====
Did you know about *xref:plugin-reference.adoc[], 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:
. <<#declare-plugin,How can you declare a plugin?>>
. <<#core,How can you declare a plugin that needs to be represented in a Log4j configuration file?>>
. <<#plugin-registry,How can you register your plugin to Log4j?>>
. <<#plugin-discovery,How does Log4j discover plugins?>>
. <<#plugin-load,How can you load other plugins in a plugin?>>
[#declare-plugin]
== Declaring plugins
A class can be declared as a plugin by adding a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@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`.
`name` matching is case-insensitive.
`category` (optional)::
A name used for grouping a set of plugins.
`category` matching is case-sensitive.
`elementType` (deprecated)::
We don't recommend the usage of `elementType` anymore.
Existing usages are kept for backward compatibility reasons with the legacy configuration syntax: `<appender type="ConsoleAppender"`.
See {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/LowerLookup.java[`LowerLookup.java`] (a xref:manual/lookups.adoc[lookup] for lower-casing its input) for a simple example.
.Click to read more on *name collision* and *overriding an existing plugin*
[%collapsible]
====
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 <<plugin-discovery,discovery order>> will determine the effective plugin.
For example, to override the `File` plugin which is provided by the built-in xref:manual/appenders/file.adoc#FileAppender[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
https://docs.osgi.org/javadoc/r6/core/org/osgi/framework/BundleContext.html#getBundles()[`getBundles()`]
and
https://docs.osgi.org/javadoc/r6/core/org/osgi/framework/SynchronousBundleListener.html[`SynchronousBundleListener`].
In short, name collisions are even more unpredictable in an OSGi environment.
====
[#core]
== Declaring plugins represented in a configuration file
If your plugin needs to be represented by an element in a configuration file (such as an xref:manual/appenders.adoc[appender], xref:manual/layouts.adoc[layout], xref:manual/api.adoc#loggers[logger], or xref:manual/filters.adoc[filter]), following requirements must be met:
* The `category` attribute of the `@Plugin` annotation must be set to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Node.html#CATEGORY[`Node.CATEGORY`] (`Core`)
* It must have a xref:declare-plugin-factory[plugin factory]
See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example and notice these details:
* There are two plugin declarations: `JsonTemplateLayout` and `JsonTemplateLayout.EventTemplateAdditionalField`
* Both plugin declarations
** Set the `category` attribute to `Node.CATEGORY`
** Provide a `@PluginBuilderFactory`-annotated static method
[#declare-plugin-factory]
=== Declaring plugin factories
A *plugin factory* is responsible for
* Creating an instance of the plugin
* Receiving values (`Configuration` instance, 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 link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginFactory.html[`@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 {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/CsvParameterLayout.java[`CsvParameterLayout.java`] for an example on `@PluginFactory` usage.
a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginBuilderFactory.html[`@PluginBuilderFactory`]-annotated static method of return type
link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/Builder.html[`Builder<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
[%collapsible]
====
* 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 {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example on `@PluginBuilderFactory` usage.
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`.
[#attribute-types]
==== 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.
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginAliases.html[`@PluginAliases`]::
Identifies a list of aliases for a `@Plugin`, `@PluginAttribute`, or `@PluginBuilderAttribute`
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginAttribute.html[`@PluginAttribute`]::
Denotes a configuration element attribute.
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 that `PluginBuilderAttribute` can be used in builder class fields as an easier way to provide default values.
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.html[`@PluginConfiguration`]::
The current `Configuration` object will be passed to the plugin as a parameter.
[[PluginElement]] link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginElement.html[`@PluginElement`]::
The parameter may represent a complex object that itself has parameters that can be configured.
This also supports injecting an array of elements.
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginNode.html[`@PluginNode`]::
The current `Node` being parsed will be passed to the plugin as a parameter.
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginValue.html[`@PluginValue`]::
The value of the current `Node` or its attribute named `value`.
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.
[#type-converters]
==== Plugin factory attribute type converters
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.html[`TypeConverter`]s are a certain group of plugins for converting ``String``s read from configuration file elements into the types used in plugin factory attributes.
Other plugins can already be injected via <<PluginElement,the `@PluginElement` annotation>>; now, any type supported by ``TypeConverter``s can be used in a `@PluginAttribute`-annotated factory attribute.
Conversion of enum types are supported on demand and do not require custom ``TypeConverter``s.
A large number of built-in Java classes (`int`, `long`, `BigDecimal`, etc.) are already supported; see link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.html[`TypeConverters`] for a more exhaustive listing.
You can create custom ``TypeConverter``s as follows:
* Extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverter.html[the `TypeConverter` interface]
* Set the `category` attribute of the `@Plugin` annotation to link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.html#CATEGORY[`TypeConverters.CATEGORY`] (`TypeConverter`).
Unlike other plugins, the plugin name of a `TypeConverter` is purely cosmetic.
* Have a default constructor
* Optionally, extend from `Comparable<TypeConverter<?>>`, which will be used for determining the order in case of multiple `TypeConverter` candidates for a certain type
See {project-github-url}/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/TypeConverters.java[`TypeConverters.java`] for example implementations.
[#constraint-validators]
==== Plugin factory attribute validators
Plugin factory fields and parameters can be automatically validated at runtime using constraint validators inspired by https://beanvalidation.org[Bean Validation].
The following annotations are bundled in Log4j, but custom ``ConstraintValidator`` can be created as well.
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/validation/constraints/Required.html[`@Required`]::
This annotation validates that a value is non-empty.
This covers a check for null as well as several other scenarios: empty `CharSequence` objects, empty arrays, empty `Collection` instances, and empty `Map` instances.
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidHost.html[`@ValidHost`]::
This annotation validates that a value corresponds to a valid host name.
This uses the same validation as https://docs.oracle.com/javase/{java-target-version}/docs/api/java/net/InetAddress.html#getByName-java.lang.String-[`InetAddress.getByName(String)`].
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/validation/constraints/ValidPort.html[`@ValidPort`]::
This annotation validates that a value corresponds to a valid port number between 0 and 65535.
[#plugin-registry]
== Registering plugins
Registering plugins are done by placing a *Log4j plugin descriptor* (i.e., `Log4j2Plugins.dat`) into the classpath.
This file is generated using the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.html[`PluginProcessor`] annotation processor at compile-time.
You need to configure your build tool as follows to employ `PluginProcessor` by the Java compiler:
[tabs]
====
Maven::
+
[source,xml,subs="+attributes"]
----
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<executions>
<execution>
<id>generate-log4j-plugin-descriptor</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<proc>only</proc>
<annotationProcessorPaths>
<!-- Include `log4j-core` providing `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` that generates `Log4j2Plugins.dat` -->
<path>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>{log4j-core-version}</version>
</path>
</annotationProcessorPaths>
<annotationProcessors>
<!-- Process sources using `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` to generate `Log4j2Plugins.dat` -->
<processor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</processor>
</annotationProcessors>
</configuration>
</execution>
</executions>
</plugin>
----
Gradle::
+
[source,groovy,subs="+attributes"]
----
dependencies {
// Process sources using `log4j-core` providing `org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor` that generates `Log4j2Plugins.dat` -->
annotationProcessor('org.apache.logging.log4j:log4j-core:{log4j-core-version}')
}
----
====
[#plugin-discovery]
== Discovering plugins
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/util/PluginManager.html[`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-core` artifact).
These files are generated automatically at compile-time by the Log4j plugin annotation processor.
See <<plugin-registry>> for details.
. *[OSGi only]* Serialized plugin listing files in each active OSGi bundle.
A `BundleListener` is 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.packages` system property
. *[Deprecated]* Packages passed to the static `PluginManager.addPackages()` method before Log4j configuration takes place
. *[Deprecated]* The `packages` attribute declared at the root element of your Log4j configuration file
[#plugin-load]
== 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 <<#declare-plugin-factory,plugin factory>> (i.e., it is represented by a configuration file element), you can use <<#PluginElement, the `@PluginElement` annotation>> to receive other plugins.
See `@PluginElement("EventTemplateAdditionalField")` usage in {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example.
* Otherwise, you can use
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/util/PluginUtil.html[`PluginUtil`],
which is a convenient wrapper around <<#plugin-discovery,`PluginManager`>>, to discover and load plugins.
See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolverFactories.java[`TemplateResolverFactories.java`] for example usages.