From 820396fdff80aed95c5b3dfcc1d8eb4ee601e7bb Mon Sep 17 00:00:00 2001 From: Jonatan Ivanov Date: Wed, 15 Nov 2023 14:09:37 -0800 Subject: [PATCH] Add ProcessInfoContributor This InfoContributor exposes information about the process of the application. See gh-38371 --- .../InfoContributorAutoConfiguration.java | 10 ++- ...itional-spring-configuration-metadata.json | 6 ++ ...InfoContributorAutoConfigurationTests.java | 12 ++++ .../actuate/info/ProcessInfoContributor.java | 58 +++++++++++++++++ .../info/ProcessInfoContributorTests.java | 55 ++++++++++++++++ .../src/docs/asciidoc/actuator/endpoints.adoc | 13 +++- .../boot/info/ProcessInfo.java | 65 +++++++++++++++++++ .../boot/info/ProcessInfoTests.java | 40 ++++++++++++ 8 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/ProcessInfoContributor.java create mode 100644 spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/ProcessInfoContributorTests.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java index ae674854af1..cbad4b851b0 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.java @@ -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. @@ -22,6 +22,7 @@ import org.springframework.boot.actuate.info.GitInfoContributor; 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.ProcessInfoContributor; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -92,4 +93,11 @@ public class InfoContributorAutoConfiguration { return new OsInfoContributor(); } + @Bean + @ConditionalOnEnabledInfoContributor(value = "process", fallback = InfoContributorFallback.DISABLE) + @Order(DEFAULT_ORDER) + public ProcessInfoContributor processInfoContributor() { + return new ProcessInfoContributor(); + } + } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index bfdc19a9445..cc6ae2934c1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -309,6 +309,12 @@ "description": "Whether to enable Operating System info.", "defaultValue": false }, + { + "name": "management.info.process.enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable process info.", + "defaultValue": false + }, { "name": "management.metrics.binders.files.enabled", "type": "java.lang.Boolean", diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java index 62801fdd641..cf2447a6d12 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfigurationTests.java @@ -28,11 +28,13 @@ import org.springframework.boot.actuate.info.Info; 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.ProcessInfoContributor; 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.ProcessInfo; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -164,6 +166,16 @@ class InfoContributorAutoConfigurationTests { }); } + @Test + void processInfoContributor() { + this.contextRunner.withPropertyValues("management.info.process.enabled=true").run((context) -> { + assertThat(context).hasSingleBean(ProcessInfoContributor.class); + Map content = invokeContributor(context.getBean(ProcessInfoContributor.class)); + assertThat(content).containsKey("process"); + assertThat(content.get("process")).isInstanceOf(ProcessInfo.class); + }); + } + private Map invokeContributor(InfoContributor contributor) { Info.Builder builder = new Info.Builder(); contributor.contribute(builder); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/ProcessInfoContributor.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/ProcessInfoContributor.java new file mode 100644 index 00000000000..65e8ec41ef7 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/ProcessInfoContributor.java @@ -0,0 +1,58 @@ +/* + * 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.ProcessInfoContributor.ProcessInfoContributorRuntimeHints; +import org.springframework.boot.info.ProcessInfo; +import org.springframework.context.annotation.ImportRuntimeHints; + +/** + * An {@link InfoContributor} that exposes {@link ProcessInfo}. + * + * @author Jonatan Ivanov + * @since 3.3.0 + */ +@ImportRuntimeHints(ProcessInfoContributorRuntimeHints.class) +public class ProcessInfoContributor implements InfoContributor { + + private final ProcessInfo processInfo; + + public ProcessInfoContributor() { + this.processInfo = new ProcessInfo(); + } + + @Override + public void contribute(Builder builder) { + builder.withDetail("process", this.processInfo); + } + + static class ProcessInfoContributorRuntimeHints implements RuntimeHintsRegistrar { + + private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar(); + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + this.bindingRegistrar.registerReflectionHints(hints.reflection(), ProcessInfo.class); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/ProcessInfoContributorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/ProcessInfoContributorTests.java new file mode 100644 index 00000000000..389a3ca2bbb --- /dev/null +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/info/ProcessInfoContributorTests.java @@ -0,0 +1,55 @@ +/* + * 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.ProcessInfoContributor.ProcessInfoContributorRuntimeHints; +import org.springframework.boot.info.ProcessInfo; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ProcessInfoContributor}. + * + * @author Jonatan Ivanov + */ +class ProcessInfoContributorTests { + + @Test + void processInfoShouldBeAdded() { + ProcessInfoContributor processInfoContributor = new ProcessInfoContributor(); + Info.Builder builder = new Info.Builder(); + processInfoContributor.contribute(builder); + Info info = builder.build(); + assertThat(info.get("process")).isInstanceOf(ProcessInfo.class); + } + + @Test + void shouldRegisterHints() { + RuntimeHints runtimeHints = new RuntimeHints(); + new ProcessInfoContributorRuntimeHints().registerHints(runtimeHints, getClass().getClassLoader()); + assertThat(RuntimeHintsPredicates.reflection() + .onType(ProcessInfo.class) + .withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) + .accepts(runtimeHints); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc index 92989a7bdd9..641d4445218 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/endpoints.adoc @@ -1087,12 +1087,17 @@ When appropriate, Spring auto-configures the following `InfoContributor` beans: | Exposes Operating System information. | None. +| `process` +| {spring-boot-actuator-module-code}/info/ProcessInfoContributor.java[`ProcessInfoContributor`] +| Exposes process information. +| None. + |=== Whether an individual contributor is enabled is controlled by its `management.info..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 `process` contributors are disabled by default. Each can be enabled by setting its `management.info..enabled` property to `true`. The `build` and `git` info contributors are enabled by default. @@ -1190,6 +1195,12 @@ The `info` endpoint publishes information about your Operating System, see {spri +[[actuator.endpoints.info.process-information]] +==== Process Information +The `info` endpoint publishes information about your process, see {spring-boot-module-api}/info/ProcessInfo.html[`Process`] 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. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java new file mode 100644 index 00000000000..37fc0b02773 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/info/ProcessInfo.java @@ -0,0 +1,65 @@ +/* + * 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; + +/** + * Information about the process of the application. + * + * @author Jonatan Ivanov + * @since 3.3.0 + */ +public class ProcessInfo { + + private static final Runtime runtime = Runtime.getRuntime(); + + private final long pid; + + private final long parentPid; + + private final String owner; + + public ProcessInfo() { + ProcessHandle process = ProcessHandle.current(); + this.pid = process.pid(); + this.parentPid = process.parent().map(ProcessHandle::pid).orElse(-1L); + this.owner = process.info().user().orElse(null); + } + + /** + * Number of processors available to the process. This value may change between + * invocations especially in (containerized) environments where resource usage can be + * isolated (for example using control groups). + * @return result of {@link Runtime#availableProcessors()} + * @see Runtime#availableProcessors() + */ + public int getCpus() { + return runtime.availableProcessors(); + } + + public long getPid() { + return this.pid; + } + + public long getParentPid() { + return this.parentPid; + } + + public String getOwner() { + return this.owner; + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java new file mode 100644 index 00000000000..8337d2d243a --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/info/ProcessInfoTests.java @@ -0,0 +1,40 @@ +/* + * 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.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ProcessInfo}. + * + * @author Jonatan Ivanov + */ +class ProcessInfoTests { + + @Test + void processInfoIsAvailable() { + ProcessInfo processInfo = new ProcessInfo(); + assertThat(processInfo.getCpus()).isEqualTo(Runtime.getRuntime().availableProcessors()); + assertThat(processInfo.getOwner()).isEqualTo(ProcessHandle.current().info().user().orElse(null)); + assertThat(processInfo.getPid()).isEqualTo(ProcessHandle.current().pid()); + assertThat(processInfo.getParentPid()) + .isEqualTo(ProcessHandle.current().parent().map(ProcessHandle::pid).orElse(null)); + } + +}