Skip to content

Commit ba3602b

Browse files
committed
Fix handling of application names containing parenthesis
Closes gh-39564
1 parent 244f7c5 commit ba3602b

File tree

6 files changed

+229
-16
lines changed

6 files changed

+229
-16
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2012-2024 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.logback;
18+
19+
import ch.qos.logback.classic.pattern.ClassicConverter;
20+
import ch.qos.logback.classic.pattern.PropertyConverter;
21+
import ch.qos.logback.classic.spi.ILoggingEvent;
22+
23+
import org.springframework.boot.logging.LoggingSystemProperty;
24+
25+
/**
26+
* Logback {@link ClassicConverter} to convert the
27+
* {@link LoggingSystemProperty#APPLICATION_NAME APPLICATION_NAME} into a value suitable
28+
* for logging. Similar to Logback's {@link PropertyConverter} but a non-existent property
29+
* is logged as an empty string rather than {@code null}.
30+
*
31+
* @author Andy Wilkinson
32+
* @since 3.2.4
33+
*/
34+
public class ApplicationNameConverter extends ClassicConverter {
35+
36+
@Override
37+
public String convert(ILoggingEvent event) {
38+
String applicationName = event.getLoggerContextVO()
39+
.getPropertyMap()
40+
.get(LoggingSystemProperty.APPLICATION_NAME.getEnvironmentVariableName());
41+
if (applicationName == null) {
42+
applicationName = System.getProperty(LoggingSystemProperty.APPLICATION_NAME.getEnvironmentVariableName());
43+
if (applicationName == null) {
44+
applicationName = "";
45+
}
46+
}
47+
return applicationName;
48+
}
49+
50+
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -68,14 +68,15 @@ void apply(LogbackConfigurator config) {
6868
}
6969

7070
private void defaults(LogbackConfigurator config) {
71+
config.conversionRule("applicationName", ApplicationNameConverter.class);
7172
config.conversionRule("clr", ColorConverter.class);
7273
config.conversionRule("correlationId", CorrelationIdConverter.class);
7374
config.conversionRule("wex", WhitespaceThrowableProxyConverter.class);
7475
config.conversionRule("wEx", ExtendedWhitespaceThrowableProxyConverter.class);
7576
config.getContext()
7677
.putProperty("CONSOLE_LOG_PATTERN", resolve(config, "${CONSOLE_LOG_PATTERN:-"
7778
+ "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) "
78-
+ "%clr(${PID:- }){magenta} %clr(---){faint} %clr(${LOGGED_APPLICATION_NAME:-}[%15.15t]){faint} "
79+
+ "%clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%15.15t]){faint} "
7980
+ "%clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} "
8081
+ "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));
8182
String defaultCharset = Charset.defaultCharset().name();
@@ -84,7 +85,7 @@ private void defaults(LogbackConfigurator config) {
8485
config.getContext().putProperty("CONSOLE_LOG_THRESHOLD", resolve(config, "${CONSOLE_LOG_THRESHOLD:-TRACE}"));
8586
config.getContext()
8687
.putProperty("FILE_LOG_PATTERN", resolve(config, "${FILE_LOG_PATTERN:-"
87-
+ "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- ${LOGGED_APPLICATION_NAME:-}[%t] "
88+
+ "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] "
8889
+ "${LOG_CORRELATION_PATTERN:-}"
8990
+ "%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"));
9091
config.getContext()

spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@ Default logback configuration provided for import
55
-->
66

77
<included>
8+
<conversionRule conversionWord="applicationName" converterClass="org.springframework.boot.logging.logback.ApplicationNameConverter" />
89
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
910
<conversionRule conversionWord="correlationId" converterClass="org.springframework.boot.logging.logback.CorrelationIdConverter" />
1011
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
1112
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
1213

13-
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(${LOGGED_APPLICATION_NAME:-}[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
14+
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr(%applicationName[%15.15t]){faint} %clr(${LOG_CORRELATION_PATTERN:-}){faint}%clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
1415
<property name="CONSOLE_LOG_CHARSET" value="${CONSOLE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>
1516
<property name="CONSOLE_LOG_THRESHOLD" value="${CONSOLE_LOG_THRESHOLD:-TRACE}"/>
16-
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- ${LOGGED_APPLICATION_NAME:-}[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
17+
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- %applicationName[%t] ${LOG_CORRELATION_PATTERN:-}%-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
1718
<property name="FILE_LOG_CHARSET" value="${FILE_LOG_CHARSET:-${file.encoding:-UTF-8}}"/>
1819
<property name="FILE_LOG_THRESHOLD" value="${FILE_LOG_THRESHOLD:-TRACE}"/>
1920

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -565,7 +565,7 @@ void correlationLoggingToConsoleWhenHasCorrelationPattern(CapturedOutput output)
565565
}
566566

567567
@Test
568-
void applicationNameLoggingWhenHasApplicationName(CapturedOutput output) {
568+
void applicationNameLoggingToConsoleWhenHasApplicationName(CapturedOutput output) {
569569
this.environment.setProperty("spring.application.name", "myapp");
570570
this.loggingSystem.setStandardConfigLocations(false);
571571
this.loggingSystem.beforeInitialize();
@@ -575,25 +575,66 @@ void applicationNameLoggingWhenHasApplicationName(CapturedOutput output) {
575575
}
576576

577577
@Test
578-
void applicationNamePlaceHolderNotShowingWhenDisabled(CapturedOutput output) {
578+
void applicationNameLoggingToConsoleWhenHasApplicationNameWithParenthesis(CapturedOutput output) {
579+
this.environment.setProperty("spring.application.name", "myapp (dev)");
580+
this.loggingSystem.setStandardConfigLocations(false);
581+
this.loggingSystem.beforeInitialize();
582+
this.loggingSystem.initialize(this.initializationContext, null, null);
583+
this.logger.info("Hello world");
584+
assertThat(getLineWithText(output, "Hello world")).contains("[myapp (dev)] ");
585+
}
586+
587+
@Test
588+
void applicationNameLoggingToConsoleWhenDisabled(CapturedOutput output) {
579589
this.environment.setProperty("spring.application.name", "application-name");
580590
this.environment.setProperty("logging.include-application-name", "false");
581591
this.loggingSystem.setStandardConfigLocations(false);
582592
this.loggingSystem.beforeInitialize();
583593
this.loggingSystem.initialize(this.initializationContext, null, null);
584594
this.logger.info("Hello world");
585-
assertThat(getLineWithText(output, "Hello world")).doesNotContain("${sys:LOGGED_APPLICATION_NAME}");
595+
assertThat(getLineWithText(output, "Hello world")).doesNotContain("${sys:LOGGED_APPLICATION_NAME}")
596+
.doesNotContain("myapp");
586597
}
587598

588599
@Test
589-
void applicationNameLoggingWhenDisabled(CapturedOutput output) {
600+
void applicationNameLoggingToFileWhenHasApplicationName() {
590601
this.environment.setProperty("spring.application.name", "myapp");
602+
new LoggingSystemProperties(this.environment).apply();
603+
File file = new File(tmpDir(), "log4j2-test.log");
604+
LogFile logFile = getLogFile(file.getPath(), null);
605+
this.loggingSystem.setStandardConfigLocations(false);
606+
this.loggingSystem.beforeInitialize();
607+
this.loggingSystem.initialize(this.initializationContext, null, logFile);
608+
this.logger.info("Hello world");
609+
assertThat(getLineWithText(file, "Hello world")).contains("[myapp] ");
610+
}
611+
612+
@Test
613+
void applicationNameLoggingToFileWhenHasApplicationNameWithParenthesis() {
614+
this.environment.setProperty("spring.application.name", "myapp (dev)");
615+
new LoggingSystemProperties(this.environment).apply();
616+
File file = new File(tmpDir(), "log4j2-test.log");
617+
LogFile logFile = getLogFile(file.getPath(), null);
618+
this.loggingSystem.setStandardConfigLocations(false);
619+
this.loggingSystem.beforeInitialize();
620+
this.loggingSystem.initialize(this.initializationContext, null, logFile);
621+
this.logger.info("Hello world");
622+
assertThat(getLineWithText(file, "Hello world")).contains("[myapp (dev)] ");
623+
}
624+
625+
@Test
626+
void applicationNameLoggingToFileWhenDisabled() {
627+
this.environment.setProperty("spring.application.name", "application-name");
591628
this.environment.setProperty("logging.include-application-name", "false");
629+
new LoggingSystemProperties(this.environment).apply();
630+
File file = new File(tmpDir(), "log4j2-test.log");
631+
LogFile logFile = getLogFile(file.getPath(), null);
592632
this.loggingSystem.setStandardConfigLocations(false);
593633
this.loggingSystem.beforeInitialize();
594-
this.loggingSystem.initialize(this.initializationContext, null, null);
634+
this.loggingSystem.initialize(this.initializationContext, null, logFile);
595635
this.logger.info("Hello world");
596-
assertThat(getLineWithText(output, "Hello world")).doesNotContain("myapp");
636+
assertThat(getLineWithText(file, "Hello world")).doesNotContain("${sys:LOGGED_APPLICATION_NAME}")
637+
.doesNotContain("myapp");
597638
}
598639

599640
private String getRelativeClasspathLocation(String fileName) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2012-2024 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.logback;
18+
19+
import java.util.Collections;
20+
21+
import ch.qos.logback.classic.LoggerContext;
22+
import ch.qos.logback.classic.spi.LoggerContextVO;
23+
import ch.qos.logback.classic.spi.LoggingEvent;
24+
import org.junit.jupiter.api.Test;
25+
26+
import org.springframework.boot.logging.LoggingSystemProperty;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
/**
31+
* Tests for {@link ApplicationNameConverter}.
32+
*
33+
* @author Andy Wilkinson
34+
*/
35+
class ApplicationNameConverterTests {
36+
37+
private final ApplicationNameConverter converter;
38+
39+
private final LoggingEvent event = new LoggingEvent();
40+
41+
ApplicationNameConverterTests() {
42+
this.converter = new ApplicationNameConverter();
43+
this.converter.setContext(new LoggerContext());
44+
this.event.setLoggerContextRemoteView(
45+
new LoggerContextVO("test", Collections.emptyMap(), System.currentTimeMillis()));
46+
}
47+
48+
@Test
49+
void whenNoLoggedApplicationNameConvertReturnsEmptyString() {
50+
withLoggedApplicationName(null, () -> {
51+
this.converter.start();
52+
String converted = this.converter.convert(this.event);
53+
assertThat(converted).isEqualTo("");
54+
});
55+
}
56+
57+
@Test
58+
void whenLoggedApplicationNameConvertReturnsIt() {
59+
withLoggedApplicationName("my-application", () -> {
60+
this.converter.start();
61+
String converted = this.converter.convert(this.event);
62+
assertThat(converted).isEqualTo("my-application");
63+
});
64+
}
65+
66+
private void withLoggedApplicationName(String name, Runnable action) {
67+
if (name == null) {
68+
System.clearProperty(LoggingSystemProperty.APPLICATION_NAME.getEnvironmentVariableName());
69+
}
70+
else {
71+
System.setProperty(LoggingSystemProperty.APPLICATION_NAME.getEnvironmentVariableName(), name);
72+
}
73+
try {
74+
action.run();
75+
}
76+
finally {
77+
System.clearProperty(LoggingSystemProperty.APPLICATION_NAME.getEnvironmentVariableName());
78+
}
79+
}
80+
81+
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -761,20 +761,59 @@ void correlationLoggingToFileWhenUsingFileConfiguration() {
761761
}
762762

763763
@Test
764-
void applicationNameLoggingWhenHasApplicationName(CapturedOutput output) {
764+
void applicationNameLoggingToConsoleWhenHasApplicationName(CapturedOutput output) {
765765
this.environment.setProperty("spring.application.name", "myapp");
766766
initialize(this.initializationContext, null, null);
767767
this.logger.info("Hello world");
768768
assertThat(getLineWithText(output, "Hello world")).contains("[myapp] ");
769769
}
770770

771771
@Test
772-
void applicationNameLoggingWhenDisabled(CapturedOutput output) {
772+
void applicationNameLoggingToConsoleWhenHasApplicationNameWithParenthesis(CapturedOutput output) {
773+
this.environment.setProperty("spring.application.name", "myapp (dev)");
774+
initialize(this.initializationContext, null, null);
775+
this.logger.info("Hello world");
776+
assertThat(getLineWithText(output, "Hello world")).contains("[myapp (dev)] ");
777+
}
778+
779+
@Test
780+
void applicationNameLoggingToConsoleWhenDisabled(CapturedOutput output) {
773781
this.environment.setProperty("spring.application.name", "myapp");
774782
this.environment.setProperty("logging.include-application-name", "false");
775783
initialize(this.initializationContext, null, null);
776784
this.logger.info("Hello world");
777-
assertThat(getLineWithText(output, "Hello world")).doesNotContain("myapp");
785+
assertThat(getLineWithText(output, "Hello world")).doesNotContain("myapp").doesNotContain("null");
786+
}
787+
788+
@Test
789+
void applicationNameLoggingToFileWhenHasApplicationName() {
790+
this.environment.setProperty("spring.application.name", "myapp");
791+
File file = new File(tmpDir(), "logback-test.log");
792+
LogFile logFile = getLogFile(file.getPath(), null);
793+
initialize(this.initializationContext, null, logFile);
794+
this.logger.info("Hello world");
795+
assertThat(getLineWithText(file, "Hello world")).contains("[myapp] ");
796+
}
797+
798+
@Test
799+
void applicationNameLoggingToFileWhenHasApplicationNameWithParenthesis() {
800+
this.environment.setProperty("spring.application.name", "myapp (dev)");
801+
File file = new File(tmpDir(), "logback-test.log");
802+
LogFile logFile = getLogFile(file.getPath(), null);
803+
initialize(this.initializationContext, null, logFile);
804+
this.logger.info("Hello world");
805+
assertThat(getLineWithText(file, "Hello world")).contains("[myapp (dev)] ");
806+
}
807+
808+
@Test
809+
void applicationNameLoggingToFileWhenDisabled(CapturedOutput output) {
810+
this.environment.setProperty("spring.application.name", "myapp");
811+
this.environment.setProperty("logging.include-application-name", "false");
812+
File file = new File(tmpDir(), "logback-test.log");
813+
LogFile logFile = getLogFile(file.getPath(), null);
814+
initialize(this.initializationContext, null, logFile);
815+
this.logger.info("Hello world");
816+
assertThat(getLineWithText(file, "Hello world")).doesNotContain("myapp").doesNotContain("null");
778817
}
779818

780819
@Test

0 commit comments

Comments
 (0)