vpelikh commented on code in PR #4230:
URL: https://github.com/apache/logging-log4j2/pull/4230#discussion_r4056572877
##########
src/site/antora/modules/ROOT/pages/manual/customconfig.adoc:
##########
@@ -14,372 +14,236 @@
See the License for the specific language governing permissions and
limitations under the License.
////
-= Programmatic Configuration
+= Programmatic configuration
-Log4j 2 provides a few ways for applications to create their own
-programmatic configuration:
+Next to xref:manual/configuration.adoc[configuration files], Log4j Core can be
configured programmatically too.
+In this page, we will explore utilities helping with programmatic
configuration and demonstrate how they can be leveraged for certain use cases.
-* Specify a custom `ConfigurationFactory` to start Log4j with a
-programmatic configuration
-* Use the `Configurator` to replace the configuration after Log4j started
-* Initialize Log4j with a combination of a configuration file and
-programmatic configuration
-* Modify the current `Configuration` after initialization
+[#prelim]
+== Preliminaries
+
+To begin with, we strongly encourage you to check out the
xref:manual/architecture.adoc[] page first.
+Let's repeat some basic definitions of particular interest:
+
+xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]::
+It is the anchor of the logging system.
+Generally there is one, statically-accessible, global `LoggerContext` for most
applications.
+But there can be multiple ``LoggerContext``s, for instance, to use in tests,
in Java EE web applications, etc.
+
+xref:manual/architecture.adoc#Configuration[`Configuration`]::
+It encapsulates a Log4j Core configuration (properties, appenders, loggers,
etc.) and is associated with a `LoggerContext`.
+
+[#tooling]
+== Tooling
+
+For programmatic configuration, Log4j Core essentially provides the following
tooling:
+
+<<ConfigurationBuilder>>:: for declaratively creating a `Configuration`
+
+<<Configurator>>:: for associating a `Configuration` with a `LoggerContext`
+
+<<ConfigurationFactory>>:: for registering a `Configuration` factory to
xref:manual/configuration.adoc[the configuration file mechanism]
+
+In short, we will create ``Configuration``s using `ConfigurationBuilder`, and
activate them using `Configurator`.
[#ConfigurationBuilder]
-== The ConfigurationBuilder API
-
-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 about Log4j
-internals which makes them difficult to work with if all you want is to
-create a `Configuration`.
-
-The new `ConfigurationBuilder` API (in the
-`org.apache.logging.log4j.core.config.builder.api` package) allows users
-to create Configurations in code by constructing component
-_definitions_. 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.
-
-`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 `newLogger()`, `newLayout()` etc. The generic
-`builder.newComponent()` method can be used if no convenience method
-exists for the component you want to configure.
-
-For example, the builder does not know what sub-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().
-
-Examples of using the `ConfigurationBuilder` API are in the sections that
-follow.
+=== `ConfigurationBuilder`
-[#ConfigurationFactory]
-== Understanding ConfigurationFactory
-
-During initialization, Log4j 2 will search for available
-xref:manual/extending.adoc#ConfigurationFactory[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:
-
-1. A system property named `log4j2.configurationFactory` can be set
-with the name of the ConfigurationFactory to be used.
-2. `ConfigurationFactory.setConfigurationFactory(ConfigurationFactory)`
-can be called with the instance of the `ConfigurationFactory` to be used.
-This must be called before any other calls to Log4j.
-3. 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.
-
-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.
-
-[#Example]
-== Initialize Log4j Using ConfigurationBuilder with a Custom
ConfigurationFactory
-
-One way to programmatically configure Log4j 2 is to create a custom
-`ConfigurationFactory` that uses the
-<<ConfigurationBuilder,`ConfigurationBuilder`>> to create a
-Configuration. The below example overrides the `getConfiguration()`
-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.
+link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html[`ConfigurationBuilder`]
interface models a fluent API to programmatically create
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]s.
+If you have ever created a xref:manual/configuration.adoc[Log4j Core
configuration file], consider `ConfigurationBuilder` as a convenience utility
to model the very same declarative configuration structure programmatically.
+
+Let's show `ConfigurationBuilder` usage with an example.
+Consider the following Log4j Core configuration file:
+
+[tabs]
+====
+XML::
++
+.Snippet from an example
{antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.xml[`log4j2.xml`]
+[source,xml]
+----
+include::example$manual/customconfig/ConfigurationBuilder/log4j2.xml[lines=24..34,indent=0]
+----
+
+JSON::
++
+.Snippet from an example
{antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.json[`log4j2.json`]
+[source,json]
+----
+include::example$manual/customconfig/ConfigurationBuilder/log4j2.json[lines=3..16,indent=0]
+----
+
+YAML::
++
+.Snippet from an example
{antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.yaml[`log4j2.yaml`]
+[source,yaml]
+----
+include::example$manual/customconfig/ConfigurationBuilder/log4j2.yaml[lines=19..-1,indent=0]
+----
+Properties::
++
+.Snippet from an example
{antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.properties[`log4j2.properties`]
+[source,properties]
+----
+include::example$manual/customconfig/ConfigurationBuilder/log4j2.properties[lines=17..-1]
+----
+====
+
+Above Log4j Core configuration can be programmatically built using
`ConfigurationBuilder` as follows:
+
+.Snippet from an example
{antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`]
[source,java]
----
-@Namespace(ConfigurationFactory.NAMESPACE)
-@Plugin
-@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(final LoggerContext loggerContext,
final ConfigurationSource source) {
- return getConfiguration(loggerContext, source.toString(), null);
- }
-
- @Override
- public Configuration getConfiguration(final LoggerContext loggerContext,
final String name, final URI configLocation) {
- ConfigurationBuilder<BuiltConfiguration> builder =
newConfigurationBuilder();
- return createConfiguration(name, builder);
- }
-
- @Override
- protected String[] getSupportedTypes() {
- return new String[] {"*"};
- }
-}
+include::example$manual/customconfig/Usage.java[tag=createConfiguration,indent=0]
----
+<1> The default `ConfigurationBuilder` instance is obtained using
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.html#newConfigurationBuilder()[`ConfigurationBuilderFactory.newConfigurationBuilder()`]
static method
+<2> Add the appender along with the layout
+<3> Add the root logger along with a level and appender reference
+<4> Create the configuration, but *don't initialize* it
++
+[TIP]
+====
+It is a good practice to not initialize ``Configuration``s when they are
constructed.
+This task should ideally be delegated to <<Configurator>>.
+====
+
+`ConfigurationBuilder` has convenience methods for the base components that
can be configured such as loggers, appenders, filters, properties, etc.
+Though there are cases where the provided convenience methods fall short of:
-As of version 2.7, the `ConfigurationFactory.getConfiguration()` methods
-take an additional `LoggerContext` parameter.
+* Custom xref:manual/plugins.adoc#core[plugins that are declared to be
represented in a configuration]
+* Custom subcomponents (e.g., a
xref:manual/appenders.adoc#TriggeringPolicies[triggering policy] for
xref:manual/appenders.adoc#RollingFileAppender[`RollingFileAppender`])
+
+For those, you can use the generic
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html#newComponent()[`ConfigurationBuilder#newComponent()`]
method.
+
+See
{project-github-url}/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java[`Configurator1Test.java`]
for examples on `ConfigurationBuilder`, `newComponent()`, etc. usage.
[#Configurator]
-== Reconfigure Log4j Using ConfigurationBuilder with the Configurator
+=== `Configurator`
+
+link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configurator.html[`Configurator`]
is a programmatic interface to associate a ``Configuration`` with either new,
or an existing `LoggerContext`.
-An alternative to a custom `ConfigurationFactory` is to configure with the
-`Configurator`. Once a `Configuration` object has been constructed, it can
-be passed to one of the `Configurator.initialize` methods to set up the
-Log4j configuration.
+[#Configurator-initialize]
+==== Obtaining a `LoggerContext`
-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.
+You can use `Configurator` to obtain a `LoggerContext`:
+.Snippet from an example
{antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`]
[source,java]
----
-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());
+include::example$manual/customconfig/Usage.java[tag=useConfiguration,indent=0]
----
-This example shows how to create a configuration that includes a
-RollingFileAppender.
+`initialize()` will either return the `LoggerContext` currently associated
with the caller, or create a new one.
+This is a convenient way to create isolated ``LoggerContext``s for tests, etc.
+
+[#Configurator-reconfigure]
+==== Reconfiguring the active `LoggerContext`
+
+You can use `Configurator` to reconfigure the active `LoggerContext` as
follows:
+.Snippet from an example
{antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`]
[source,java]
----
-ConfigurationBuilder<BuiltConfiguration> builder =
ConfigurationBuilderFactory.newConfigurationBuilder();
-
-builder.setStatusLevel(Level.ERROR);
-builder.setConfigurationName("RollingBuilder");
-// create a console appender
-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"));
-builder.add(appenderBuilder);
-// create a rolling file appender
-LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout")
- .addAttribute("pattern", "%d [%t] %-5level: %msg%n");
-ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
-
.addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule",
"0 0 0 * * ?"))
-
.addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size",
"100M"));
-appenderBuilder = builder.newAppender("rolling", "RollingFile")
- .addAttribute("fileName", "target/rolling.log")
- .addAttribute("filePattern", "target/archive/rolling-%d{MM-dd-yy}.log.gz")
- .add(layoutBuilder)
- .addComponent(triggeringPolicy);
-builder.add(appenderBuilder);
-
-// create the new logger
-builder.add(builder.newLogger("TestLogger", Level.DEBUG)
- .add(builder.newAppenderRef("rolling"))
- .addAttribute("additivity", false));
-
-builder.add(builder.newRootLogger(Level.DEBUG)
- .add(builder.newAppenderRef("rolling")));
-LoggerContext ctx = Configurator.initialize(builder.build());
+include::example$manual/customconfig/Usage.java[tag=reconfigureActiveLoggerContext,indent=0]
----
-[#Hybrid]
-== Initialize Log4j by Combining Configuration File with Programmatic
Configuration
+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.
+
+[#ConfigurationFactory]
+=== [[Example]] `ConfigurationFactory`
-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.
+link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`]
interface, which is mainly used by
xref:manual/configuration.adoc#automatic-configuration[the configuration file
mechanism] to load a
link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`],
can be leveraged to inject a custom `Configuration`.
+You need to
-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.
+* xref:manual/configuration.adoc#ConfigurationFactory[Create a custom
`ConfigurationFactory` plugin]
+* Assign it a higher priority (i.e., higher `@Ordered` value)
Review Comment:
Addressed: `customconfig.adoc` now uses `@Ordered`; the remaining 'unswept
@Order' on Yaml/JavaProps/OSGi factories were swept to `@Ordered`; the
per-anchor xrefs were validated by the website build (no new broken xrefs);
added the `#2696` changelog entry.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]