diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java index b342f6bb3cfb..c418c27c19c8 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java @@ -336,7 +336,7 @@ public ConfigurableApplicationContext run(String... args) { afterRefresh(context, applicationArguments); startup.started(); if (this.logStartupInfo) { - new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), startup); + new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup); } listeners.started(context, startup.timeTakenToStarted()); callRunners(context, applicationArguments); @@ -404,6 +404,7 @@ private void prepareContext(DefaultBootstrapContext bootstrapContext, Configurab bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); + logStartupInfo(context); logStartupProfileInfo(context); } // Add boot specific singleton beans @@ -633,14 +634,27 @@ protected void applyInitializers(ConfigurableApplicationContext context) { /** * Called to log startup information, subclasses may override to add additional * logging. - * @param isRoot true if this application is the root of a context hierarchy + * @param context the application context + * @since 3.4.0 */ - protected void logStartupInfo(boolean isRoot) { + protected void logStartupInfo(ConfigurableApplicationContext context) { + boolean isRoot = context.getParent() == null; if (isRoot) { - new StartupInfoLogger(this.mainApplicationClass).logStarting(getApplicationLog()); + new StartupInfoLogger(this.mainApplicationClass, context.getEnvironment()).logStarting(getApplicationLog()); } } + /** + * Called to log startup information, subclasses may override to add additional + * logging. + * @param isRoot true if this application is the root of a context hierarchy + * @deprecated since 3.4.0 for removal in 3.6.0 in favor of + * {@link #logStartupInfo(ConfigurableApplicationContext)} + */ + @Deprecated(since = "3.4.0", forRemoval = true) + protected void logStartupInfo(boolean isRoot) { + } + /** * Called to log active profile information. * @param context the application context diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java index 8b6c5f3439f5..b3d40bd8085b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/StartupInfoLogger.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -23,8 +23,8 @@ import org.springframework.aot.AotDetector; import org.springframework.boot.SpringApplication.Startup; import org.springframework.boot.system.ApplicationHome; -import org.springframework.boot.system.ApplicationPid; import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; import org.springframework.core.log.LogMessage; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -41,8 +41,11 @@ class StartupInfoLogger { private final Class sourceClass; - StartupInfoLogger(Class sourceClass) { + private final Environment environment; + + StartupInfoLogger(Class sourceClass, Environment environment) { this.sourceClass = sourceClass; + this.environment = environment; } void logStarting(Log applicationLog) { @@ -62,7 +65,7 @@ private CharSequence getStartingMessage() { message.append("Starting"); appendAotMode(message); appendApplicationName(message); - appendVersion(message, this.sourceClass); + appendApplicationVersion(message); appendJavaVersion(message); appendPid(message); appendContext(message); @@ -106,8 +109,12 @@ private void appendVersion(StringBuilder message, Class source) { append(message, "v", () -> source.getPackage().getImplementationVersion()); } + private void appendApplicationVersion(StringBuilder message) { + append(message, "v", () -> this.environment.getProperty("spring.application.version")); + } + private void appendPid(StringBuilder message) { - append(message, "with PID ", ApplicationPid::new); + append(message, "with PID ", () -> this.environment.getProperty("spring.application.pid")); } private void appendContext(StringBuilder message) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java index ae1bbb4ed2ee..cdb42a1ce5a2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/StartupInfoLoggerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-2024 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. @@ -17,10 +17,11 @@ package org.springframework.boot; import org.apache.commons.logging.Log; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.SpringApplication.Startup; -import org.springframework.boot.system.ApplicationPid; +import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.assertArg; @@ -39,15 +40,48 @@ class StartupInfoLoggerTests { private final Log log = mock(Log.class); + private MockEnvironment environment; + + @BeforeEach + void setUp() { + this.environment = new MockEnvironment(); + this.environment.setProperty("spring.application.version", "1.2.3"); + this.environment.setProperty("spring.application.pid", "42"); + } + @Test void startingFormat() { given(this.log.isInfoEnabled()).willReturn(true); - new StartupInfoLogger(getClass()).logStarting(this.log); + new StartupInfoLogger(getClass(), this.environment).logStarting(this.log); then(this.log).should() - .info(assertArg((message) -> assertThat(message.toString()) - .contains("Starting " + getClass().getSimpleName() + " using Java " + System.getProperty("java.version") - + " with PID " + new ApplicationPid() + " (started by " + System.getProperty("user.name") - + " in " + System.getProperty("user.dir") + ")"))); + .info(assertArg( + (message) -> assertThat(message.toString()).contains("Starting " + getClass().getSimpleName() + + " v1.2.3 using Java " + System.getProperty("java.version") + " with PID 42 (started by " + + System.getProperty("user.name") + " in " + System.getProperty("user.dir") + ")"))); + } + + @Test + void startingFormatWhenVersionIsNotAvailable() { + this.environment.setProperty("spring.application.version", ""); + given(this.log.isInfoEnabled()).willReturn(true); + new StartupInfoLogger(getClass(), this.environment).logStarting(this.log); + then(this.log).should() + .info(assertArg( + (message) -> assertThat(message.toString()).contains("Starting " + getClass().getSimpleName() + + " using Java " + System.getProperty("java.version") + " with PID 42 (started by " + + System.getProperty("user.name") + " in " + System.getProperty("user.dir") + ")"))); + } + + @Test + void startingFormatWhenPidIsNotAvailable() { + this.environment.setProperty("spring.application.pid", ""); + given(this.log.isInfoEnabled()).willReturn(true); + new StartupInfoLogger(getClass(), this.environment).logStarting(this.log); + then(this.log).should() + .info(assertArg( + (message) -> assertThat(message.toString()).contains("Starting " + getClass().getSimpleName() + + " v1.2.3 using Java " + System.getProperty("java.version") + " (started by " + + System.getProperty("user.name") + " in " + System.getProperty("user.dir") + ")"))); } @Test @@ -55,11 +89,11 @@ void startingFormatInAotMode() { System.setProperty("spring.aot.enabled", "true"); try { given(this.log.isInfoEnabled()).willReturn(true); - new StartupInfoLogger(getClass()).logStarting(this.log); + new StartupInfoLogger(getClass(), this.environment).logStarting(this.log); then(this.log).should() .info(assertArg((message) -> assertThat(message.toString()) - .contains("Starting AOT-processed " + getClass().getSimpleName() + " using Java " - + System.getProperty("java.version") + " with PID " + new ApplicationPid() + " (started by " + .contains("Starting AOT-processed " + getClass().getSimpleName() + " v1.2.3 using Java " + + System.getProperty("java.version") + " with PID 42 (started by " + System.getProperty("user.name") + " in " + System.getProperty("user.dir") + ")"))); } @@ -71,7 +105,7 @@ void startingFormatInAotMode() { @Test void startedFormat() { given(this.log.isInfoEnabled()).willReturn(true); - new StartupInfoLogger(getClass()).logStarted(this.log, new TestStartup(1345L, "Started")); + new StartupInfoLogger(getClass(), this.environment).logStarted(this.log, new TestStartup(1345L, "Started")); then(this.log).should() .info(assertArg((message) -> assertThat(message.toString()).matches("Started " + getClass().getSimpleName() + " in \\d+\\.\\d{1,3} seconds \\(process running for 1.345\\)"))); @@ -80,7 +114,7 @@ void startedFormat() { @Test void startedWithoutUptimeFormat() { given(this.log.isInfoEnabled()).willReturn(true); - new StartupInfoLogger(getClass()).logStarted(this.log, new TestStartup(null, "Started")); + new StartupInfoLogger(getClass(), this.environment).logStarted(this.log, new TestStartup(null, "Started")); then(this.log).should() .info(assertArg((message) -> assertThat(message.toString()) .matches("Started " + getClass().getSimpleName() + " in \\d+\\.\\d{1,3} seconds"))); @@ -89,7 +123,7 @@ void startedWithoutUptimeFormat() { @Test void restoredFormat() { given(this.log.isInfoEnabled()).willReturn(true); - new StartupInfoLogger(getClass()).logStarted(this.log, new TestStartup(null, "Restored")); + new StartupInfoLogger(getClass(), this.environment).logStarted(this.log, new TestStartup(null, "Restored")); then(this.log).should() .info(assertArg((message) -> assertThat(message.toString()) .matches("Restored " + getClass().getSimpleName() + " in \\d+\\.\\d{1,3} seconds")));