diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointAutoConfiguration.java index 1efe2c026d8..a86bea91e23 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointAutoConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.logging; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.logging.LogFileWebEndpoint; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -48,8 +49,9 @@ public class LogFileWebEndpointAutoConfiguration { @Bean @ConditionalOnMissingBean @Conditional(LogFileCondition.class) - public LogFileWebEndpoint logFileWebEndpoint(Environment environment, LogFileWebEndpointProperties properties) { - return new LogFileWebEndpoint(environment, properties.getExternalFile()); + public LogFileWebEndpoint logFileWebEndpoint(ObjectProvider logFile, + LogFileWebEndpointProperties properties) { + return new LogFileWebEndpoint(logFile.getIfAvailable(), properties.getExternalFile()); } private static class LogFileCondition extends SpringBootCondition { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java index 0778a1c83c2..5e68cde6ca3 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/documentation/LogFileWebEndpointDocumentationTests.java @@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentatio import org.junit.jupiter.api.Test; import org.springframework.boot.actuate.logging.LogFileWebEndpoint; +import org.springframework.boot.logging.LogFile; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -56,7 +57,7 @@ class LogFileWebEndpointDocumentationTests extends MockMvcEndpointDocumentationT @Bean LogFileWebEndpoint endpoint(Environment environment) { - return new LogFileWebEndpoint(environment); + return new LogFileWebEndpoint(LogFile.get(environment), null); } } diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java index ade50420653..e7043def919 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/logging/LogFileWebEndpoint.java @@ -25,7 +25,6 @@ import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint; import org.springframework.boot.logging.LogFile; -import org.springframework.core.env.Environment; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; @@ -42,17 +41,13 @@ public class LogFileWebEndpoint { private static final Log logger = LogFactory.getLog(LogFileWebEndpoint.class); - private final Environment environment; - private File externalFile; - public LogFileWebEndpoint(Environment environment, File externalFile) { - this.environment = environment; - this.externalFile = externalFile; - } + private final LogFile logFile; - public LogFileWebEndpoint(Environment environment) { - this(environment, null); + public LogFileWebEndpoint(LogFile logFile, File externalFile) { + this.externalFile = externalFile; + this.logFile = logFile; } @ReadOperation(produces = "text/plain; charset=UTF-8") @@ -68,12 +63,11 @@ public class LogFileWebEndpoint { if (this.externalFile != null) { return new FileSystemResource(this.externalFile); } - LogFile logFile = LogFile.get(this.environment); - if (logFile == null) { + if (this.logFile == null) { logger.debug("Missing 'logging.file.name' or 'logging.file.path' properties"); return null; } - return new FileSystemResource(logFile.toString()); + return new FileSystemResource(this.logFile.toString()); } } diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointTests.java index 0eb1be1cb27..432256b55f4 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointTests.java @@ -25,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.springframework.boot.logging.LogFile; import org.springframework.core.io.Resource; import org.springframework.mock.env.MockEnvironment; import org.springframework.util.FileCopyUtils; @@ -43,8 +44,6 @@ class LogFileWebEndpointTests { private final MockEnvironment environment = new MockEnvironment(); - private final LogFileWebEndpoint endpoint = new LogFileWebEndpoint(this.environment); - private File logFile; @BeforeEach @@ -55,19 +54,22 @@ class LogFileWebEndpointTests { @Test void nullResponseWithoutLogFile() { - assertThat(this.endpoint.logFile()).isNull(); + LogFileWebEndpoint endpoint = new LogFileWebEndpoint(null, null); + assertThat(endpoint.logFile()).isNull(); } @Test void nullResponseWithMissingLogFile() { this.environment.setProperty("logging.file.name", "no_test.log"); - assertThat(this.endpoint.logFile()).isNull(); + LogFileWebEndpoint endpoint = new LogFileWebEndpoint(LogFile.get(this.environment), null); + assertThat(endpoint.logFile()).isNull(); } @Test void resourceResponseWithLogFile() throws Exception { this.environment.setProperty("logging.file.name", this.logFile.getAbsolutePath()); - Resource resource = this.endpoint.logFile(); + LogFileWebEndpoint endpoint = new LogFileWebEndpoint(LogFile.get(this.environment), null); + Resource resource = endpoint.logFile(); assertThat(resource).isNotNull(); assertThat(contentOf(resource.getFile())).isEqualTo("--TEST--"); } @@ -76,14 +78,15 @@ class LogFileWebEndpointTests { @Deprecated void resourceResponseWithLogFileAndDeprecatedProperty() throws Exception { this.environment.setProperty("logging.file", this.logFile.getAbsolutePath()); - Resource resource = this.endpoint.logFile(); + LogFileWebEndpoint endpoint = new LogFileWebEndpoint(LogFile.get(this.environment), null); + Resource resource = endpoint.logFile(); assertThat(resource).isNotNull(); assertThat(contentOf(resource.getFile())).isEqualTo("--TEST--"); } @Test void resourceResponseWithExternalLogFile() throws Exception { - LogFileWebEndpoint endpoint = new LogFileWebEndpoint(this.environment, this.logFile); + LogFileWebEndpoint endpoint = new LogFileWebEndpoint(null, this.logFile); Resource resource = endpoint.logFile(); assertThat(resource).isNotNull(); assertThat(contentOf(resource.getFile())).isEqualTo("--TEST--"); diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointWebIntegrationTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointWebIntegrationTests.java index 425ed574b8d..5f994663962 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointWebIntegrationTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/logging/LogFileWebEndpointWebIntegrationTests.java @@ -19,16 +19,17 @@ package org.springframework.boot.actuate.logging; import java.io.File; import java.io.IOException; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.io.TempDir; import org.springframework.boot.actuate.endpoint.web.test.WebEndpointTest; -import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.boot.logging.LogFile; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; import org.springframework.http.MediaType; +import org.springframework.mock.env.MockEnvironment; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.FileCopyUtils; @@ -44,31 +45,28 @@ class LogFileWebEndpointWebIntegrationTests { private WebTestClient client; - private File logFile; + private static File tempFile; @BeforeEach - void setUp(@TempDir File temp, WebTestClient client, ConfigurableApplicationContext context) throws IOException { - this.logFile = new File(temp, "test.log"); + void setUp(WebTestClient client, ConfigurableApplicationContext context) { this.client = client; this.context = context; - FileCopyUtils.copy("--TEST--".getBytes(), this.logFile); + } - @WebEndpointTest - void getRequestProduces404ResponseWhenLogFileNotFound() { - this.client.get().uri("/actuator/logfile").exchange().expectStatus().isNotFound(); + @BeforeAll + static void setup(@TempDir File temp) throws IOException { + tempFile = temp; } @WebEndpointTest void getRequestProducesResponseWithLogFile() { - TestPropertyValues.of("logging.file.name:" + this.logFile.getAbsolutePath()).applyTo(this.context); this.client.get().uri("/actuator/logfile").exchange().expectStatus().isOk().expectHeader() .contentType("text/plain; charset=UTF-8").expectBody(String.class).isEqualTo("--TEST--"); } @WebEndpointTest void getRequestThatAcceptsTextPlainProducesResponseWithLogFile() { - TestPropertyValues.of("logging.file:" + this.logFile.getAbsolutePath()).applyTo(this.context); this.client.get().uri("/actuator/logfile").accept(MediaType.TEXT_PLAIN).exchange().expectStatus().isOk() .expectHeader().contentType("text/plain; charset=UTF-8").expectBody(String.class).isEqualTo("--TEST--"); } @@ -77,8 +75,12 @@ class LogFileWebEndpointWebIntegrationTests { static class TestConfiguration { @Bean - LogFileWebEndpoint logFileEndpoint(Environment environment) { - return new LogFileWebEndpoint(environment); + LogFileWebEndpoint logFileEndpoint() throws IOException { + File logFile = new File(tempFile, "test.log"); + FileCopyUtils.copy("--TEST--".getBytes(), logFile); + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.file", logFile.getAbsolutePath()); + return new LogFileWebEndpoint(LogFile.get(environment), null); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java index 4d8de4265d9..a786b56aea4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/logging/LoggingApplicationListener.java @@ -121,6 +121,11 @@ public class LoggingApplicationListener implements GenericApplicationListener { */ public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem"; + /** + * The name of the {@link LogFile} bean. + */ + public static final String LOGFILE_BEAN_NAME = "springBootLogFile"; + private static final Map> DEFAULT_GROUP_LOGGERS; static { MultiValueMap loggers = new LinkedMultiValueMap<>(); @@ -161,6 +166,8 @@ public class LoggingApplicationListener implements GenericApplicationListener { private LoggingSystem loggingSystem; + private LogFile logFile; + private int order = DEFAULT_ORDER; private boolean parseArgs = true; @@ -225,6 +232,9 @@ public class LoggingApplicationListener implements GenericApplicationListener { if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) { beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem); } + if (this.logFile != null && !beanFactory.containsBean(LOGFILE_BEAN_NAME)) { + beanFactory.registerSingleton(LOGFILE_BEAN_NAME, this.logFile); + } } private void onContextClosedEvent() { @@ -247,12 +257,12 @@ public class LoggingApplicationListener implements GenericApplicationListener { */ protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { new LoggingSystemProperties(environment).apply(); - LogFile logFile = LogFile.get(environment); - if (logFile != null) { - logFile.applyToSystemProperties(); + this.logFile = LogFile.get(environment); + if (this.logFile != null) { + this.logFile.applyToSystemProperties(); } initializeEarlyLoggingLevel(environment); - initializeSystem(environment, this.loggingSystem, logFile); + initializeSystem(environment, this.loggingSystem, this.logFile); initializeFinalLoggingLevels(environment, this.loggingSystem); registerShutdownHookIfNecessary(environment, this.loggingSystem); } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java index 41922918f70..0f6d12e55b7 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/EndpointsPropertiesSampleActuatorApplicationTests.java @@ -63,6 +63,13 @@ class EndpointsPropertiesSampleActuatorApplicationTests { assertThat(entity.getBody()).contains("\"hello\":\"world\""); } + @Test + void logfileWithRandomName() { + ResponseEntity entity = this.restTemplate.withBasicAuth("user", getPassword()) + .getForEntity("/admin/logfile", String.class); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + } + private String getPassword() { return "password"; } diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/application-endpoints.properties b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/application-endpoints.properties index dcd010006e5..fab27a09e53 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/application-endpoints.properties +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/resources/application-endpoints.properties @@ -1,3 +1,5 @@ server.error.path: /oops management.endpoint.health.show-details: always management.endpoints.web.base-path: /admin +logging.file=./target/${spring.application.instance_id}.log +spring.application.instance_id=${random.value}