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

    <body>
      <section name="Custom Configurations">
        <p>
          Log4j 2 provides a few ways for applications to create their own custom configurations:
        </p>
        <ul>
          <li>Specify a custom ConfigurationFactory</li>
          <li>Use the Configurator</li>
          <li>Modify the current Configuration after initialization</li>
        </ul>
        <a name="ConfigurationBuilder"/>
          <subsection name="The ConfigurationBuilder API">
            <p>
              Starting with release 2.4, Log4j provides a ConfigurationBuilder and a set of component builders that
              allow a Configuration to be created fairly easily.
              Actual configuration objects like LoggerConfig or Appender can be unwieldy; they require a lot
              of knowledge on Log4j internals which makes them difficult to work with if all you want is 
              create a Configuration.
            </p>
            <p>
              The new ConfigurationBuilder API (in the <code>org.apache.logging.log4j.core.config.builder.api</code> package)
              allows users to create Configurations in code by constructing component <i>definitions</i>.
              There is no need to work directly with actual configuration objects.
              Component definitions are added to the ConfigurationBuilder, and once all the definitions have
              been collected all the actual configuration objects (like Loggers and Appenders) are constructed.
            </p>
            <p>
              ConfigurationBuilder has convenience methods for the base components that can be configured such as
              Loggers, Appenders, Filter, Properties, etc. 
              However, Log4j 2's plugin mechanism means that users can create any number of custom components.
              As a trade-off, the ConfigurationBuilder API provides only a limited number of "strongly typed"
              convenience methods like <code>newLogger()</code>, <code>newLayout()</code> etc.
              The generic <code>builder.newComponent()</code> method can be used if no convenience method exists
              for the component you want to configure.
            </p>
            <p>
              For example, the builder does not know what components can be configured on specific components
              such as the RollingFileAppender vs. the RoutingAppender. To specify a triggering policy on a 
              RollingFileAppender you would use builder.newComponent().
            </p>
          </subsection>
          <a name="ConfigurationFactory"/>
          <subsection name="Understanding ConfigurationFactory">
            <p>
              Log4j 2 will search for available ConfigurationFactories and then select the one to use. The
              selected ConfigurationFactory creates the Configuration that Log4j will use. Here is how Log4j finds
              the available ConfigurationFactories:
            </p>
            <ol>
              <li>A system property named "log4j.configurationFactory" can be set with the name of the ConfigurationFactory to be used.</li>
              <li><code>ConfigurationFactory.setConfigurationFactory(ConfigurationFactory)</code> can be called with the instance of the
              ConfigurationFactory to be used. This must be called before any other calls to Log4j.</li>
              <li>A ConfigurationFactory implementation can be added to the classpath and configured as a plugin
                in the "ConfigurationFactory" category.
                The Order annotation can be used to specify the relative priority when multiple applicable 
                ConfigurationFactories are found.</li>
            </ol>
            <p>
              ConfigurationFactories have the concept of "supported types", which basically maps to the
              file extension of the configuration file that the ConfigurationFactory can handle. 
              If a configuration file location is specified, ConfigurationFactories whose supported type
              does not include "*" or the matching file extension will not be used.
            </p>
          </subsection>
          <a name="Example"/>
          <subsection name="Using ConfigurationBuilder with a Custom ConfigurationFactory">
            <p>
              One way to programmatically configure Log4j 2 is to create a custom ConfigurationFactory
              that uses the <a href="#ConfigurationBuilder">ConfigurationBuilder</a> to create a Configuration.
              The below example overrides the <code>getConfiguration()</code> method to return a
              Configuration created by the ConfigurationBuilder.
              This will cause the Configuration to automatically be hooked into Log4j when the LoggerContext is created.
              In the example below, because it specifies a supported type of "*" it will override any configuration files provided.
            </p>
            <pre class="prettyprint linenum"><![CDATA[
@Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
@Order(50)
public class CustomConfigurationFactory extends ConfigurationFactory {

    static Configuration createConfiguration(final String name, ConfigurationBuilder<BuiltConfiguration> builder) {
        builder.setConfigurationName(name);
        builder.setStatusLevel(Level.ERROR);
        builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL).
            addAttribute("level", Level.DEBUG));
        AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").
            addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
        appenderBuilder.add(builder.newLayout("PatternLayout").
            addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
        appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY,
            Filter.Result.NEUTRAL).addAttribute("marker", "FLOW"));
        builder.add(appenderBuilder);
        builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG).
            add(builder.newAppenderRef("Stdout")).
            addAttribute("additivity", false));
        builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout")));
        return builder.build();
    }

    @Override
    public Configuration getConfiguration(ConfigurationSource source) {
        return getConfiguration(source.toString(), null);
    }

    @Override
    public Configuration getConfiguration(final String name, final URI configLocation) {
        ConfigurationBuilder<BuiltConfiguration> builder = newConfigurationBuilder();
        return createConfiguration(name, builder);
    }

    @Override
    protected String[] getSupportedTypes() {
        return new String[] {"*"};
    }
}]]></pre>
          </subsection>
        <a name="Configurator"/>
          <subsection name="Using ConfigurationBuilder with the Configurator">
            <p>
              An alternative to a custom ConfigurationFactory is to configure with the <code>Configurator</code>.
              Once a Configuration object has been constructed, it can be passed to one of the 
              <code>Configurator.initialize</code> methods to set up the Log4j configuration.
            </p>
            <p>
              Using the Configurator in this manner allows the application control over when Log4j is initialized.
              However, should any logging be attempted before Configurator.initialize() is called then the
              default configuration will be used for those log events.
            </p>
            <pre class="prettyprint linenum"><![CDATA[
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.ERROR);
builder.setConfigurationName("BuilderTest");
builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL)
    .addAttribute("level", Level.DEBUG));
AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target",
    ConsoleAppender.Target.SYSTEM_OUT);
appenderBuilder.add(builder.newLayout("PatternLayout").
    addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable"));
appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL).
    addAttribute("marker", "FLOW"));
builder.add(appenderBuilder);
builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG).
    add(builder.newAppenderRef("Stdout")).addAttribute("additivity", false));
builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout")));
ctx = Configurator.initialize(builder.build());
]]></pre>
        </subsection>

          <a name="Hybrid"/>
          <subsection name="Combining Configuration File with Programmatic Configuration">
            <p>
              Sometimes you want to configure with a configuration file but do some additional programmatic
              configuration. A possible use case might be that you want to allow for a flexible configuration using XML 
              but at the same time make sure there are a few configuration elements that are always present that can't be removed.
            </p>
            <p>
              The easiest way to achieve this is to extend one of the standard Configuration classes
              (XMLConfiguration, JSONConfiguration) and then create a new ConfigurationFactory for the extended class.
              After the standard configuration completes the custom configuration can be added to it.
            </p>
            <p>
              The example below shows how to extend XMLConfiguration to manually add an Appender and a LoggerConfig
              to the configuration.
            </p>
            <pre class="prettyprint linenums">
@Plugin(name = "MyXMLConfigurationFactory", category = "ConfigurationFactory")
@Order(10)
public class MyXMLConfigurationFactory 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 MyXMLConfiguration(source, configFile);
    }

    /**
     * Returns the file suffixes for XML files.
     * @return An array of File extensions.
     */
    public String[] getSupportedTypes() {
        return SUFFIXES;
    }
}

public class MyXMLConfiguration extends XMLConfiguration {
    public MyXMLConfiguration(final ConfigurationFactory.ConfigurationSource configSource) {
      super(configSource);
    }

    @Override
    protected void doConfigure() {
        super.doConfigure();
        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        final Layout layout = PatternLayout.createLayout(PatternLayout.SIMPLE_CONVERSION_PATTERN, config, null,
              null,null, null);
        final Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true",
              "false", "false", "4000", layout, null, "false", null, config);
        appender.start();
        addAppender(appender);
        LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j",
              "true", refs, null, config, null );
        loggerConfig.addAppender(appender, null, null);
        addLogger("org.apache.logging.log4j", loggerConfig);
    }
}</pre>
          </subsection>
        <a name="AddingToCurrent"/>
        <subsection name="Programmatically Adding to the Current Configuration">
            <p>
              Applications sometimes have the need to customize logging separate from the actual configuration.
              Log4j allows this although it suffers from a few limitations:
            </p>
              <ol>
                <li>If the configuration file is changed the configuration will be reloaded and the manual changes
                will be lost.</li>
                <li>Modification to the running configuration requires that all the methods being called (addAppender
                  and addLogger) be synchronized.</li>
              </ol>
            <p>
              As such, the recommended approach for customizing a configuration is to extend one of the standard
              Configuration classes, override the setup method to first do super.setup() and then add the custom
              Appenders, Filters and LoggerConfigs to the configuration before it is registered for use.
            </p>
            <p>
              The following example adds an Appender and a new LoggerConfig using that Appender to the current
              configuration.
            </p>
            <!-- TODO: update code example below with new plugin API -->
            <pre class="prettyprint linenums"><![CDATA[
        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
        final Configuration config = ctx.getConfiguration();
        Layout layout = PatternLayout.createLayout(PatternLayout.SIMPLE_CONVERSION_PATTERN, config, null,
            null,null, null);
        Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true",
            "false", "false", "4000", layout, null, "false", null, config);
        appender.start();
        config.addAppender(appender);
        AppenderRef ref = AppenderRef.createAppenderRef("File", null, null);
        AppenderRef[] refs = new AppenderRef[] {ref};
        LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j",
            "true", refs, null, config, null );
        loggerConfig.addAppender(appender, null, null);
        config.addLogger("org.apache.logging.log4j", loggerConfig);
        ctx.updateLoggers();
}]]></pre>
          </subsection>

      </section>

    </body>
</document>
