Skip to content

Add SpringInfoContributor #38356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-2023 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.
Expand All @@ -22,6 +22,7 @@
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.info.JavaInfoContributor;
import org.springframework.boot.actuate.info.OsInfoContributor;
import org.springframework.boot.actuate.info.SpringInfoContributor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -34,6 +35,7 @@
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;

/**
* {@link EnableAutoConfiguration Auto-configuration} for standard
Expand Down Expand Up @@ -92,4 +94,11 @@ public OsInfoContributor osInfoContributor() {
return new OsInfoContributor();
}

@Bean
@ConditionalOnEnabledInfoContributor(value = "spring", fallback = InfoContributorFallback.DISABLE)
@Order(DEFAULT_ORDER)
public SpringInfoContributor springInfoContributor(Environment environment) {
return new SpringInfoContributor(environment);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@
"description": "Whether to enable Operating System info.",
"defaultValue": false
},
{
"name": "management.info.spring.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable Spring-related info.",
"defaultValue": false
},
{
"name": "management.metrics.binders.files.enabled",
"type": "java.lang.Boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.info.JavaInfoContributor;
import org.springframework.boot.actuate.info.OsInfoContributor;
import org.springframework.boot.actuate.info.SpringInfoContributor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.boot.info.JavaInfo;
import org.springframework.boot.info.OsInfo;
import org.springframework.boot.info.SpringInfo;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -164,6 +166,16 @@ void osInfoContributor() {
});
}

@Test
void springInfoContributor() {
this.contextRunner.withPropertyValues("management.info.spring.enabled=true").run((context) -> {
assertThat(context).hasSingleBean(SpringInfoContributor.class);
Map<String, Object> content = invokeContributor(context.getBean(SpringInfoContributor.class));
assertThat(content).containsKey("spring");
assertThat(content.get("spring")).isInstanceOf(SpringInfo.class);
});
}

private Map<String, Object> invokeContributor(InfoContributor contributor) {
Info.Builder builder = new Info.Builder();
contributor.contribute(builder);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.actuate.info;

import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.boot.actuate.info.Info.Builder;
import org.springframework.boot.actuate.info.SpringInfoContributor.SpringInfoContributorRuntimeHints;
import org.springframework.boot.info.SpringInfo;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.env.Environment;

/**
* An {@link InfoContributor} that exposes info related to Spring projects.
*
* @author Jonatan Ivanov
* @since 3.3.0
*/
@ImportRuntimeHints(SpringInfoContributorRuntimeHints.class)
public class SpringInfoContributor implements InfoContributor {

private final SpringInfo springInfo;

public SpringInfoContributor(Environment environment) {
this.springInfo = new SpringInfo(environment);
}

@Override
public void contribute(Builder builder) {
builder.withDetail("spring", this.springInfo);
}

static class SpringInfoContributorRuntimeHints implements RuntimeHintsRegistrar {

private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
this.bindingRegistrar.registerReflectionHints(hints.reflection(), SpringInfo.class);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.actuate.info;

import org.junit.jupiter.api.Test;

import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.boot.actuate.info.SpringInfoContributor.SpringInfoContributorRuntimeHints;
import org.springframework.boot.info.SpringInfo;
import org.springframework.mock.env.MockEnvironment;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link SpringInfoContributor}.
*
* @author Jonatan Ivanov
*/
class SpringInfoContributorTests {

@Test
void springInfoShouldBeAdded() {
SpringInfoContributor springInfoContributor = new SpringInfoContributor(new MockEnvironment());
Info.Builder builder = new Info.Builder();
springInfoContributor.contribute(builder);
Info info = builder.build();
assertThat(info.getDetails().get("spring")).isInstanceOf(SpringInfo.class);
}

@Test
void shouldRegisterHints() {
RuntimeHints runtimeHints = new RuntimeHints();
new SpringInfoContributorRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader());
assertThat(RuntimeHintsPredicates.reflection()
.onType(SpringInfo.class)
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))
.accepts(runtimeHints);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1087,12 +1087,17 @@ When appropriate, Spring auto-configures the following `InfoContributor` beans:
| Exposes Operating System information.
| None.

| `spring`
| {spring-boot-actuator-module-code}/info/SpringInfoContributor.java[`SpringInfoContributor`]
| Exposes Spring-related information.
| None.

|===

Whether an individual contributor is enabled is controlled by its `management.info.<id>.enabled` property.
Different contributors have different defaults for this property, depending on their prerequisites and the nature of the information that they expose.

With no prerequisites to indicate that they should be enabled, the `env`, `java`, and `os` contributors are disabled by default.
With no prerequisites to indicate that they should be enabled, the `env`, `java`, `os`, and `spring` contributors are disabled by default.
Each can be enabled by setting its `management.info.<id>.enabled` property to `true`.

The `build` and `git` info contributors are enabled by default.
Expand Down Expand Up @@ -1190,6 +1195,12 @@ The `info` endpoint publishes information about your Operating System, see {spri



[[actuator.endpoints.info.spring-information]]
==== Spring Information
The `info` endpoint publishes Spring-related information, see {spring-boot-module-api}/info/SpringInfo.html[`SpringInfo`] for more details.



[[actuator.endpoints.info.writing-custom-info-contributors]]
==== Writing Custom InfoContributors
To provide custom application information, you can register Spring beans that implement the {spring-boot-actuator-module-code}/info/InfoContributor.java[`InfoContributor`] interface.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.info;

import org.springframework.boot.SpringBootVersion;
import org.springframework.core.SpringVersion;
import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils;

/**
* Information related to Spring projects.
*
* @author Jonatan Ivanov
* @since 3.3.0
*/
public class SpringInfo {

private final SpringFrameworkInfo framework;

private final SpringBootInfo boot;

private final String[] profiles;

public SpringInfo(Environment environment) {
this.framework = new SpringFrameworkInfo();
this.boot = new SpringBootInfo();
this.profiles = ObjectUtils.isEmpty(environment.getActiveProfiles()) ? environment.getDefaultProfiles()
: environment.getActiveProfiles();
}

public SpringFrameworkInfo getFramework() {
return this.framework;
}

public SpringBootInfo getBoot() {
return this.boot;
}

public String[] getProfiles() {
return this.profiles;
}

/**
* Information about Spring Framework.
*/
public static class SpringFrameworkInfo {

private final String version;

public SpringFrameworkInfo() {
this.version = SpringVersion.getVersion();
}

public String getVersion() {
return this.version;
}

}

/**
* Information about Spring Boot.
*/
public static class SpringBootInfo {

private final String version;

public SpringBootInfo() {
this.version = SpringBootVersion.getVersion();
}

public String getVersion() {
return this.version;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.info;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import org.springframework.boot.SpringBootVersion;
import org.springframework.core.SpringVersion;
import org.springframework.mock.env.MockEnvironment;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link SpringInfo}.
*
* @author Jonatan Ivanov
*/
@ExtendWith(MockitoExtension.class)
class SpringInfoTests {

private MockEnvironment environment;

@BeforeEach
void setUp() {
this.environment = new MockEnvironment();
}

@Test
void springInfoIsAvailableWithoutExplicitProfiles() {
SpringInfo info = new SpringInfo(this.environment);
assertThat(info.getFramework().getVersion()).isEqualTo(SpringVersion.getVersion());
assertThat(info.getBoot().getVersion()).isEqualTo(SpringBootVersion.getVersion());
assertThat(info.getProfiles()).containsExactly("default");
}

@Test
void springInfoContainsDefaultProfilesIfDefaultProfileIsSet() {
this.environment.setDefaultProfiles("test-default-profile");
SpringInfo info = new SpringInfo(this.environment);
assertThat(info.getProfiles()).containsExactly("test-default-profile");
}

@Test
void springInfoContainsActiveProfilesIfActiveProfilesIsSet() {
this.environment.setActiveProfiles("test-active-profile");
SpringInfo info = new SpringInfo(this.environment);
assertThat(info.getProfiles()).containsExactly("test-active-profile");
}

@Test
void springInfoContainsActiveProfilesIfBothDefaultAndActiveProfilesAreSet() {
this.environment.setDefaultProfiles("test-default-profile");
this.environment.setActiveProfiles("test-active-profile");
SpringInfo info = new SpringInfo(this.environment);
assertThat(info.getProfiles()).containsExactly("test-active-profile");
}

}