Skip to content

Commit b12fda2

Browse files
committed
Merge pull request for #32578 from rgoers
* pr/32578: Document Log4j2 extensions * Merge pull request for #32735: Polish 'Resolve URLs using Log4J2 mechanisms' Resolve URLs using Log4j2 mechanisms * Merge pull request for #32734: Polish 'Support profile specific Log4j2 configuration' Support profile specific Log4j2 configuration * Merge pull request for #32733: Polish 'Add Log4J2 PropertySource backed by the Spring Environment' Add Log4J2 PropertySource backed by the Spring Environment * Merge pull request for #32732: Polish 'Support Log4J2 string lookups from the Spring Environment' Support Log4J2 string lookups from the Spring Environment * Merge pull request for #32730: Polish 'Add Spring Environment to LoggerContext' Add Spring Environment to LoggerContext * Merge pull request for #32730: Polish 'Support 'log4j.configurationFile' system property' Support 'log4j.configurationFile' system property Closes gh-32578
2 parents ec17b78 + 029aab6 commit b12fda2

File tree

13 files changed

+804
-50
lines changed

13 files changed

+804
-50
lines changed

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/logging.adoc

+76-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[[features.logging]]
22
== Logging
33
Spring Boot uses https://commons.apache.org/logging[Commons Logging] for all internal logging but leaves the underlying log implementation open.
4-
Default configurations are provided for {java-api}/java/util/logging/package-summary.html[Java Util Logging], https://logging.apache.org/log4j/2.x/[Log4J2], and https://logback.qos.ch/[Logback].
4+
Default configurations are provided for {java-api}/java/util/logging/package-summary.html[Java Util Logging], https://logging.apache.org/log4j/2.x/[Log4j2], and https://logback.qos.ch/[Logback].
55
In each case, loggers are pre-configured to use console output with optional file output also available.
66

