Skip to content

Commit 6764e5e

Browse files
committed
Export metrics to Wavefront using WavefrontSender
This commit upgrades the Wavefront metrics export auto-configuration to provide a `WavefrontSender` if necessary and use that to export metrics rather than the http client Micrometer used previously. As a result, the "read-timeout" and "connect-timeout" properties are no longer honoured. Closes gh-20810
1 parent 9543ab1 commit 6764e5e

File tree

4 files changed

+156
-12
lines changed

4 files changed

+156
-12
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/wavefront/WavefrontMetricsExportAutoConfiguration.java

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,14 +16,19 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront;
1818

19+
import java.time.Duration;
20+
21+
import com.wavefront.sdk.common.WavefrontSender;
22+
import com.wavefront.sdk.direct.ingestion.WavefrontDirectIngestionClient.Builder;
1923
import io.micrometer.core.instrument.Clock;
20-
import io.micrometer.core.ipc.http.HttpUrlConnectionSender;
24+
import io.micrometer.core.instrument.config.MissingRequiredConfigurationException;
2125
import io.micrometer.wavefront.WavefrontConfig;
2226
import io.micrometer.wavefront.WavefrontMeterRegistry;
2327

2428
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
2529
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
2630
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
31+
import org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontProperties.Sender;
2732
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2833
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2934
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -32,21 +37,25 @@
3237
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3338
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3439
import org.springframework.boot.context.properties.EnableConfigurationProperties;
40+
import org.springframework.boot.context.properties.PropertyMapper;
3541
import org.springframework.context.annotation.Bean;
3642
import org.springframework.context.annotation.Configuration;
43+
import org.springframework.util.StringUtils;
44+
import org.springframework.util.unit.DataSize;
3745

