<?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>
    <properties>
        <title>Extending Log4j 2</title>
        <author email="rgoers@apache.org">Ralph Goers</author>
    </properties>

    <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 LoggerContextFactory binds the Log4j API to its implementation. The Log4j LogManager
              locates a LoggerContextFactory by locating all instances of META-INF/log4j-provider.xml, a
              file that conforms to the java.util.Properties DTD, and then inspecting each to verify that it
              specifies a value for the "Log4jAPIVersion" property that conforms to the version required by the
              LogManager. If more than one valid implementation is located the value for "FactoryPriority" will
              be used to identify the factory with the highest priority.
              Finally, the value of the "LoggerContextFactory" property will be used to locate the
              LoggerContextFactory. In Log4j 2 this is provided by Log4jContextFactory.
            </p>
            <p>
              Applications may change the LoggerContextFactory that will be used by
              <ol>
                <li>Implementing a new LoggerContextFactory and creating a log4j-provider.xml to reference it making
                  sure that it has the highest priority.</li>
                <li>Create a new log4j-provider.xml and configure it with the desired LoggerContextFactory making
                  sure that it has the highest priority.</li>
                <li>Setting the system property "log4j2.LoggerContextFactory" to the name of the LoggerContextFactory
                  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>
            </p>
          </subsection>
          <subsection name="ContextSelector">
            <p>
              ContextSelectors are called by the 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.
            </p>
            <p>
              Log4j provides three ContextSelectors:
              <dl>
                <dt>BasicContextSelector</dt>
                <dd>Uses either a LoggerContext that has been stored in a ThreadLocal or a common LoggerContext.</dd>
                <dt>ClassLoaderContextSelector</dt>
                <dd>Associates LoggerContexts with the ClassLoader that created the caller of the getLogger call.</dd>
                <dt>JNDIContextSelector</dt>
                <dd>Locates the LoggerContext by querying JNDI.</dd>
              </dl>
            </p>
          </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(@PluginAttr("additivity") String additivity,
                                            @PluginAttr("level") String loggerLevel,
                                            @PluginElement("AppenderRef") AppenderRef[] refs,
                                            @PluginElement("Filters") Filter filter) {
        List<AppenderRef> appenderRefs = Arrays.asList(refs);
        Level level;
        try {
            level = loggerLevel == null ? Level.ERROR : Level.valueOf(loggerLevel.toUpperCase());
        } catch (Exception ex) {
            LOGGER.error("Invalid Log level specified: {}. Defaulting to Error", loggerLevel);
            level = Level.ERROR;
        }
        boolean additive = additivity == null ? true : Boolean.parseBoolean(additivity);

        return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additive);
    }
}]]></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>
            <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(@PluginAttr("level") String loggerLevel,
                                               @PluginAttr("onMatch") String match,
                                               @PluginAttr("onMismatch") String mismatch) {
        Level level = loggerLevel == null ? Level.ERROR : Level.toLevel(loggerLevel.toUpperCase());
        Result onMatch = match == null ? Result.NEUTRAL : Result.valueOf(match.toUpperCase());
        Result onMismatch = mismatch == null ? Result.DENY : Result.valueOf(mismatch.toUpperCase());

        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. Appender's 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(@PluginAttr("name") String name,
                                              @PluginAttr("ignoreExceptions") String ignore,
                                              @PluginElement("Layout") Layout layout,
                                              @PluginElement("Filters") Filter filter) {

        boolean ignoreExceptions = Boolean.parseBoolean(ignore);

        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.createLayout(null, null, null, null);
        }
        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(@PluginAttr("locationInfo") String locationInfo,
                                            @PluginAttr("properties") String properties,
                                            @PluginAttr("complete") String complete,
                                            @PluginAttr("charset") String charset) {
        Charset c = Charset.isSupported("UTF-8") ?
                    Charset.forName("UTF-8") : Charset.defaultCharset();
        if (charset != null) {
            if (Charset.isSupported(charset)) {
                c = Charset.forName(charset);
            } else {
                LOGGER.error("Charset " + charset + " is not supported for layout, using " +
                             c.displayName());
            }
        }
        boolean info = locationInfo == null ? false : Boolean.valueOf(locationInfo);
        boolean props = properties == null ? false : Boolean.valueOf(properties);
        boolean comp = complete == null ? false : Boolean.valueOf(complete);
        return new SampleLayout(info, props, comp, c);
    }
}</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">

          </subsection>
      </section>

    </body>
</document>
