diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java index 89021b3efa0f..d71ffbf94b43 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/LoggingSystemProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.function.BiConsumer; import org.springframework.boot.system.ApplicationPid; import org.springframework.core.env.ConfigurableEnvironment; @@ -130,15 +131,35 @@ public class LoggingSystemProperties { */ public static final String LOG_DATEFORMAT_PATTERN = "LOG_DATEFORMAT_PATTERN"; + private static final BiConsumer systemPropertySetter = (name, value) -> { + if (System.getProperty(name) == null && value != null) { + System.setProperty(name, value); + } + }; + private final Environment environment; + private final BiConsumer setter; + /** * Create a new {@link LoggingSystemProperties} instance. * @param environment the source environment */ public LoggingSystemProperties(Environment environment) { + this(environment, systemPropertySetter); + } + + /** + * Create a new {@link LoggingSystemProperties} instance. + * @param environment the source environment + * @param setter setter used to apply the property + * @since 2.4.2 + */ + public LoggingSystemProperties(Environment environment, BiConsumer setter) { Assert.notNull(environment, "Environment must not be null"); + Assert.notNull(setter, "Setter must not be null"); this.environment = environment; + this.setter = setter; } protected Charset getDefaultCharset() { @@ -200,9 +221,7 @@ protected final void setSystemProperty(PropertyResolver resolver, String systemP } protected final void setSystemProperty(String name, String value) { - if (System.getProperty(name) == null && value != null) { - System.setProperty(name, value); - } + this.setter.accept(name, value); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java index f257ff5ee787..a1a9747c43ed 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -146,15 +146,11 @@ protected void loadDefaults(LoggingInitializationContext initializationContext, if (debug) { StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); } + Environment environment = initializationContext.getEnvironment(); + // Apply system properties directly in case the same JVM runs multiple apps + new LoggingSystemProperties(environment, context::putProperty).apply(logFile); LogbackConfigurator configurator = debug ? new DebugLogbackConfigurator(context) : new LogbackConfigurator(context); - Environment environment = initializationContext.getEnvironment(); - context.putProperty(LoggingSystemProperties.LOG_LEVEL_PATTERN, - environment.resolvePlaceholders("${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}")); - context.putProperty(LoggingSystemProperties.LOG_DATEFORMAT_PATTERN, environment.resolvePlaceholders( - "${logging.pattern.dateformat:${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}")); - context.putProperty(LoggingSystemProperties.ROLLING_FILE_NAME_PATTERN, environment - .resolvePlaceholders("${logging.pattern.rolling-file-name:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}")); new DefaultLogbackConfiguration(initializationContext, logFile).apply(configurator); context.setPackagingDataEnabled(true); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 9319b12c254c..0788dcdaa931 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,9 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Handler; import java.util.logging.LogManager; @@ -87,10 +89,13 @@ class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { private LoggingInitializationContext initializationContext; + private Set systemPropertyNames; + @BeforeEach void setup() { System.getProperties().remove(LoggingSystemProperties.CONSOLE_LOG_CHARSET); System.getProperties().remove(LoggingSystemProperties.FILE_LOG_CHARSET); + this.systemPropertyNames = new HashSet<>(System.getProperties().keySet()); this.loggingSystem.cleanUp(); this.logger = ((LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory()).getLogger(getClass()); this.environment = new MockEnvironment(); @@ -101,6 +106,7 @@ void setup() { @AfterEach void cleanUp() { + System.getProperties().keySet().retainAll(this.systemPropertyNames); this.loggingSystem.cleanUp(); ((LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory()).stop(); } @@ -312,6 +318,7 @@ void testConsolePatternProperty(CapturedOutput output) { @Test void testLevelPatternProperty(CapturedOutput output) { this.environment.setProperty("logging.pattern.level", "X%clr(%p)X"); + new LoggingSystemProperties(this.environment).apply(); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment); initialize(loggingInitializationContext, null, null); this.logger.info("Hello world"); @@ -513,6 +520,19 @@ void initializationIsOnlyPerformedOnceUntilCleanedUp() { @Test void testDateformatPatternProperty(CapturedOutput output) { + this.environment.setProperty("logging.pattern.dateformat", "yyyy-MM-dd'T'hh:mm:ss.SSSZ"); + new LoggingSystemProperties(this.environment).apply(); + LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment); + initialize(loggingInitializationContext, null, null); + this.logger.info("Hello world"); + assertThat(getLineWithText(output, "Hello world")) + .containsPattern("\\d{4}-\\d{2}\\-\\d{2}T\\d{2}:\\d{2}:\\d{2}"); + } + + @Test // gh-24835 + void testDateformatPatternPropertyDirect(CapturedOutput output) { + this.environment.setProperty("logging.pattern.dateformat", "yyyy'T'hh:mm:ss.SSSZ"); + new LoggingSystemProperties(this.environment).apply(); this.environment.setProperty("logging.pattern.dateformat", "yyyy-MM-dd'T'hh:mm:ss.SSSZ"); LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext(this.environment); initialize(loggingInitializationContext, null, null);