blob: fc7f00881dd24a6262089fa5d24d74c448dd20c5 [file] [log] [blame]
////
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.
////
= 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.
[#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]
=== `ConfigurationBuilder`
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]
----
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:
* 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]
=== `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`.
[#Configurator-initialize]
==== Obtaining a `LoggerContext`
You can use `Configurator` to obtain a `LoggerContext`:
.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`]
[source,java]
----
include::example$manual/customconfig/Usage.java[tag=useConfiguration,indent=0]
----
`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]
----
include::example$manual/customconfig/Usage.java[tag=reconfigureActiveLoggerContext,indent=0]
----
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`
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
* xref:manual/configuration.adoc#ConfigurationFactory[Create a custom `ConfigurationFactory` plugin]
* Assign it a higher priority (i.e., higher `@Order` value)
* Support all configuration file types (i.e. return `*` from `getSupportedTypes()`)
Consider the example below:
.Snippet from an example {antora-examples-url}/manual/customconfig/ExampleConfigurationFactory.java[`ExampleConfigurationFactory.java`]
[source,java]
----
include::example$manual/customconfig/ExampleConfigurationFactory.java[tag=class]
----
<1> `getConfiguration(LoggerContext, ConfigurationSource)` is only called if `ConfigurationSource` is not null.
This is possible if the `Configuration` is provided programmatically.
Hence, you are encouraged to implement `getConfiguration(LoggerContext, String, URI)` overload too.
[#guides]
== How-to guides
In this section we will share guides on programmatically configuring Log4j Core for certain use cases.
[#load-config-file]
=== Loading a configuration file
<<ConfigurationFactory>> provides the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#getInstance()[`getInstance()`] method returning a meta-`ConfigurationFactory` that combines the behaviour of all available ``ConfigurationFactory`` implementations, including xref:manual/configuration.adoc#configuration-factories[the predefined ones]; `XmlConfigurationFactory`, `JsonConfigurationFactory`, etc.
You can use this `getInstance()` method to load a configuration file programmatically, granted that the input file format is supported by at least one of the available `ConfigurationFactory` plugins:
.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`]
[source,java]
----
include::example$manual/customconfig/Usage.java[tag=loadConfigurationFile,indent=0]
----
<1> Passing the `LoggerContext` argument as null, since this is the first time we are instantiating this `Configuration`, and it is not associated with a `LoggerContext` yet
<2> Passing the configuration name argument as null, since it is not used when the configuration source location is provided
<3> URI pointing to the configuration file; `file://path/to/log4j2.xml`, `classpath:log4j2.xml`, etc.
[#CompositeConfiguration]
=== Combining multiple configurations
include::partial$manual/composite-configuration.adoc[tag=intro]
You can programmatically combine multiple configurations into a single one using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.html[`CompositeConfiguration`]:
.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`]
[source,java]
----
include::example$manual/customconfig/Usage.java[tag=combineConfigurations,indent=0]
----
<1> Loading a common, and an application-specific configuration from file
<2> Casting them to `AbstractConfiguration`, the type required by `CompositeConfiguration`
<3> Programmatically creating an uninitialized configuration.
Note that no casting is needed.
<4> Creating a `CompositeConfiguration` using all three configurations created.
Note that passed configuration order matters!
.How does `CompositeConfiguration` work?
[%collapsible]
====
include::partial$manual/composite-configuration.adoc[tag=how]
====
[#Hybrid]
=== [[AddingToCurrent]] [[AppendingToWritersAndOutputStreams]] Modifying configuration components
[WARNING]
====
*We strongly advise against programmatically modifying components of a configuration!*
This section will explain what it is, and why you should avoid it.
====
It is unfortunately common that users modify components (appenders, filters, etc.) of a configuration programmatically as follows:
[source,java]
----
LoggerContext context = LoggerContext.getContext(false);
Configuration config = context.getConfiguration();
PatternLayout layout = PatternLayout.createDefaultLayout(config);
Appender appender = createCustomAppender();
appender.start();
config.addAppender(appender);
updateLoggers(appender, config);
----
This approach is prone several problems:
* Your code relies on Log4j Core internals which don't have any backward compatibility guarantees.
You not only risk breaking your build at a minor Log4j Core version upgrade, but also make the life of Log4j maintainers trying to evolve the project extremely difficult.
* You move out from the safety zone, where Log4j Core takes care of components' life cycle (initialization, reconfiguration, etc.), and step into a minefield seriously undermining the reliability of your logging setup.
If you happen to have code programmatically modifying components of a configuration, we advise you to migrate to other declarative approaches shared in this page.
In case of need, feel free to ask for help in {logging-services-url}/support.html#discussions-user[user support channels].