You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2018/04/08 21:03:43 UTC
[2/7] logging-log4j2 git commit: LOG4J2-1802: Convert extending Log4j
manual page to asciidoc
LOG4J2-1802: Convert extending Log4j manual page to asciidoc
Also has some documentation fixes
Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/7b4493f2
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/7b4493f2
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/7b4493f2
Branch: refs/heads/master
Commit: 7b4493f2205b997741b0a3b06eca214dcbeee459
Parents: 77a3712
Author: Matt Sicker <bo...@gmail.com>
Authored: Sun Apr 8 15:46:28 2018 -0500
Committer: Matt Sicker <bo...@gmail.com>
Committed: Sun Apr 8 16:03:36 2018 -0500
----------------------------------------------------------------------
src/site/asciidoc/manual/extending.adoc | 624 +++++++++++++++++++++++++++
src/site/xdoc/manual/extending.xml | 565 ------------------------
2 files changed, 624 insertions(+), 565 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7b4493f2/src/site/asciidoc/manual/extending.adoc
----------------------------------------------------------------------
diff --git a/src/site/asciidoc/manual/extending.adoc b/src/site/asciidoc/manual/extending.adoc
new file mode 100644
index 0000000..db5a6f9
--- /dev/null
+++ b/src/site/asciidoc/manual/extending.adoc
@@ -0,0 +1,624 @@
+////
+ 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
+
+ https://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.
+////
+= Extending Log4j 2
+Ralph Goers <rg...@apache.org>
+
+Log4j 2 provides numerous ways that it can be manipulated and extended.
+This section includes an overview of the various ways that are directly
+supported by the Log4j 2 implementation.
+
+[#LoggerContextFactory]
+== LoggerContextFactory
+
+The `LoggerContextFactory` binds the Log4j API to its implementation.
+The Log4j `LogManager` locates a `LoggerContextFactory` by using
+java.util.ServiceLoader to locate all instances of
+`org.apache.logging.log4j.spi.Provider`. Each implementation must
+provide a class that extends`org.apache.logging.log4j.spi.Provider` and
+should have a no-arg constructor that delegates to Provider's
+constructor passing the Priority, the API versions it is compatible
+with, and the class that implements
+`org.apache.logging.log4j.spi.LoggerContextFactory`. Log4j will compare
+the current API version and if it is compatible the implementation will
+be added to the list of providers. The API version in
+`org.apache.logging.log4j.LogManager` is only changed when a feature is
+added to the API that implementations need to be aware of. If more than
+one valid implementation is located the value for the Priority will be
+used to identify the factory with the highest priority. Finally, the
+class that implements
+`org.apache.logging.log4j.spi.LoggerContextFactory` will be instantiated
+and bound to the LogManager. In Log4j 2 this is provided by
+`Log4jContextFactory`.
+
+Applications may change the LoggerContextFactory that will be used by
+
+1. Create a binding to the logging implementation.
+.. Implement a new `LoggerContextFactory`.
+.. Implement a class that extends `org.apache.logging.spi.Provider.`
+with a no-arg constructor that calls super-class's constructor with the
+Priority, the API version(s), `LoggerContextFactory` class, and
+optionally, a `ThreadContextMap` implementation class.
+.. Create a `META-INF/services/org.apache.logging.spi.Provider` file
+that contains the name of the class that implements
+`org.apache.logging.spi.Provider`.
+2. Setting the system property log4j2.loggerContextFactory to the name
+of the `LoggerContextFactory` class to use.
+3. Setting the property "log4j2.loggerContextFactory" in a properties
+file named "log4j2.LogManager.properties" to the name of the
+LoggerContextFactory class to use. The properties file must be on the
+classpath.
+
+[#ContextSelector]
+== ContextSelector
+
+ContextSelectors are called by the
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[Log4j
+LoggerContext factory]. They perform the actual work of locating or
+creating a LoggerContext, which is the anchor for Loggers and their
+configuration. ContextSelectors are free to implement any mechanism they
+desire to manage LoggerContexts. The default Log4jContextFactory checks
+for the presence of a System Property named "Log4jContextSelector". If
+found, the property is expected to contain the name of the Class that
+implements the ContextSelector to be used.
+
+Log4j provides five ContextSelectors:
+
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/selector/BasicContextSelector.html[`BasicContextSelector`]::
+ Uses either a LoggerContext that has been stored in a ThreadLocal or a
+ common LoggerContext.
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html[`ClassLoaderContextSelector`]::
+ Associates LoggerContexts with the ClassLoader that created the caller
+ of the getLogger call. This is the default ContextSelector.
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/selector/JndiContextSelector.html[`JndiContextSelector`]::
+ Locates the LoggerContext by querying JNDI.
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/async/AsyncLoggerContextSelector.html[`AsyncLoggerContextSelector`]::
+ Creates a LoggerContext that ensures that all loggers are
+ AsyncLoggers.
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/osgi/BundleContextSelector.html[`BundleContextSelector`]::
+ Associates LoggerContexts with the ClassLoader of the bundle that
+ created the caller of the getLogger call. This is enabled by default
+ in OSGi environments.
+
+[#ConfigurationFactory]
+== ConfigurationFactory
+
+Modifying the way in which logging can be configured is usually one of
+the areas with the most interest. The primary method for doing that is
+by implementing or extending a
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`].
+Log4j provides two ways of adding new ConfigurationFactories. The first
+is by defining the system property named "log4j.configurationFactory" to
+the name of the class that should be searched first for a configuration.
+The second method is by defining the `ConfigurationFactory` as a `Plugin`.
+
+All the ConfigurationFactories are then processed in order. Each factory
+is called on its `getSupportedTypes` method to determine the file
+extensions it supports. If a configuration file is located with one of
+the specified file extensions then control is passed to that
+`ConfigurationFactory` to load the configuration and create the
+`Configuration` object.
+
+Most `Configuration` extend the `BaseConfiguration` class. This class
+expects that the subclass will process the configuration file and create
+a hierarchy of `Node` objects. Each `Node` is fairly simple in that it
+consists of the name of the node, the name/value pairs associated with
+the node, The `PluginType` of the node and a List of all of its child
+Nodes. `BaseConfiguration` will then be passed the `Node` tree and
+instantiate the configuration objects from that.
+
+[source,java]
+----
+@Plugin(name = "XMLConfigurationFactory", category = "ConfigurationFactory")
+@Order(5)
+public class XMLConfigurationFactory extends ConfigurationFactory {
+
+ /**
+ * Valid file extensions for XML files.
+ */
+ public static final String[] SUFFIXES = new String[] {".xml", "*"};
+
+ /**
+ * Returns the Configuration.
+ * @param loggerContext The logger context.
+ * @param source The InputSource.
+ * @return The Configuration.
+ */
+ @Override
+ public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
+ return new XmlConfiguration(loggerContext, source);
+ }
+
+ /**
+ * Returns the file suffixes for XML files.
+ * @return An array of File extensions.
+ */
+ public String[] getSupportedTypes() {
+ return SUFFIXES;
+ }
+}
+----
+
+[#LoggerConfig]
+== LoggerConfig
+
+`LoggerConfig` objects are where Loggers created by applications tie into
+the configuration. The Log4j implementation requires that all
+LoggerConfigs be based on the LoggerConfig class, so applications
+wishing to make changes must do so by extending the `LoggerConfig` class.
+To declare the new `LoggerConfig`, declare it as a Plugin of type "Core"
+and providing the name that applications should specify as the element
+name in the configuration. The `LoggerConfig` should also define a
+PluginFactory that will create an instance of the `LoggerConfig`.
+
+The following example shows how the root `LoggerConfig` simply extends a
+generic `LoggerConfig`.
+
+[source,java]
+----
+@Plugin(name = "root", category = "Core", printObject = true)
+public static class RootLogger extends LoggerConfig {
+
+ @PluginFactory
+ public static LoggerConfig createLogger(@PluginAttribute(value = "additivity", defaultBooleanValue = true) boolean additivity,
+ @PluginAttribute(value = "level", defaultStringValue = "ERROR") Level level,
+ @PluginElement("AppenderRef") AppenderRef[] refs,
+ @PluginElement("Filters") Filter filter) {
+ List<AppenderRef> appenderRefs = Arrays.asList(refs);
+ return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additivity);
+ }
+}
+----
+
+[#LogEventFactory]
+== LogEventFactory
+
+A `LogEventFactory` is used to generate LogEvents. Applications may
+replace the standard `LogEventFactory` by setting the value of the system
+property `log4j2.logEventFactory` to the name of the custom `LogEventFactory`
+class.
+
+Note: When log4j is configured to have link:async.html#AllAsync[all
+loggers asynchronous], log events are pre-allocated in a ring buffer and
+the `LogEventFactory` is not used.
+
+[#MessageFactory]
+== MessageFactory
+
+A `MessageFactory` is used to generate `Message` objects. Applications may
+replace the standard `ParameterizedMessageFactory` (or
+`ReusableMessageFactory` in garbage-free mode) by setting the value of the
+system property `log4j2.messageFactory` to the name of the custom
+`MessageFactory` class.
+
+Flow messages for the `Logger.entry()` and `Logger.exit()` methods have
+a separate `FlowMessageFactory`. Applications may replace the
+`DefaultFlowMessageFactory` by setting the value of the system property
+`log4j2.flowMessageFactory` to the name of the custom `FlowMessageFactory`
+class.
+
+[#Lookups]
+== Lookups
+
+Lookups are the means in which parameter substitution is performed.
+During Configuration initialization an "Interpolator" is created that
+locates all the Lookups and registers them for use when a variable needs
+to be resolved. The interpolator matches the "prefix" portion of the
+variable name to a registered Lookup and passes control to it to resolve
+the variable.
+
+A Lookup must be declared using a `@Plugin` annotation with a `type` of
+"Lookup". The `name` specified on the `@Plugin` annotation will be used to
+match the prefix. Unlike other Plugins, Lookups do not use a
+`@PluginFactory`. Instead, they are required to provide a constructor that
+accepts no arguments. The example below shows a Lookup that will return
+the value of a System Property.
+
+The provided Lookups are documented here: link:./lookups.html[Lookups]
+
+[source,java]
+----
+@Plugin(name = "sys", category = "Lookup")
+public class SystemPropertiesLookup implements StrLookup {
+
+ /**
+ * Lookup the value for the key.
+ * @param key the key to be looked up, may be null
+ * @return The value for the key.
+ */
+ public String lookup(String key) {
+ return System.getProperty(key);
+ }
+
+ /**
+ * Lookup the value for the key using the data in the LogEvent.
+ * @param event The current LogEvent.
+ * @param key the key to be looked up, may be null
+ * @return The value associated with the key.
+ */
+ public String lookup(LogEvent event, String key) {
+ return System.getProperty(key);
+ }
+}
+----
+
+[#Filters]
+== Filters
+
+As might be expected, Filters are the used to reject or accept log
+events as they pass through the logging system. A Filter is declared
+using a `@Plugin` annotation of `type` "Core" and an `elementType` of "filter".
+The `name` attribute on the Plugin annotation is used to specify the name
+of the element users should use to enable the Filter. Specifying the
+`printObject` attribute with a value of "true" indicates that a call to
+`toString` will format the arguments to the filter as the configuration is
+being processed. The Filter must also specify a `@PluginFactory` method
+or `@PluginFactoryBuilder` builder class and method
+that will be called to create the Filter
+
+The example below shows a Filter used to reject LogEvents based upon
+their logging level. Notice the typical pattern where all the filter
+methods resolve to a single filter method.
+
+[source,java]
+----
+@Plugin(name = "ThresholdFilter", category = "Core", elementType = "filter", printObject = true)
+public final class ThresholdFilter extends AbstractFilter {
+
+ private final Level level;
+
+ private ThresholdFilter(Level level, Result onMatch, Result onMismatch) {
+ super(onMatch, onMismatch);
+ this.level = level;
+ }
+
+ public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
+ return filter(level);
+ }
+
+ public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
+ return filter(level);
+ }
+
+ public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
+ return filter(level);
+ }
+
+ @Override
+ public Result filter(LogEvent event) {
+ return filter(event.getLevel());
+ }
+
+ private Result filter(Level level) {
+ return level.isAtLeastAsSpecificAs(this.level) ? onMatch : onMismatch;
+ }
+
+ @Override
+ public String toString() {
+ return level.toString();
+ }
+
+ /**
+ * Create a ThresholdFilter.
+ * @param loggerLevel The log Level.
+ * @param match The action to take on a match.
+ * @param mismatch The action to take on a mismatch.
+ * @return The created ThresholdFilter.
+ */
+ @PluginFactory
+ public static ThresholdFilter createFilter(@PluginAttribute(value = "level", defaultStringValue = "ERROR") Level level,
+ @PluginAttribute(value = "onMatch", defaultStringValue = "NEUTRAL") Result onMatch,
+ @PluginAttribute(value = "onMismatch", defaultStringValue = "DENY") Result onMismatch) {
+ return new ThresholdFilter(level, onMatch, onMismatch);
+ }
+}
+----
+
+[#Appenders]
+== Appenders
+
+Appenders are passed an event, (usually) invoke a Layout to format the
+event, and then "publish" the event in whatever manner is desired.
+Appenders are declared as Plugins with a `type` of "Core" and an
+`elementType` of "appender". The `name` attribute on the Plugin annotation
+specifies the name of the element users must provide in their
+configuration to use the Appender. Appenders should specify `printObject`
+as "true" if the toString method renders the values of the attributes
+passed to the Appender.
+
+Appenders must also declare a `@PluginFactory` method or `@PluginFactoryBuilder`
+builder class and method that will create the appender. The example below shows
+an Appender named "Stub" that can be used as an initial template.
+
+Most Appenders use Managers. A manager actually "owns" the resources,
+such as an `OutputStream` or socket. When a reconfiguration occurs a new
+Appender will be created. However, if nothing significant in the
+previous Manager has changed, the new Appender will simply reference it
+instead of creating a new one. This insures that events are not lost
+while a reconfiguration is taking place without requiring that logging
+pause while the reconfiguration takes place.
+
+[source,java]
+----
+@Plugin(name = "Stub", category = "Core", elementType = "appender", printObject = true)
+public final class StubAppender extends OutputStreamAppender {
+
+ private StubAppender(String name, Layout layout, Filter filter, StubManager manager,
+ boolean ignoreExceptions) {
+ }
+
+ @PluginFactory
+ public static StubAppender createAppender(@PluginAttribute("name") String name,
+ @PluginAttribute("ignoreExceptions") boolean ignoreExceptions,
+ @PluginElement("Layout") Layout layout,
+ @PluginElement("Filters") Filter filter) {
+
+ if (name == null) {
+ LOGGER.error("No name provided for StubAppender");
+ return null;
+ }
+
+ StubManager manager = StubManager.getStubManager(name);
+ if (manager == null) {
+ return null;
+ }
+ if (layout == null) {
+ layout = PatternLayout.createDefaultLayout();
+ }
+ return new StubAppender(name, layout, filter, manager, ignoreExceptions);
+ }
+}
+----
+
+[#Layouts]
+== Layouts
+
+Layouts perform the formatting of events into the printable text that is
+written by Appenders to some destination. All Layouts must implement the
+`Layout` interface. Layouts that format the event into a `String` should
+extend `AbstractStringLayout`, which will take care of converting the
+`String` into the required byte array.
+
+Every Layout must declare itself as a plugin using the `@Plugin`
+annotation. The `type` must be "Core", and the `elementType` must be
+"layout". `printObject` should be set to "true" if the plugin's `toString`
+method will provide a representation of the object and its parameters.
+The name of the plugin must match the value users should use to specify
+it as an element in their Appender configuration. The plugin also must
+provide a static method annotated as a `@PluginFactory` and with each of
+the methods parameters annotated with `@PluginAttribute` or `@PluginElement` as
+appropriate. The plugin can alternatively use the plugin builder notation.
+
+[source,java]
+----
+@Plugin(name = "SampleLayout", category = "Core", elementType = "layout", printObject = true)
+public class SampleLayout extends AbstractStringLayout {
+
+ protected SampleLayout(boolean locationInfo, boolean properties, boolean complete,
+ Charset charset) {
+ }
+
+ @PluginFactory
+ public static SampleLayout createLayout(@PluginAttribute("locationInfo") boolean locationInfo,
+ @PluginAttribute("properties") boolean properties,
+ @PluginAttribute("complete") boolean complete,
+ @PluginAttribute(value = "charset", defaultStringValue = "UTF-8") Charset charset) {
+ return new SampleLayout(locationInfo, properties, complete, charset);
+ }
+}
+----
+
+[#PatternConverters]
+== PatternConverters
+
+PatternConverters are used by the PatternLayout to format the log event
+into a printable `String`. Each Converter is responsible for a single kind
+of manipulation, however Converters are free to format the event in
+complex ways. For example, there are several converters that manipulate
+Throwables and format them in various ways.
+
+A PatternConverter must first declare itself as a Plugin using the
+standard `@Plugin` annotation but must specify value of "Converter" on the
+`type` attribute. Furthermore, the Converter must also specify the
+`@ConverterKeys` annotation to define the tokens that can be specified in
+the pattern (preceded by a '%' character) to identify the Converter.
+
+Unlike most other Plugins, Converters do not use a `@PluginFactory`.
+Instead, each Converter is required to provide a static `newInstance`
+method that accepts an array of `String` as the only parameter. The
+`String[]` is the values that are specified within the curly braces
+that can follow the converter key.
+
+The following shows the skeleton of a Converter plugin.
+
+[source,java]
+----
+@Plugin(name = "query", category = "Converter")
+@ConverterKeys({"q", "query"})
+public final class QueryConverter extends LogEventPatternConverter {
+
+ public QueryConverter(String[] options) {
+ }
+
+ public static QueryConverter newInstance(final String[] options) {
+ return new QueryConverter(options);
+ }
+}
+----
+
+[#Plugin_Builders]
+== Plugin Builders
+
+Some plugins take a lot of optional configuration options. When a plugin
+takes many options, it is more maintainable to use a builder class
+rather than a factory method (see _Item 2: Consider a builder when faced
+with many constructor parameters_ in _Effective Java_ by Joshua Bloch).
+There are some other advantages to using an annotated builder class over
+an annotated 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 (also allowing a runtime-calculated default value which isn't
+allowed in annotations).
+* 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.
+* Default values are specified via code rather than relying on
+reflection and injection, so they work programmatically as well as in a
+configuration file.
+
+Here is an example of a plugin factory from `ListAppender`:
+
+[source,java]
+----
+@PluginFactory
+public static ListAppender createAppender(
+ @PluginAttribute("name") @Required(message = "No name provided for ListAppender") final String name,
+ @PluginAttribute("entryPerNewLine") final boolean newLine,
+ @PluginAttribute("raw") final boolean raw,
+ @PluginElement("Layout") final Layout<? extends Serializable> layout,
+ @PluginElement("Filter") final Filter filter) {
+ return new ListAppender(name, filter, layout, newLine, raw);
+}
+----
+
+Here is that same factory using a builder pattern instead:
+
+[source,java]
+----
+@PluginBuilderFactory
+public static Builder newBuilder() {
+ return new Builder();
+}
+
+public static class Builder implements org.apache.logging.log4j.core.util.Builder<ListAppender> {
+
+ @PluginBuilderAttribute
+ @Required(message = "No name provided for ListAppender")
+ private String name;
+
+ @PluginBuilderAttribute
+ private boolean entryPerNewLine;
+
+ @PluginBuilderAttribute
+ private boolean raw;
+
+ @PluginElement("Layout")
+ private Layout<? extends Serializable> layout;
+
+ @PluginElement("Filter")
+ private Filter filter;
+
+ public Builder setName(final String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setEntryPerNewLine(final boolean entryPerNewLine) {
+ this.entryPerNewLine = entryPerNewLine;
+ return this;
+ }
+
+ public Builder setRaw(final boolean raw) {
+ this.raw = raw;
+ return this;
+ }
+
+ public Builder setLayout(final Layout<? extends Serializable> layout) {
+ this.layout = layout;
+ return this;
+ }
+
+ public Builder setFilter(final Filter filter) {
+ this.filter = filter;
+ return this;
+ }
+
+ @Override
+ public ListAppender build() {
+ return new ListAppender(name, filter, layout, entryPerNewLine, raw);
+ }
+}
+----
+
+The only difference in annotations is using `@PluginBuilderAttribute`
+instead of `@PluginAttribute` so that default values and reflection can
+be used instead of specifying them in the annotation. Either annotation
+can be used in a builder, but the former is better suited for field
+injection while the latter is better suited for parameter injection.
+Otherwise, the same annotations (`@PluginConfiguration`,
+`@PluginElement`, `@PluginNode`, and `@PluginValue`) are all supported
+on fields. Note that a factory method is still required to supply a
+builder, and this factory method should be annotated with
+`@PluginBuilderFactory`.
+// TODO: this will change with LOG4J2-1188
+
+When plugins are being constructed after a configuration has been
+parsed, a plugin builder will be used if available, otherwise a plugin
+factory method will be used as a fallback. If a plugin contains neither
+factory, then it cannot be used from a configuration file (it can still
+be used programmatically of course).
+
+Here is an example of using a plugin factory versus a plugin builder
+programmatically:
+
+[source,java]
+----
+ListAppender list1 = ListAppender.createAppender("List1", true, false, null, null);
+ListAppender list2 = ListAppender.newBuilder().setName("List1").setEntryPerNewLine(true).build();
+----
+
+[#Custom_ContextDataInjector]
+== Custom ContextDataInjector
+
+The `ContextDataInjector` (introduced in Log4j 2.7) is responsible for
+populating the LogEvent's
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/LogEvent.html#getContextData()[context
+data] with key-value pairs or replacing it completely. The default
+implementation is `ThreadContextDataInjector`, which obtains context
+attributes from the ThreadContext.
+
+Applications may replace the default `ContextDataInjector` by setting the
+value of the system property `log4j2.contextDataInjector` to the name of
+the custom `ContextDataInjector` class.
+
+Implementors should be aware there are some subtleties related to
+thread-safety and implementing a context data injector in a garbage-free
+manner. See the
+link:../log4j-core/apidocs/org/apache/logging/log4j/core/ContextDataInjector.html[`ContextDataInjector`]
+javadoc for detail.
+
+== Custom ThreadContextMap implementations
+
+A garbage-free `StringMap`-based context map can be installed by setting
+system property `log4j2.garbagefreeThreadContextMap` to true. (Log4j
+must be link:garbagefree.html#Config[enabled] to use ThreadLocals.)
+
+Any custom `ThreadContextMap` implementation can be installed by setting
+system property `log4j2.threadContextMap` to the fully qualified class
+name of the class implementing the `ThreadContextMap` interface. By also
+implementing the `ReadOnlyThreadContextMap` interface, your custom
+`ThreadContextMap` implementation will be accessible to applications via the
+link:../log4j-api/apidocs/org/apache/logging/log4j/ThreadContext.html#getThreadContextMap()[`ThreadContext::getThreadContextMap`]
+method.
+
+[#Custom_Plugins]
+== Custom Plugins
+
+// TODO
+See the link:plugins.html[Plugins] section of the manual.
http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/7b4493f2/src/site/xdoc/manual/extending.xml
----------------------------------------------------------------------
diff --git a/src/site/xdoc/manual/extending.xml b/src/site/xdoc/manual/extending.xml
deleted file mode 100644
index ee0dd01..0000000
--- a/src/site/xdoc/manual/extending.xml
+++ /dev/null
@@ -1,565 +0,0 @@
-<?xml version="1.0"?>
-<!--
- 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.
--->
-
-<document xmlns="http://maven.apache.org/XDOC/2.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
- <properties>
- <title>Extending Log4j 2</title>
- <author email="rgoers@apache.org">Ralph Goers</author>
- </properties>
-<!-- TODO: add in documentation regarding typed attributes -->
- <body>
- <section name="Extending Log4j">
- <p>
- Log4j 2 provides numerous ways that it can be manipulated and extended. This section includes an
- overview of the various ways that are directly supported by the Log4j 2 implementation.
- </p>
- <subsection name="LoggerContextFactory">
- <p>
- The <code>LoggerContextFactory</code> binds the Log4j API to its implementation. The Log4j
- <code>LogManager</code> locates a <code>LoggerContextFactory</code> by using java.util.ServiceLoader
- to locate all instances of <code>org.apache.logging.log4j.spi.Provider</code>. Each implementation must
- provide a class that extends<code>org.apache.logging.log4j.spi.Provider</code> and should have a
- no-arg constructor that delegates to Provider's constructor passing the <var>Priority</var>,
- the API versions it is compatible with, and the class that implements
- <code>org.apache.logging.log4j.spi.LoggerContextFactory</code>. Log4j will compare the current API
- version and if it is compatible the implementation will be added to the list of providers. The
- API version in <code>org.apache.logging.log4j.LogManager</code> is only changed when a feature is added
- to the API that implementations need to be aware of. If more than one valid implementation is located
- the value for the <var>Priority</var> will be used to identify the factory with the highest priority.
- Finally, the class that implements <code>org.apache.logging.log4j.spi.LoggerContextFactory</code> will be
- instantiated and bound to the LogManager. In Log4j 2 this is provided by <code>Log4jContextFactory</code>.
- </p>
- <p>
- Applications may change the LoggerContextFactory that will be used by
- </p>
- <ol>
- <li>Create a binding to the logging implementation.
- <ol style="list-style-type: lower-alpha">
- <li>Implement a new <code>LoggerContextFactory</code>.</li>
- <li>Implement a class that extends <code>org.apache.logging.spi.Provider.</code> with a no-arg
- constructor that calls super-class's constructor with the <var>Priority</var>, the API version(s),
- <code>LoggerContextFactory</code> class, and optionally, a <code>ThreadContextMap</code>
- implementation class.</li>
- <li>Create a <code>META-INF/services/org.apache.logging.spi.Provider</code> file that contains the
- name of the class that implements <code>org.apache.logging.spi.Provider</code>.
- </li>
- </ol></li>
- <li>Setting the system property <var>log4j2.loggerContextFactory</var> to the name of the
- <code>LoggerContextFactory</code> class to use.
- </li>
- <li>Setting the property "log4j2.loggerContextFactory" in a properties file named
- "log4j2.LogManager.properties" to the name of the LoggerContextFactory class to use. The properties
- file must be on the classpath.
- </li>
- </ol>
- </subsection>
- <subsection name="ContextSelector">
- <p>
- ContextSelectors are called by the
- <a href="../log4j-core/apidocs/org/apache/logging/log4j/core/impl/Log4jContextFactory.html">Log4j
- LoggerContext factory</a>. They perform the actual work of
- locating or creating a LoggerContext, which is the anchor for Loggers and their configuration.
- ContextSelectors are free to implement any mechanism they desire to manage LoggerContexts. The
- default Log4jContextFactory checks for the presence of a System Property named "Log4jContextSelector".
- If found, the property is expected to contain the name of the Class that implements the
- ContextSelector to be used.
- </p>
- <p>
- Log4j provides five ContextSelectors:
- </p>
- <dl>
- <dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/selector/BasicContextSelector.html">BasicContextSelector</a></dt>
- <dd>Uses either a LoggerContext that has been stored in a ThreadLocal or a common LoggerContext.</dd>
- <dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html">ClassLoaderContextSelector</a></dt>
- <dd>Associates LoggerContexts with the ClassLoader that created the caller of the getLogger call. This is
- the default ContextSelector.</dd>
- <dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/selector/JndiContextSelector.html">JndiContextSelector</a></dt>
- <dd>Locates the LoggerContext by querying JNDI.</dd>
- <dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/async/AsyncLoggerContextSelector.html">AsyncLoggerContextSelector</a></dt>
- <dd>Creates a LoggerContext that ensures that all loggers are AsyncLoggers.</dd>
- <dt><a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/osgi/BundleContextSelector.html">BundleContextSelector</a></dt>
- <dd>Associates LoggerContexts with the ClassLoader of the bundle that created the caller of the getLogger
- call. This is enabled by default in OSGi environments.</dd>
- </dl>
- </subsection>
- <subsection name="ConfigurationFactory">
- <p>
- Modifying the way in which logging can be configured is usually one of the areas with the most
- interest. The primary method for doing that is by implementing or extending a
- <a href="../log4j-core/apidocs/org/apache/logging/log4j/core/config/ConfigurationFactory.html">ConfigurationFactory</a>.
- Log4j provides two ways of adding new ConfigurationFactories. The first is by defining the system
- property named "log4j.configurationFactory" to the name of the class that should be searched first
- for a configuration. The second method is by defining the ConfigurationFactory as a Plugin.
- </p>
- <p>
- All the ConfigurationFactories are then processed in order. Each factory is called on its
- getSupportedTypes method to determine the file extensions it supports. If a configuration file
- is located with one of the specified file extensions then control is passed to that
- ConfigurationFactory to load the configuration and create the Configuration object.
- </p>
- <p>
- Most Configuration extend the BaseConfiguration class. This class expects that the subclass will
- process the configuration file and create a hierarchy of Node objects. Each Node is fairly simple
- in that it consists of the name of the node, the name/value pairs associated with the node, The
- PluginType of the node and a List of all of its child Nodes. BaseConfiguration will then be
- passed the Node tree and instantiate the configuration objects from that.
- </p>
- <pre class="prettyprint linenums">
-@Plugin(name = "XMLConfigurationFactory", category = "ConfigurationFactory")
-@Order(5)
-public class XMLConfigurationFactory extends ConfigurationFactory {
-
- /**
- * Valid file extensions for XML files.
- */
- public static final String[] SUFFIXES = new String[] {".xml", "*"};
-
- /**
- * Returns the Configuration.
- * @param loggerContext The logger context.
- * @param source The InputSource.
- * @return The Configuration.
- */
- @Override
- public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
- return new XmlConfiguration(loggerContext, source);
- }
-
- /**
- * Returns the file suffixes for XML files.
- * @return An array of File extensions.
- */
- public String[] getSupportedTypes() {
- return SUFFIXES;
- }
-}</pre>
- </subsection>
- <subsection name="LoggerConfig">
- <p>
- LoggerConfig objects are where Loggers created by applications tie into the configuration. The Log4j
- implementation requires that all LoggerConfigs be based on the LoggerConfig class, so applications
- wishing to make changes must do so by extending the LoggerConfig class. To declare the new
- LoggerConfig, declare it as a Plugin of type "Core" and providing the name that applications
- should specify as the element name in the configuration. The LoggerConfig should also define
- a PluginFactory that will create an instance of the LoggerConfig.
- </p>
- <p>
- The following example shows how the root LoggerConfig simply extends a generic LoggerConfig.
- </p>
- <pre class="prettyprint linenums"><![CDATA[
-@Plugin(name = "root", category = "Core", printObject = true)
-public static class RootLogger extends LoggerConfig {
-
- @PluginFactory
- public static LoggerConfig createLogger(@PluginAttribute(value = "additivity", defaultBooleanValue = true) boolean additivity,
- @PluginAttribute(value = "level", defaultStringValue = "ERROR") Level level,
- @PluginElement("AppenderRef") AppenderRef[] refs,
- @PluginElement("Filters") Filter filter) {
- List<AppenderRef> appenderRefs = Arrays.asList(refs);
- return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additivity);
- }
-}]]></pre>
- </subsection>
- <subsection name="LogEventFactory">
- <p>A LogEventFactory is used to generate LogEvents. Applications may replace the standard LogEventFactory
- by setting the value of the system property Log4jLogEventFactory to the name of the custom
- LogEventFactory class. </p>
- <p>Note: When log4j is configured to have <a href="async.html#AllAsync">all loggers asynchronous</a>,
- log events are pre-allocated in a ring buffer and the LogEventFactory is not used.</p>
- </subsection>
- <subsection name="MessageFactory">
- <p>A MessageFactory is used to generate Message objects. Applications may replace the standard
- ParameterizedMessageFactory (or ReusableMessageFactory in garbage-free mode)
- by setting the value of the system property log4j2.messageFactory to the name of the custom
- MessageFactory class. </p>
- <p>Flow messages for the <tt>Logger.entry()</tt> and <tt>Logger.exit()</tt> methods have a separate FlowMessageFactory.
- Applications may replace the DefaultFlowMessageFactory by setting the value of the system property
- log4j2.flowMessageFactory to the name of the custom FlowMessageFactory class.
- </p>
- </subsection>
- <subsection name="Lookups">
- <p>
- Lookups are the means in which parameter substitution is performed. During Configuration initialization
- an "Interpolator" is created that locates all the Lookups and registers them for use when a variable
- needs to be resolved. The interpolator matches the "prefix" portion of the variable name to a
- registered Lookup and passes control to it to resolve the variable.
- </p>
- <p>
- A Lookup must be declared using a Plugin annotation with a type of "Lookup". The name specified on
- the Plugin annotation will be used to match the prefix. Unlike other Plugins, Lookups do not
- use a PluginFactory. Instead, they are required to provide a constructor that accepts no arguments.
- The example below shows a Lookup that will return the value of a System Property.
- </p>
- <p>The provided Lookups are documented here: <a href="./lookups.html">Lookups</a></p>
- <pre class="prettyprint linenums">
-@Plugin(name = "sys", category = "Lookup")
-public class SystemPropertiesLookup implements StrLookup {
-
- /**
- * Lookup the value for the key.
- * @param key the key to be looked up, may be null
- * @return The value for the key.
- */
- public String lookup(String key) {
- return System.getProperty(key);
- }
-
- /**
- * Lookup the value for the key using the data in the LogEvent.
- * @param event The current LogEvent.
- * @param key the key to be looked up, may be null
- * @return The value associated with the key.
- */
- public String lookup(LogEvent event, String key) {
- return System.getProperty(key);
- }
-}</pre>
- </subsection>
- <subsection name="Filters">
- <p>
- As might be expected, Filters are the used to reject or accept log events as they pass through the
- logging system. A Filter is declared using a Plugin annotation of type "Core" and an elementType of
- "filter". The name attribute on the Plugin annotation is used to specify the name of the element
- users should use to enable the Filter. Specifying the printObject attribute with a value of "true"
- indicates that a call to toString will format the arguments to the filter as the configuration
- is being processed. The Filter must also specify a PluginFactory method that will be called to
- create the Filter.
- </p>
- <p>
- The example below shows a Filter used to reject LogEvents based upon their logging level. Notice the
- typical pattern where all the filter methods resolve to a single filter method.
- </p>
- <pre class="prettyprint linenums">
-@Plugin(name = "ThresholdFilter", category = "Core", elementType = "filter", printObject = true)
-public final class ThresholdFilter extends AbstractFilter {
-
- private final Level level;
-
- private ThresholdFilter(Level level, Result onMatch, Result onMismatch) {
- super(onMatch, onMismatch);
- this.level = level;
- }
-
- public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) {
- return filter(level);
- }
-
- public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) {
- return filter(level);
- }
-
- public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) {
- return filter(level);
- }
-
- @Override
- public Result filter(LogEvent event) {
- return filter(event.getLevel());
- }
-
- private Result filter(Level level) {
- return level.isAtLeastAsSpecificAs(this.level) ? onMatch : onMismatch;
- }
-
- @Override
- public String toString() {
- return level.toString();
- }
-
- /**
- * Create a ThresholdFilter.
- * @param loggerLevel The log Level.
- * @param match The action to take on a match.
- * @param mismatch The action to take on a mismatch.
- * @return The created ThresholdFilter.
- */
- @PluginFactory
- public static ThresholdFilter createFilter(@PluginAttribute(value = "level", defaultStringValue = "ERROR") Level level,
- @PluginAttribute(value = "onMatch", defaultStringValue = "NEUTRAL") Result onMatch,
- @PluginAttribute(value = "onMismatch", defaultStringValue = "DENY") Result onMismatch) {
- return new ThresholdFilter(level, onMatch, onMismatch);
- }
-}</pre>
- </subsection>
- <subsection name="Appenders">
- <p>
- Appenders are passed an event, (usually) invoke a Layout to format the event, and then "publish"
- the event in whatever manner is desired. Appenders are declared as Plugins with a type of "Core"
- and an elementType of "appender". The name attribute on the Plugin annotation specifies the name
- of the element users must provide in their configuration to use the Appender. Appenders should
- specify printObject as "true" if the toString method renders the values of the attributes passed
- to the Appender.
- </p>
- <p>
- Appenders must also declare a PluginFactory method that will create the appender. The example
- below shows an Appender named "Stub" that can be used as an initial template.
- </p>
- <p>
- Most Appenders use Managers. A manager actually "owns" the resources, such as an OutputStream or
- socket. When a reconfiguration occurs a new Appender will be created. However, if nothing significant
- in the previous Manager has changed, the new Appender will simply reference it instead of creating a
- new one. This insures that events are not lost while a reconfiguration is taking place without
- requiring that logging pause while the reconfiguration takes place.
- </p>
- <pre class="prettyprint linenums">
-@Plugin(name = "Stub", category = "Core", elementType = "appender", printObject = true)
-public final class StubAppender extends OutputStreamAppender {
-
- private StubAppender(String name, Layout layout, Filter filter, StubManager manager,
- boolean ignoreExceptions) {
- }
-
- @PluginFactory
- public static StubAppender createAppender(@PluginAttribute("name") String name,
- @PluginAttribute("ignoreExceptions") boolean ignoreExceptions,
- @PluginElement("Layout") Layout layout,
- @PluginElement("Filters") Filter filter) {
-
- if (name == null) {
- LOGGER.error("No name provided for StubAppender");
- return null;
- }
-
- StubManager manager = StubManager.getStubManager(name);
- if (manager == null) {
- return null;
- }
- if (layout == null) {
- layout = PatternLayout.createDefaultLayout();
- }
- return new StubAppender(name, layout, filter, manager, ignoreExceptions);
- }
-}</pre>
- </subsection>
- <subsection name="Layouts">
- <p>
- Layouts perform the formatting of events into the printable text that is written by Appenders to
- some destination. All Layouts must implement the Layout interface. Layouts that format the
- event into a String should extend AbstractStringLayout, which will take care of converting the
- String into the required byte array.
- </p>
- <p>
- Every Layout must declare itself as a plugin using the Plugin annotation. The type must be "Core",
- and the elementType must be "layout". printObject should be set to true if the plugin's toString
- method will provide a representation of the object and its parameters. The name of the plugin must
- match the value users should use to specify it as an element in their Appender configuration.
- The plugin also must provide a static method annotated as a PluginFactory and with each of the
- methods parameters annotated with PluginAttr or PluginElement as appropriate.
- </p>
- <pre class="prettyprint linenums">
-@Plugin(name = "SampleLayout", category = "Core", elementType = "layout", printObject = true)
-public class SampleLayout extends AbstractStringLayout {
-
- protected SampleLayout(boolean locationInfo, boolean properties, boolean complete,
- Charset charset) {
- }
-
- @PluginFactory
- public static SampleLayout createLayout(@PluginAttribute("locationInfo") boolean locationInfo,
- @PluginAttribute("properties") boolean properties,
- @PluginAttribute("complete") boolean complete,
- @PluginAttribute(value = "charset", defaultStringValue = "UTF-8") Charset charset) {
- return new SampleLayout(locationInfo, properties, complete, charset);
- }
-}</pre>
- </subsection>
- <subsection name="PatternConverters">
- <p>
- PatternConverters are used by the PatternLayout to format the log event into a printable String. Each
- Converter is responsible for a single kind of manipulation, however Converters are free to format
- the event in complex ways. For example, there are several converters that manipulate Throwables and
- format them in various ways.
- </p>
- <p>
- A PatternConverter must first declare itself as a Plugin using the standard Plugin annotation but
- must specify value of "Converter" on the type attribute. Furthermore, the Converter must also
- specify the ConverterKeys attribute to define the tokens that can be specified in the pattern
- (preceded by a '%' character) to identify the Converter.
- </p>
- <p>
- Unlike most other Plugins, Converters do not use a PluginFactory. Instead, each Converter is
- required to provide a static newInstance method that accepts an array of Strings as the only
- parameter. The String array are the values that are specified within the curly braces that can
- follow the converter key.
- </p>
- <p>
- The following shows the skeleton of a Converter plugin.
- </p>
- <pre class="prettyprint linenums">
-@Plugin(name = "query", category = "Converter")
-@ConverterKeys({"q", "query"})
-public final class QueryConverter extends LogEventPatternConverter {
-
- public QueryConverter(String[] options) {
- }
-
- public static QueryConverter newInstance(final String[] options) {
- return new QueryConverter(options);
- }
-}</pre>
- </subsection>
- <subsection name="Plugin Builders">
- <p>
- Some plugins take a lot of optional configuration options. When a plugin takes many options, it is more
- maintainable to use a builder class rather than a factory method (see <i>Item 2: Consider a builder when
- faced with many constructor parameters</i> in <i>Effective Java</i> by Joshua Bloch). There are some other
- advantages to using an annotated builder class over an annotated factory method:
- </p>
- <ul>
- <li>Attribute names don't need to be specified if they match the field name.</li>
- <li>Default values can be specified in code rather than through an annotation (also allowing a
- runtime-calculated default value which isn't allowed in annotations).</li>
- <li>Adding new optional parameters doesn't require existing programmatic configuration to be refactored.</li>
- <li>Easier to write unit tests using builders rather than factory methods with optional parameters.</li>
- <li>Default values are specified via code rather than relying on reflection and injection, so they work
- programmatically as well as in a configuration file.</li>
- </ul>
- <p>
- Here is an example of a plugin factory from <code>ListAppender</code>:
- </p>
- <pre class="prettyprint linenums"><![CDATA[
-@PluginFactory
-public static ListAppender createAppender(
- @PluginAttribute("name") @Required(message = "No name provided for ListAppender") final String name,
- @PluginAttribute("entryPerNewLine") final boolean newLine,
- @PluginAttribute("raw") final boolean raw,
- @PluginElement("Layout") final Layout<? extends Serializable> layout,
- @PluginElement("Filter") final Filter filter) {
- return new ListAppender(name, filter, layout, newLine, raw);
-}]]></pre>
- <p>
- Here is that same factory using a builder pattern instead:
- </p>
- <pre class="prettyprint linenums"><![CDATA[
-@PluginBuilderFactory
-public static Builder newBuilder() {
- return new Builder();
-}
-
-public static class Builder implements org.apache.logging.log4j.core.util.Builder<ListAppender> {
-
- @PluginBuilderAttribute
- @Required(message = "No name provided for ListAppender")
- private String name;
-
- @PluginBuilderAttribute
- private boolean entryPerNewLine;
-
- @PluginBuilderAttribute
- private boolean raw;
-
- @PluginElement("Layout")
- private Layout<? extends Serializable> layout;
-
- @PluginElement("Filter")
- private Filter filter;
-
- public Builder setName(final String name) {
- this.name = name;
- return this;
- }
-
- public Builder setEntryPerNewLine(final boolean entryPerNewLine) {
- this.entryPerNewLine = entryPerNewLine;
- return this;
- }
-
- public Builder setRaw(final boolean raw) {
- this.raw = raw;
- return this;
- }
-
- public Builder setLayout(final Layout<? extends Serializable> layout) {
- this.layout = layout;
- return this;
- }
-
- public Builder setFilter(final Filter filter) {
- this.filter = filter;
- return this;
- }
-
- @Override
- public ListAppender build() {
- return new ListAppender(name, filter, layout, entryPerNewLine, raw);
- }
-}]]></pre>
- <p>
- The only difference in annotations is using <code>@PluginBuilderAttribute</code> instead of
- <code>@PluginAttribute</code> so that default values and reflection can be used instead of specifying
- them in the annotation. Either annotation can be used in a builder, but the former is better suited
- for field injection while the latter is better suited for parameter injection. Otherwise, the same
- annotations (<code>@PluginConfiguration</code>, <code>@PluginElement</code>, <code>@PluginNode</code>,
- and <code>@PluginValue</code>) are all supported on fields. Note that a factory method is still required
- to supply a builder, and this factory method should be annotated with <code>@PluginBuilderFactory</code>.
- <!-- TODO: this will change with LOG4J2-1188 -->
- </p>
- <p>
- When plugins are being constructed after a configuration has been parsed, a plugin builder will be used
- if available, otherwise a plugin factory method will be used as a fallback. If a plugin contains neither
- factory, then it cannot be used from a configuration file (it can still be used programmatically of
- course).
- </p>
- <p>
- Here is an example of using a plugin factory versus a plugin builder programmatically:
- </p>
- <pre class="prettyprint linenums"><![CDATA[
-ListAppender list1 = ListAppender.createAppender("List1", true, false, null, null);
-ListAppender list2 = ListAppender.newBuilder().setName("List1").setEntryPerNewLine(true).build();
-]]></pre>
- </subsection>
- <subsection name="Custom ContextDataInjector">
- <p>
- The <code>ContextDataInjector</code> (introduced in Log4j 2.7) is responsible for
- populating the LogEvent's
- <a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/LogEvent.html#getContextData()">context data</a>
- with key-value pairs or replacing it completely.
- The default implementation is ThreadContextDataInjector, which obtains context attributes from the ThreadContext.
- </p><p>
- Applications may replace the default ContextDataInjector by setting the value of the system property
- <tt>log4j2.contextDataInjector</tt> to the name of the custom ContextDataInjector class.
- </p><p>
- Implementors should be aware there are some subtleties related to thread-safety and implementing a
- context data injector in a garbage-free manner.
- See the <a class="javadoc" href="../log4j-core/apidocs/org/apache/logging/log4j/core/ContextDataInjector.html">ContextDataInjector</a>
- javadoc for detail.
- </p>
- </subsection>
- <subsection name="Custom ThreadContextMap implementations">
- <p>
- A garbage-free StringMap-based context map can be installed by setting system property <tt>log4j2.garbagefreeThreadContextMap</tt>
- to true. (Log4j must be <a href="garbagefree.html#Config">enabled</a> to use ThreadLocals.)
- </p><p>
- Any custom <tt>ThreadContextMap</tt> implementation can be installed by setting system property
- <tt>log4j2.threadContextMap</tt> to the fully qualified class name of the class implementing the
- ThreadContextMap interface. By also implementing the <tt>ReadOnlyThreadContextMap</tt> interface, your custom
- ThreadContextMap implementation will be accessible to applications via the
- <a href="../log4j-api/apidocs/org/apache/logging/log4j/ThreadContext.html#getThreadContextMap()">ThreadContext::getThreadContextMap</a>
- method.
- </p>
- </subsection>
- <subsection name="Custom Plugins">
- <p>See the <a href="plugins.html">Plugins</a> section of the manual.</p>
-<!-- TODO: some documentation here! -->
- </subsection>
- </section>
-
- </body>
-</document>