Skip to content

Commit ccba876

Browse files
grafjosnicoll
authored andcommitted
Add Stackdriver metrics export support
See spring-projectsgh-19528
1 parent 30c124f commit ccba876

File tree

8 files changed

+406
-0
lines changed

8 files changed

+406
-0
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@
179179
<artifactId>micrometer-registry-statsd</artifactId>
180180
<optional>true</optional>
181181
</dependency>
182+
<dependency>
183+
<groupId>io.micrometer</groupId>
184+
<artifactId>micrometer-registry-stackdriver</artifactId>
185+
<optional>true</optional>
186+
</dependency>
182187
<dependency>
183188
<groupId>io.micrometer</groupId>
184189
<artifactId>micrometer-registry-wavefront</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver;
18+
19+
import io.micrometer.core.instrument.Clock;
20+
import io.micrometer.stackdriver.StackdriverConfig;
21+
import io.micrometer.stackdriver.StackdriverMeterRegistry;
22+
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
23+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
24+
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
25+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
26+
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
27+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
29+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
31+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
32+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
33+
import org.springframework.context.annotation.Bean;
34+
import org.springframework.context.annotation.Configuration;
35+
36+
/**
37+
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Stackdriver.
38+
*
39+
* @author Johannes Graf
40+
*/
41+
@Configuration(proxyBeanMethods = false)
42+
@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class })
43+
@AutoConfigureAfter(MetricsAutoConfiguration.class)
44+
@ConditionalOnBean(Clock.class)
45+
@ConditionalOnClass(StackdriverMeterRegistry.class)
46+
@ConditionalOnProperty(prefix = "management.metrics.export.stackdriver", name = "enabled", havingValue = "true",
47+
matchIfMissing = true
48+
)
49+
@EnableConfigurationProperties(StackdriverProperties.class)
50+
public class StackdriverMetricsExportAutoConfiguration {
51+
private final StackdriverProperties properties;
52+
53+
public StackdriverMetricsExportAutoConfiguration(StackdriverProperties stackdriverProperties) {
54+
this.properties = stackdriverProperties;
55+
}
56+
57+
@Bean
58+
@ConditionalOnMissingBean
59+
public StackdriverConfig stackdriverConfig() {
60+
return new StackdriverPropertiesConfigAdapter(this.properties);
61+
}
62+
63+
@Bean
64+
@ConditionalOnMissingBean
65+
public StackdriverMeterRegistry StackdriverMeterRegistry(StackdriverConfig stackdriverConfig, Clock clock) {
66+
return StackdriverMeterRegistry.builder(stackdriverConfig)
67+
.clock(clock)
68+
.build();
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver;
18+
19+
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryProperties;
20+
import org.springframework.boot.context.properties.ConfigurationProperties;
21+
22+
23+
/**
24+
* {@link ConfigurationProperties @ConfigurationProperties} for configuring Stackdriver metrics
25+
* export.
26+
*
27+
* @author Johannes Graf
28+
*/
29+
@ConfigurationProperties(prefix = "management.metrics.export.stackdriver")
30+
public class StackdriverProperties extends StepRegistryProperties {
31+
32+
/**
33+
* The ID of your google cloud platform project
34+
*/
35+
private String projectId;
36+
37+
/**
38+
* The resource type of the metrics
39+
*/
40+
private String resourceType = "global";
41+
42+
public String getProjectId() {
43+
return projectId;
44+
}
45+
46+
public void setProjectId(String projectId) {
47+
this.projectId = projectId;
48+
}
49+
50+
public String getResourceType() {
51+
return resourceType;
52+
}
53+
54+
public void setResourceType(String resourceType) {
55+
this.resourceType = resourceType;
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver;
18+
19+
import io.micrometer.stackdriver.StackdriverConfig;
20+
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.StepRegistryPropertiesConfigAdapter;
21+
22+
/**
23+
* Adapter to convert {@link StackdriverProperties} to a {@link StackdriverConfig}.
24+
*
25+
* @author Johannes Graf
26+
*/
27+
public class StackdriverPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter<StackdriverProperties> implements StackdriverConfig {
28+
29+
public StackdriverPropertiesConfigAdapter(StackdriverProperties properties) {
30+
super(properties);
31+
}
32+
33+
@Override
34+
public String projectId() {
35+
return get(StackdriverProperties::getProjectId, StackdriverConfig.super::projectId);
36+
}
37+
38+
@Override
39+
public String resourceType() {
40+
return get(StackdriverProperties::getResourceType, StackdriverConfig.super::resourceType);
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* Support for exporting actuator metrics to Stackdriver.
19+
*/
20+
package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver;
18+
19+
import io.micrometer.core.instrument.Clock;
20+
import io.micrometer.stackdriver.StackdriverConfig;
21+
import io.micrometer.stackdriver.StackdriverMeterRegistry;
22+
import org.junit.jupiter.api.Test;
23+
import org.springframework.boot.autoconfigure.AutoConfigurations;
24+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.context.annotation.Import;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
/**
32+
* Tests for {@link StackdriverMetricsExportAutoConfiguration}.
33+
*
34+
* @author Johannes Graf
35+
*/
36+
class StackdriverMetricsExportAutoConfigurationTest {
37+
38+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
39+
.withConfiguration(AutoConfigurations.of(StackdriverMetricsExportAutoConfiguration.class));
40+
41+
@Test
42+
void backsOffWithoutAClock() {
43+
this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(StackdriverMeterRegistry.class));
44+
}
45+
46+
@Test
47+
void failsWithoutAnProjectId() {
48+
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
49+
.run((context) -> assertThat(context).hasFailed());
50+
}
51+
52+
@Test
53+
void autoConfiguresConfigAndMeterRegistry() {
54+
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
55+
.withPropertyValues("management.metrics.export.stackdriver.project-id=qwert")
56+
.run((context) -> assertThat(context).hasSingleBean(StackdriverMeterRegistry.class)
57+
.hasSingleBean(StackdriverConfig.class));
58+
}
59+
60+
@Test
61+
void autoConfigurationCanBeDisabled() {
62+
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
63+
.withPropertyValues("management.metrics.export.stackdriver.enabled=false")
64+
.run((context) -> assertThat(context).doesNotHaveBean(StackdriverMeterRegistry.class)
65+
.doesNotHaveBean(StackdriverConfig.class));
66+
}
67+
68+
@Test
69+
void allowsCustomConfigToBeUsed() {
70+
this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class).run((context) -> assertThat(context)
71+
.hasSingleBean(StackdriverMeterRegistry.class).hasSingleBean(StackdriverConfig.class).hasBean("customConfig"));
72+
}
73+
74+
@Test
75+
void allowsCustomRegistryToBeUsed() {
76+
this.contextRunner.withUserConfiguration(CustomRegistryConfiguration.class)
77+
.withPropertyValues("management.metrics.export.stackdriver.project-id=qwert")
78+
.run((context) -> assertThat(context).hasSingleBean(StackdriverMeterRegistry.class)
79+
.hasBean("customRegistry").hasSingleBean(StackdriverConfig.class));
80+
}
81+
82+
@Test
83+
void stopsMeterRegistryWhenContextIsClosed() {
84+
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
85+
.withPropertyValues("management.metrics.export.stackdriver.project-id=qwert").run((context) -> {
86+
StackdriverMeterRegistry registry = context.getBean(StackdriverMeterRegistry.class);
87+
assertThat(registry.isClosed()).isFalse();
88+
context.close();
89+
assertThat(registry.isClosed()).isTrue();
90+
});
91+
}
92+
93+
@Configuration(proxyBeanMethods = false)
94+
static class BaseConfiguration {
95+
96+
@Bean
97+
Clock clock() {
98+
return Clock.SYSTEM;
99+
}
100+
101+
}
102+
103+
@Configuration(proxyBeanMethods = false)
104+
@Import(BaseConfiguration.class)
105+
static class CustomConfigConfiguration {
106+
107+
@Bean
108+
StackdriverConfig customConfig() {
109+
return (key) -> {
110+
if ("stackdriver.projectId".equals(key)) {
111+
return "qwert";
112+
}
113+
return null;
114+
};
115+
}
116+
117+
}
118+
119+
@Configuration(proxyBeanMethods = false)
120+
@Import(BaseConfiguration.class)
121+
static class CustomRegistryConfiguration {
122+
123+
@Bean
124+
StackdriverMeterRegistry customRegistry(StackdriverConfig config, Clock clock) {
125+
return new StackdriverMeterRegistry(config, clock);
126+
}
127+
128+
}
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.actuate.autoconfigure.metrics.export.stackdriver;
18+
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
24+
/**
25+
* Tests for {@link StackdriverPropertiesConfigAdapter}.
26+
*
27+
* @author Johannes Graf
28+
*/
29+
class StackdriverPropertiesConfigAdapterTest {
30+
31+
@Test
32+
void whenPropertiesProjectIdIsSetAdapterProjectIdReturnsIt() {
33+
StackdriverProperties properties = new StackdriverProperties();
34+
properties.setProjectId("my-gcp-project-id");
35+
assertThat(new StackdriverPropertiesConfigAdapter(properties).projectId()).isEqualTo("my-gcp-project-id");
36+
}
37+
@Test
38+
void whenPropertiesResourceTypeIsSetAdapterResourceTypeReturnsIt() {
39+
StackdriverProperties properties = new StackdriverProperties();
40+
properties.setResourceType("my-resource-type");
41+
assertThat(new StackdriverPropertiesConfigAdapter(properties).resourceType()).isEqualTo("my-resource-type");
42+
}
43+
}

0 commit comments

Comments
 (0)