| <?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 plugin builders, factories, and 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 locating all instances of |
| <code>META-INF/log4j-provider.properties</code>, a standard <code>java.util.Properties</code> file, |
| and then inspecting each to verify that it specifies a value for the <var>Log4jAPIVersion</var> property |
| that conforms to the version required by the <code>LogManager</code>. If more than one valid |
| implementation is located the value for <var>FactoryPriority</var> will be used to identify the factory |
| with the highest priority. Finally, the value of the <var>LoggerContextFactory</var> property will be |
| used to locate the <code>LoggerContextFactory</code>. 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>Implementing a new <code>LoggerContextFactory</code> and creating a <code>log4j-provider.properties</code> |
| to reference it making sure that it has the highest priority. |
| </li> |
| <li>Create a new <code>log4j-provider.xml</code> and configure it with the desired |
| <code>LoggerContextFactory</code> making sure that it has the highest priority. |
| </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 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. |
| </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", "*"}; |
| |
| /** |
| * Return the Configuration. |
| * @param source The InputSource. |
| * @return The Configuration. |
| */ |
| public Configuration getConfiguration(InputSource source) { |
| return new XMLConfiguration(source, configFile); |
| } |
| |
| /** |
| * 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> |
| </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="Custom Plugins"> |
| <p>See the <a href="plugins.html">Plugins</a> section of the manual.</p> |
| <!-- TODO: some documentation here! --> |
| </subsection> |
| </section> |
| |
| </body> |
| </document> |