| //// |
| 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. |