diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml index 3768e9f4f2..aa824b2ae1 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml @@ -174,6 +174,11 @@ micrometer-registry-signalfx true + + io.micrometer + micrometer-registry-stackdriver + true + io.micrometer micrometer-registry-statsd diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverMetricsExportAutoConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverMetricsExportAutoConfiguration.java new file mode 100644 index 0000000000..b3d9a20a3f --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverMetricsExportAutoConfiguration.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2020 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.autoconfigure.metrics.export.stackdriver; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.stackdriver.StackdriverConfig; +import io.micrometer.stackdriver.StackdriverMeterRegistry; + +import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration; +import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to + * Stackdriver. + * + * @author Johannes Graf + * @author Stephane Nicoll + * @since 2.3.0 + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class }) +@AutoConfigureAfter(MetricsAutoConfiguration.class) +@ConditionalOnBean(Clock.class) +@ConditionalOnClass(StackdriverMeterRegistry.class) +@ConditionalOnProperty(prefix = "management.metrics.export.stackdriver", name = "enabled", havingValue = "true", + matchIfMissing = true) +@EnableConfigurationProperties(StackdriverProperties.class) +public class StackdriverMetricsExportAutoConfiguration { + + private final StackdriverProperties properties; + + public StackdriverMetricsExportAutoConfiguration(StackdriverProperties stackdriverProperties) { + this.properties = stackdriverProperties; + } + + @Bean + @ConditionalOnMissingBean + public StackdriverConfig stackdriverConfig() { + return new StackdriverPropertiesConfigAdapter(this.properties); + } + + @Bean + @ConditionalOnMissingBean + public StackdriverMeterRegistry stackdriverMeterRegistry(StackdriverConfig stackdriverConfig, Clock clock) { + return StackdriverMeterRegistry.builder(stackdriverConfig).clock(clock).build(); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverProperties.java new file mode 100644 index 0000000000..07c6668244 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverProperties.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2020 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.autoconfigure.metrics.export.stackdriver; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * {@link ConfigurationProperties @ConfigurationProperties} for configuring Stackdriver + * metrics export. + * + * @author Johannes Graf + * @author Stephane Nicoll + * @since 2.3.0 + */ +@ConfigurationProperties(prefix = "management.metrics.export.stackdriver") +public class StackdriverProperties extends StepRegistryProperties { + + /** + * Identifier of the Google Cloud project to monitor. + */ + private String projectId; + + /** + * Monitored resource type. + */ + private String resourceType = "global"; + + public String getProjectId() { + return this.projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getResourceType() { + return this.resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesConfigAdapter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesConfigAdapter.java new file mode 100644 index 0000000000..9b14af43f7 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesConfigAdapter.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2020 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.autoconfigure.metrics.export.stackdriver; + +import io.micrometer.stackdriver.StackdriverConfig; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter; + +/** + * Adapter to convert {@link StackdriverProperties} to a {@link StackdriverConfig}. + * + * @author Johannes Graf + * @since 2.3.0 + */ +public class StackdriverPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter + implements StackdriverConfig { + + public StackdriverPropertiesConfigAdapter(StackdriverProperties properties) { + super(properties); + } + + @Override + public String projectId() { + return get(StackdriverProperties::getProjectId, StackdriverConfig.super::projectId); + } + + @Override + public String resourceType() { + return get(StackdriverProperties::getResourceType, StackdriverConfig.super::resourceType); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/package-info.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/package-info.java new file mode 100644 index 0000000000..df40a7adb9 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2020 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. + */ + +/** + * Support for exporting actuator metrics to Stackdriver. + */ +package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver; diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverMetricsExportAutoConfigurationTest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverMetricsExportAutoConfigurationTest.java new file mode 100644 index 0000000000..ae9182774e --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverMetricsExportAutoConfigurationTest.java @@ -0,0 +1,132 @@ +/* + * Copyright 2012-2020 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.autoconfigure.metrics.export.stackdriver; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.stackdriver.StackdriverConfig; +import io.micrometer.stackdriver.StackdriverMeterRegistry; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link StackdriverMetricsExportAutoConfiguration}. + * + * @author Johannes Graf + */ +class StackdriverMetricsExportAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(StackdriverMetricsExportAutoConfiguration.class)); + + @Test + void backsOffWithoutAClock() { + this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(StackdriverMeterRegistry.class)); + } + + @Test + void failsWithoutAnProjectId() { + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .run((context) -> assertThat(context).hasFailed()); + } + + @Test + void autoConfiguresConfigAndMeterRegistry() { + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .withPropertyValues("management.metrics.export.stackdriver.project-id=test-project") + .run((context) -> assertThat(context).hasSingleBean(StackdriverMeterRegistry.class) + .hasSingleBean(StackdriverConfig.class)); + } + + @Test + void autoConfigurationCanBeDisabled() { + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .withPropertyValues("management.metrics.export.stackdriver.enabled=false") + .run((context) -> assertThat(context).doesNotHaveBean(StackdriverMeterRegistry.class) + .doesNotHaveBean(StackdriverConfig.class)); + } + + @Test + void allowsCustomConfigToBeUsed() { + this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class) + .run((context) -> assertThat(context).hasSingleBean(StackdriverMeterRegistry.class) + .hasSingleBean(StackdriverConfig.class).hasBean("customConfig")); + } + + @Test + void allowsCustomRegistryToBeUsed() { + this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class) + .withPropertyValues("management.metrics.export.stackdriver.project-id=test-project") + .run((context) -> assertThat(context).hasSingleBean(StackdriverMeterRegistry.class) + .hasBean("customRegistry").hasSingleBean(StackdriverConfig.class)); + } + + @Test + void stopsMeterRegistryWhenContextIsClosed() { + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .withPropertyValues("management.metrics.export.stackdriver.project-id=test-project").run((context) -> { + StackdriverMeterRegistry registry = context.getBean(StackdriverMeterRegistry.class); + assertThat(registry.isClosed()).isFalse(); + context.close(); + assertThat(registry.isClosed()).isTrue(); + }); + } + + @Configuration(proxyBeanMethods = false) + static class BaseConfiguration { + + @Bean + Clock clock() { + return Clock.SYSTEM; + } + + } + + @Configuration(proxyBeanMethods = false) + @Import(BaseConfiguration.class) + static class CustomConfigConfiguration { + + @Bean + StackdriverConfig customConfig() { + return (key) -> { + if ("stackdriver.projectId".equals(key)) { + return "test-project"; + } + return null; + }; + } + + } + + @Configuration(proxyBeanMethods = false) + @Import(BaseConfiguration.class) + static class CustomRegistryConfiguration { + + @Bean + StackdriverMeterRegistry customRegistry(StackdriverConfig config, Clock clock) { + return new StackdriverMeterRegistry(config, clock); + } + + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesConfigAdapterTest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesConfigAdapterTest.java new file mode 100644 index 0000000000..6eb3650e96 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesConfigAdapterTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2020 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.autoconfigure.metrics.export.stackdriver; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link StackdriverPropertiesConfigAdapter}. + * + * @author Johannes Graf + */ +class StackdriverPropertiesConfigAdapterTest { + + @Test + void whenPropertiesProjectIdIsSetAdapterProjectIdReturnsIt() { + StackdriverProperties properties = new StackdriverProperties(); + properties.setProjectId("my-gcp-project-id"); + assertThat(new StackdriverPropertiesConfigAdapter(properties).projectId()).isEqualTo("my-gcp-project-id"); + } + + @Test + void whenPropertiesResourceTypeIsSetAdapterResourceTypeReturnsIt() { + StackdriverProperties properties = new StackdriverProperties(); + properties.setResourceType("my-resource-type"); + assertThat(new StackdriverPropertiesConfigAdapter(properties).resourceType()).isEqualTo("my-resource-type"); + } + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesTest.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesTest.java new file mode 100644 index 0000000000..89c16cb9c0 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/stackdriver/StackdriverPropertiesTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2020 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.autoconfigure.metrics.export.stackdriver; + +import io.micrometer.stackdriver.StackdriverConfig; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesTests; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link StackdriverProperties}. + * + * @author Johannes Graf + */ +class StackdriverPropertiesTest extends StepRegistryPropertiesTests { + + @Test + void defaultValuesAreConsistent() { + StackdriverProperties properties = new StackdriverProperties(); + StackdriverConfig config = (key) -> null; + assertStepRegistryDefaultValues(properties, config); + assertThat(properties.getResourceType()).isEqualTo(config.resourceType()); + } + +} diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml index 38c72904ff..ce2763d9eb 100644 --- a/spring-boot-project/spring-boot-dependencies/pom.xml +++ b/spring-boot-project/spring-boot-dependencies/pom.xml @@ -1038,6 +1038,17 @@ lettuce-core ${lettuce.version} + + io.micrometer + micrometer-registry-stackdriver + ${micrometer.version} + + + javax.annotation + javax.annotation-api + + + io.micrometer micrometer-bom diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 24fd61b032..1537c2f4f6 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -1245,6 +1245,7 @@ Spring Boot Actuator provides dependency management and auto-configuration for h - <> - <> - <> +- <> - <> - <> @@ -1584,6 +1585,25 @@ You can also disable it explicitly: +[[production-ready-metrics-export-stackdriver]] +==== Stackdriver +Stackdriver registry pushes metrics to https://cloud.google.com/stackdriver/[Stackdriver] periodically. +To export metrics to SaaS {micrometer-registry-docs}/stackdriver[Stackdriver], your Google Cloud project id must be provided: + +[source,properties,indent=0,configprops] +---- + management.metrics.export.stackdriver.project-id=my-project +---- + +You can also change the interval at which metrics are sent to Stackdriver: + +[source,properties,indent=0,configprops] +---- + management.metrics.export.stackdriver.step=30s +---- + + + [[production-ready-metrics-export-statsd]] ==== StatsD The StatsD registry pushes metrics over UDP to a StatsD agent eagerly.