77
By default, if you use the "`Starters`", Logback is used for logging.
@@ -158,7 +158,7 @@ As a result, specific configuration keys (such as `logback.configurationFile` fo
158158
[[features.logging.file-rotation]]
159159
=== File Rotation
160160
If you are using the Logback, it is possible to fine-tune log rotation settings using your `application.properties` or `application.yaml` file.
161-
For all other logging system, you will need to configure rotation settings directly yourself (for example, if you use Log4J2 then you could add a `log4j2.xml` or `log4j2-spring.xml` file).
161+
For all other logging system, you will need to configure rotation settings directly yourself (for example, if you use Log4j2 then you could add a `log4j2.xml` or `log4j2-spring.xml` file).
162162

163163
The following rotation policy properties are supported:
164164

@@ -433,7 +433,7 @@ Profile sections are supported anywhere within the `<configuration>` element.
433433
Use the `name` attribute to specify which profile accepts the configuration.
434434
The `<springProfile>` tag can contain a profile name (for example `staging`) or a profile expression.
435435
A profile expression allows for more complicated profile logic to be expressed, for example `production & (eu-central | eu-west)`.
436-
Check the {spring-framework-docs}/core.html#beans-definition-profiles-java[reference guide] for more details.
436+
Check the {spring-framework-docs}/core.html#beans-definition-profiles-java[Spring Framework reference guide] for more details.
437437
The following listing shows three sample profiles:
438438

439439
[source,xml,subs="verbatim",indent=0]
@@ -475,3 +475,76 @@ The following example shows how to expose properties for use within Logback:
475475

476476
NOTE: The `source` must be specified in kebab case (such as `my.property-name`).
477477
However, properties can be added to the `Environment` by using the relaxed rules.
478+
479+
480+
481+
[[features.logging.log4j2-extensions]]
482+
=== Log4j2 Extensions
483+
Spring Boot includes a number of extensions to Log4j2 that can help with advanced configuration.
484+
You can use these extensions in any `log4j2-spring.xml` configuration file.
485+
486+
NOTE: Because the standard `log4j2.xml` configuration file is loaded too early, you cannot use extensions in it.
487+
You need to either use `log4j2-spring.xml` or define a configprop:logging.config[] property.
488+
489+
NOTE: The extensions supersede the https://logging.apache.org/log4j/2.x/log4j-spring-boot/index.html[Spring Boot support] provided by Log4J.
490+
You should make sure not include the `org.apache.logging.log4j:log4j-spring-boot` module in your build.
491+
492+
493+
494+
[[features.logging.log4j2-extensions.profile-specific]]
495+
==== Profile-specific Configuration
496+
The `<SpringProfile>` tag lets you optionally include or exclude sections of configuration based on the active Spring profiles.
497+
Profile sections are supported anywhere within the `<Configuration>` element.
498+
Use the `name` attribute to specify which profile accepts the configuration.
499+
The `<SpringProfile>` tag can contain a profile name (for example `staging`) or a profile expression.
500+
A profile expression allows for more complicated profile logic to be expressed, for example `production & (eu-central | eu-west)`.
501+
Check the {spring-framework-docs}/core.html#beans-definition-profiles-java[Spring Framework reference guide] for more details.
502+
The following listing shows three sample profiles:
503+
504+
[source,xml,subs="verbatim",indent=0]
505+
----
506+
<SpringProfile name="staging">
507+
<!-- configuration to be enabled when the "staging" profile is active -->
508+
</SpringProfile>
509+
510+
<SpringProfile name="dev | staging">
511+
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
512+
</SpringProfile>
513+
514+
<SpringProfile name="!production">
515+
<!-- configuration to be enabled when the "production" profile is not active -->
516+
</SpringProfile>
517+
----
518+
519+
520+
521+
[[features.logging.log4j2-extensions.environment-properties-lookup]]
522+
==== Environment Properties Lookup
523+
If you want to refer to properties from your Spring `Environment` within your Log4j2 configuration you can use `spring:` prefixed https://logging.apache.org/log4j/2.x/manual/lookups.html[lookups].
524+
Doing so can be useful if you want to access values from your `application.properties` file in your Log4j2 configuration.
525+
526+
The following example shows how to set a Log4j2 property named `applicationName` that reads `spring.application.name` from the Spring `Environment`:
527+
528+
[source,xml,subs="verbatim",indent=0]
529+
----
530+
<Properties>
531+
<Property name="applicationName">${spring:spring.application.name}</property>
532+
</Properties>
533+
----
534+
535+
NOTE: The lookup key should be specified in kebab case (such as `my.property-name`).
536+
537+
538+
539+
[[features.logging.log4j2-extensions.environment-peroperty-source]]
540+
==== Log4j2 System Properties
541+
Log4j2 supports a number of https://logging.apache.org/log4j/2.x/manual/configuration.html#SystemProperties[System Properties] that can be used configure various items.
542+
For example, the `log4j2.skipJansi` system property can be used to configure if the `ConsoleAppender` will try to use a https://github.com/fusesource/jansi[Jansi] output stream on Windows.
543+
544+
All system properties that are loaded after the Log4J initialization can be obtained from the Spring `Environment`.
545+
For example, you could add `log4j2.skipJansi=false` to your `application.properties` file to have the `ConsoleAppender` use a Jansi on Windows.
546+
547+
NOTE: The Spring `Environment` is only considered when system properties and OS environment variables do not contain the value being loaded.
548+
549+
WARNING: System properties that are loaded during early Log4j2 initialization cannot reference the Spring `Environment`.
550+
For example, the property Log4j2 uses to allow the default Log4j2 implementation to be chosen is used before the Spring Environment is available.

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java

+52-22
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
package org.springframework.boot.logging.log4j2;
1818

1919
import java.io.IOException;
20-
import java.io.InputStream;
2120
import java.net.URL;
21+
import java.net.URLConnection;
2222
import java.util.ArrayList;
2323
import java.util.Collections;
2424
import java.util.LinkedHashMap;
2525
import java.util.List;
2626
import java.util.Map;
27+
import java.util.Properties;
2728
import java.util.Set;
2829
import java.util.logging.ConsoleHandler;
2930
import java.util.logging.Handler;
@@ -42,9 +43,14 @@
4243
import org.apache.logging.log4j.core.config.LoggerConfig;
4344
import org.apache.logging.log4j.core.config.composite.CompositeConfiguration;
4445
import org.apache.logging.log4j.core.filter.AbstractFilter;
46+
import org.apache.logging.log4j.core.net.UrlConnectionFactory;
47+
import org.apache.logging.log4j.core.net.ssl.SslConfiguration;
48+
import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory;
49+
import org.apache.logging.log4j.core.util.AuthorizationProvider;
4550
import org.apache.logging.log4j.core.util.NameUtil;
4651
import org.apache.logging.log4j.jul.Log4jBridgeHandler;
4752
import org.apache.logging.log4j.message.Message;
53+
import org.apache.logging.log4j.util.PropertiesUtil;
4854

4955
import org.springframework.boot.context.properties.bind.BindResult;
5056
import org.springframework.boot.context.properties.bind.Bindable;
@@ -56,8 +62,10 @@
5662
import org.springframework.boot.logging.LoggingInitializationContext;
5763
import org.springframework.boot.logging.LoggingSystem;
5864
import org.springframework.boot.logging.LoggingSystemFactory;
65+
import org.springframework.core.Conventions;
5966
import org.springframework.core.Ordered;
6067
import org.springframework.core.annotation.Order;
68+
import org.springframework.core.env.Environment;
6169
import org.springframework.util.Assert;
6270
import org.springframework.util.ClassUtils;
6371
import org.springframework.util.CollectionUtils;
@@ -71,6 +79,7 @@
7179
* @author Andy Wilkinson
7280
* @author Alexander Heusingfeld
7381
* @author Ben Hale
82+
* @author Ralph Goers
7483
* @since 1.2.0
7584
*/
7685
public class Log4J2LoggingSystem extends AbstractLoggingSystem {
@@ -81,6 +90,9 @@ public class Log4J2LoggingSystem extends AbstractLoggingSystem {
8190

8291
private static final String LOG4J_LOG_MANAGER = "org.apache.logging.log4j.jul.LogManager";
8392

93+
static final String ENVIRONMENT_KEY = Conventions.getQualifiedAttributeName(Log4J2LoggingSystem.class,
94+
"environment");
95+
8496
private static final LogLevels<Level> LEVELS = new LogLevels<>();
8597

8698
static {
@@ -123,32 +135,29 @@ public Log4J2LoggingSystem(ClassLoader classLoader) {
123135

124136
@Override
125137
protected String[] getStandardConfigLocations() {
126-
return getCurrentlySupportedConfigLocations();
127-
}
128-
129-
private String[] getCurrentlySupportedConfigLocations() {
130-
List<String> supportedConfigLocations = new ArrayList<>();
131-
addTestFiles(supportedConfigLocations);
132-
supportedConfigLocations.add("log4j2.properties");
138+
List<String> locations = new ArrayList<>();
139+
locations.add("log4j2-test.properties");
133140
if (isClassAvailable("com.fasterxml.jackson.dataformat.yaml.YAMLParser")) {
134-
Collections.addAll(supportedConfigLocations, "log4j2.yaml", "log4j2.yml");
141+
Collections.addAll(locations, "log4j2-test.yaml", "log4j2-test.yml");
135142
}
136143
if (isClassAvailable("com.fasterxml.jackson.databind.ObjectMapper")) {
137-
Collections.addAll(supportedConfigLocations, "log4j2.json", "log4j2.jsn");
144+
Collections.addAll(locations, "log4j2-test.json", "log4j2-test.jsn");
138145
}
139-
supportedConfigLocations.add("log4j2.xml");
140-
return StringUtils.toStringArray(supportedConfigLocations);
141-
}
142-
143-
private void addTestFiles(List<String> supportedConfigLocations) {
144-
supportedConfigLocations.add("log4j2-test.properties");
146+
locations.add("log4j2-test.xml");
147+
locations.add("log4j2.properties");
145148
if (isClassAvailable("com.fasterxml.jackson.dataformat.yaml.YAMLParser")) {
146-
Collections.addAll(supportedConfigLocations, "log4j2-test.yaml", "log4j2-test.yml");
149+
Collections.addAll(locations, "log4j2.yaml", "log4j2.yml");
147150
}
148151
if (isClassAvailable("com.fasterxml.jackson.databind.ObjectMapper")) {
149-
Collections.addAll(supportedConfigLocations, "log4j2-test.json", "log4j2-test.jsn");
152+
Collections.addAll(locations, "log4j2.json", "log4j2.jsn");
153+
}
154+
locations.add("log4j2.xml");
155+
String propertyDefinedLocation = new PropertiesUtil(new Properties())
156+
.getStringProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY);
157+
if (propertyDefinedLocation != null) {
158+
locations.add(propertyDefinedLocation);
150159
}
151-
supportedConfigLocations.add("log4j2-test.xml");
160+
return StringUtils.toStringArray(locations);
152161
}
153162

154163
protected boolean isClassAvailable(String className) {
@@ -227,6 +236,11 @@ public void initialize(LoggingInitializationContext initializationContext, Strin
227236
if (isAlreadyInitialized(loggerContext)) {
228237
return;
229238
}
239+
Environment environment = initializationContext.getEnvironment();
240+
if (environment != null) {
241+
getLoggerContext().putObjectIfAbsent(ENVIRONMENT_KEY, environment);
242+
PropertiesUtil.getProperties().addPropertySource(new SpringEnvironmentPropertySource(environment));
243+
}
230244
loggerContext.getConfiguration().removeFilter(FILTER);
231245
super.initialize(initializationContext, configLocation, logFile);
232246
markAsInitialized(loggerContext);
@@ -290,11 +304,16 @@ private Configuration load(String location, LoggerContext context) throws IOExce
290304
}
291305

292306
private ConfigurationSource getConfigurationSource(URL url) throws IOException {
293-
InputStream stream = url.openStream();
294307
if (FILE_PROTOCOL.equals(url.getProtocol())) {
295-
return new ConfigurationSource(stream, ResourceUtils.getFile(url));
308+
return new ConfigurationSource(url.openStream(), ResourceUtils.getFile(url));
296309
}
297-
return new ConfigurationSource(stream, url);
310+
AuthorizationProvider authorizationProvider = ConfigurationFactory
311+
.authorizationProvider(PropertiesUtil.getProperties());
312+
SslConfiguration sslConfiguration = url.getProtocol().equals("https")
313+
? SslConfigurationFactory.getSslConfiguration() : null;
314+
URLConnection connection = UrlConnectionFactory.createConnection(url, 0, sslConfiguration,
315+
authorizationProvider);
316+
return new ConfigurationSource(connection.getInputStream(), url, connection.getLastModified());
298317
}
299318

300319
private CompositeConfiguration createComposite(List<Configuration> configurations) {
@@ -467,6 +486,17 @@ private void markAsUninitialized(LoggerContext loggerContext) {
467486
loggerContext.setExternalContext(null);
468487
}
469488

489+
/**
490+
* Get the Spring {@link Environment} attached to the given {@link LoggerContext} or
491+
* {@code null} if no environment is available.
492+
* @param loggerContext the logger context
493+
* @return the Spring {@link Environment} or {@code null}
494+
* @since 3.0.0
495+
*/
496+
public static Environment getEnvironment(LoggerContext loggerContext) {
497+
return (Environment) ((loggerContext != null) ? loggerContext.getObject(ENVIRONMENT_KEY) : null);
498+
}
499+
470500
/**
471501
* {@link LoggingSystemFactory} that returns {@link Log4J2LoggingSystem} if possible.
472502
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.logging.log4j2;
18+
19+
import org.apache.logging.log4j.core.LogEvent;
20+
import org.apache.logging.log4j.core.LoggerContext;
21+
import org.apache.logging.log4j.core.config.LoggerContextAware;
22+
import org.apache.logging.log4j.core.config.plugins.Plugin;
23+
import org.apache.logging.log4j.core.lookup.StrLookup;
24+
25+
import org.springframework.core.env.Environment;
26+
import org.springframework.util.Assert;
27+
28+
/**
29+
* Lookup for Spring properties.
30+
*
31+
* @author Ralph Goers
32+
* @author Phillip Webb
33+
*/
34+
@Plugin(name = "spring", category = StrLookup.CATEGORY)
35+
class SpringEnvironmentLookup implements LoggerContextAware, StrLookup {
36+
37+
private volatile Environment environment;
38+
39+
@Override
40+
public String lookup(LogEvent event, String key) {
41+
return lookup(key);
42+
}
43+
44+
@Override
45+
public String lookup(String key) {
46+
Assert.state(this.environment != null, "Unable to obtain Spring Environment from LoggerContext");
47+
return (this.environment != null) ? this.environment.getProperty(key) : null;
48+
}
49+
50+
@Override
51+
public void setLoggerContext(LoggerContext loggerContext) {
52+
this.environment = Log4J2LoggingSystem.getEnvironment(loggerContext);
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2012-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.logging.log4j2;
18+
19+
import org.apache.logging.log4j.util.PropertySource;
20+
21+
import org.springframework.core.env.Environment;
22+
import org.springframework.util.Assert;
23+
24+
/**
25+
* Returns properties from Spring.
26+
*
27+
* @author Ralph Goers
28+
*/
29+
class SpringEnvironmentPropertySource implements PropertySource {
30+
31+
/**
32+
* System properties take precedence followed by properties in Log4j properties files.
33+
*/
34+
private static final int PRIORITY = -100;
35+
36+
private final Environment environment;
37+
38+
SpringEnvironmentPropertySource(Environment environment) {
39+
Assert.notNull(environment, "Environment must not be null");
40+
this.environment = environment;
41+
}
42+
43+
@Override
44+
public int getPriority() {
45+
return PRIORITY;
46+
}
47+
48+
@Override
49+
public String getProperty(String key) {
50+
return this.environment.getProperty(key);
51+
}
52+
53+
@Override
54+
public boolean containsProperty(String key) {
55+
return this.environment.containsProperty(key);
56+
}
57+
58+
}

0 commit comments

Comments
 (0)