3846
/**
3947
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Wavefront.
4048
*
4149
* @author Jon Schneider
4250
* @author Artsiom Yudovin
51+
* @author Stephane Nicoll
4352
* @since 2.0.0
4453
*/
4554
@Configuration(proxyBeanMethods = false)
4655
@AutoConfigureBefore({ CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class })
4756
@AutoConfigureAfter(MetricsAutoConfiguration.class)
4857
@ConditionalOnBean(Clock.class)
49-
@ConditionalOnClass(WavefrontMeterRegistry.class)
58+
@ConditionalOnClass({ WavefrontMeterRegistry.class, WavefrontSender.class })
5059
@ConditionalOnProperty(prefix = "management.metrics.export.wavefront", name = "enabled", havingValue = "true",
5160
matchIfMissing = true)
5261
@EnableConfigurationProperties(WavefrontProperties.class)
@@ -66,10 +75,38 @@ public WavefrontConfig wavefrontConfig() {
6675

6776
@Bean
6877
@ConditionalOnMissingBean
69-
public WavefrontMeterRegistry wavefrontMeterRegistry(WavefrontConfig wavefrontConfig, Clock clock) {
70-
return WavefrontMeterRegistry.builder(wavefrontConfig).clock(clock).httpClient(
71-
new HttpUrlConnectionSender(this.properties.getConnectTimeout(), this.properties.getReadTimeout()))
72-
.build();
78+
public WavefrontSender wavefrontSender(WavefrontConfig wavefrontConfig) {
79+
if (!StringUtils.hasText(wavefrontConfig.apiToken())) {
80+
throw new MissingRequiredConfigurationException(
81+
"apiToken must be set whenever publishing directly to the Wavefront API");
82+
}
83+
return createWavefrontSender(wavefrontConfig);
84+
}
85+
86+
@Bean
87+
@ConditionalOnMissingBean
88+
public WavefrontMeterRegistry wavefrontMeterRegistry(WavefrontConfig wavefrontConfig, Clock clock,
89+
WavefrontSender wavefrontSender) {
90+
return WavefrontMeterRegistry.builder(wavefrontConfig).clock(clock).wavefrontSender(wavefrontSender).build();
91+
}
92+
93+
private WavefrontSender createWavefrontSender(WavefrontConfig wavefrontConfig) {
94+
Builder builder = new Builder(getWavefrontReportingUri(wavefrontConfig.uri()), wavefrontConfig.apiToken());
95+
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
96+
Sender sender = this.properties.getSender();
97+
mapper.from(sender.getMaxQueueSize()).to(builder::maxQueueSize);
98+
mapper.from(sender.getBatchSize()).to(builder::batchSize);
99+
mapper.from(sender.getFlushInterval()).asInt(Duration::getSeconds).to(builder::flushIntervalSeconds);
100+
mapper.from(sender.getMessageSize()).asInt(DataSize::toBytes).to(builder::messageSizeBytes);
101+
return builder.build();
102+
}
103+
104+
private String getWavefrontReportingUri(String uri) {
105+
// proxy reporting is now http reporting on newer wavefront proxies.
106+
if (uri.startsWith("proxy")) {
107+
return "http" + uri.substring(5);
108+
}
109+
return uri;
73110
}
74111

75112
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/wavefront/WavefrontProperties.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront;
1818

1919
import java.net.URI;
20+
import java.time.Duration;
2021

2122
import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryProperties;
2223
import org.springframework.boot.context.properties.ConfigurationProperties;
24+
import org.springframework.util.unit.DataSize;
2325

2426
/**
2527
* {@link ConfigurationProperties @ConfigurationProperties} for configuring Wavefront
2628
* metrics export.
2729
*
2830
* @author Jon Schneider
31+
* @author Stephane Nicoll
2932
* @since 2.0.0
3033
*/
3134
@ConfigurationProperties("management.metrics.export.wavefront")
@@ -54,6 +57,8 @@ public class WavefrontProperties extends PushRegistryProperties {
5457
*/
5558
private String globalPrefix;
5659

60+
private final Sender sender = new Sender();
61+
5762
public URI getUri() {
5863
return this.uri;
5964
}
@@ -86,4 +91,52 @@ public void setGlobalPrefix(String globalPrefix) {
8691
this.globalPrefix = globalPrefix;
8792
}
8893

94+
public Sender getSender() {
95+
return this.sender;
96+
}
97+
98+
public static class Sender {
99+
100+
private int maxQueueSize = 50000;
101+
102+
private int batchSize = 10000;
103+
104+
private Duration flushInterval = Duration.ofSeconds(1);
105+
106+
private DataSize messageSize = DataSize.ofBytes(Integer.MAX_VALUE);
107+
108+
public int getMaxQueueSize() {
109+
return this.maxQueueSize;
110+
}
111+
112+
public void setMaxQueueSize(int maxQueueSize) {
113+
this.maxQueueSize = maxQueueSize;
114+
}
115+
116+
public int getBatchSize() {
117+
return this.batchSize;
118+
}
119+
120+
public void setBatchSize(int batchSize) {
121+
this.batchSize = batchSize;
122+
}
123+
124+
public Duration getFlushInterval() {
125+
return this.flushInterval;
126+
}
127+
128+
public void setFlushInterval(Duration flushInterval) {
129+
this.flushInterval = flushInterval;
130+
}
131+
132+
public DataSize getMessageSize() {
133+
return this.messageSize;
134+
}
135+
136+
public void setMessageSize(DataSize messageSize) {
137+
this.messageSize = messageSize;
138+
}
139+
140+
}
141+
89142
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,18 @@
380380
"level": "error"
381381
}
382382
},
383+
{
384+
"name": "management.metrics.export.wavefront.connect-timeout",
385+
"deprecation": {
386+
"level": "error"
387+
}
388+
},
389+
{
390+
"name": "management.metrics.export.wavefront.read-timeout",
391+
"deprecation": {
392+
"level": "error"
393+
}
394+
},
383395
{
384396
"name": "management.metrics.web.client.request.autotime.enabled",
385397
"description": "Whether to automatically time web client requests.",

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/export/wavefront/WavefrontMetricsExportAutoConfigurationTests.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront;
1818

19+
import com.wavefront.sdk.common.WavefrontSender;
1920
import io.micrometer.core.instrument.Clock;
2021
import io.micrometer.wavefront.WavefrontConfig;
2122
import io.micrometer.wavefront.WavefrontMeterRegistry;
@@ -28,6 +29,7 @@
2829
import org.springframework.context.annotation.Import;
2930

3031
import static org.assertj.core.api.Assertions.assertThat;
32+
import static org.mockito.Mockito.mock;
3133

3234
/**
3335
* Tests for {@link WavefrontMetricsExportAutoConfiguration}.
@@ -53,17 +55,41 @@ void failsWithoutAnApiTokenWhenPublishingDirectly() {
5355
@Test
5456
void autoConfigurationCanBeDisabled() {
5557
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
56-
.withPropertyValues("management.metrics.export.wavefront.enabled=false")
58+
.withPropertyValues("management.metrics.export.wavefront.api-token=abcde",
59+
"management.metrics.export.wavefront.enabled=false")
5760
.run((context) -> assertThat(context).doesNotHaveBean(WavefrontMeterRegistry.class)
58-
.doesNotHaveBean(WavefrontConfig.class));
61+
.doesNotHaveBean(WavefrontConfig.class).doesNotHaveBean(WavefrontSender.class));
5962
}
6063

6164
@Test
6265
void allowsConfigToBeCustomized() {
6366
this.contextRunner.withUserConfiguration(CustomConfigConfiguration.class)
6467
.run((context) -> assertThat(context).hasSingleBean(Clock.class)
6568
.hasSingleBean(WavefrontMeterRegistry.class).hasSingleBean(WavefrontConfig.class)
66-
.hasBean("customConfig"));
69+
.hasSingleBean(WavefrontSender.class).hasBean("customConfig"));
70+
}
71+
72+
@Test
73+
void configureWavefrontSender() {
74+
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
75+
.withPropertyValues("management.metrics.export.wavefront.api-token=abcde",
76+
"management.metrics.export.wavefront.sender.max-queue-size=100",
77+
"management.metrics.export.wavefront.sender.batch-size=200",
78+
"management.metrics.export.wavefront.sender.message-size=1KB")
79+
.run((context) -> {
80+
WavefrontSender sender = context.getBean(WavefrontSender.class);
81+
assertThat(sender).extracting("metricsBuffer").hasFieldOrPropertyWithValue("capacity", 100);
82+
assertThat(sender).hasFieldOrPropertyWithValue("batchSize", 200);
83+
assertThat(sender).hasFieldOrPropertyWithValue("messageSizeBytes", 1024);
84+
});
85+
}
86+
87+
@Test
88+
void allowsWavefrontSenderToBeCustomized() {
89+
this.contextRunner.withUserConfiguration(CustomSenderConfiguration.class)
90+
.run((context) -> assertThat(context).hasSingleBean(Clock.class)
91+
.hasSingleBean(WavefrontMeterRegistry.class).hasSingleBean(WavefrontConfig.class)
92+
.hasSingleBean(WavefrontSender.class).hasBean("customSender"));
6793
}
6894

6995
@Test
@@ -109,13 +135,29 @@ public String get(String key) {
109135

110136
@Override
111137
public String uri() {
112-
return WavefrontConfig.DEFAULT_PROXY.uri();
138+
return WavefrontConfig.DEFAULT_DIRECT.uri();
139+
}
140+
141+
@Override
142+
public String apiToken() {
143+
return "abc-def";
113144
}
114145
};
115146
}
116147

117148
}
118149

150+
@Configuration(proxyBeanMethods = false)
151+
@Import(BaseConfiguration.class)
152+
static class CustomSenderConfiguration {
153+
154+
@Bean
155+
WavefrontSender customSender() {
156+
return mock(WavefrontSender.class);
157+
}
158+
159+
}
160+
119161
@Configuration(proxyBeanMethods = false)
120162
@Import(BaseConfiguration.class)
121163
static class CustomRegistryConfiguration {

0 commit comments

Comments
 (0)