| //// |
| 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]. |