Create ApplicationTags in common WavefrontAutoConfiguration

Relocate `ApplicationTags` `@Bean` method from
`WavefrontTracingAutoConfiguration` to `WavefrontAutoConfiguration`
since it is now shared between tracing and metrics.

The `application-name`, `service-name`, `cluster-name` and `shard-name`
have also been relocated from `management.wavefront.tracing` to
`management.wavefront`.

Fixes gh-33244
pull/33251/head
Phillip Webb 2 years ago
parent fafbefa35c
commit fb5cdbd11c

@ -31,6 +31,7 @@ import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCusto
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport;
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontProperties;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontSenderConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@ -53,7 +54,7 @@ import org.springframework.context.annotation.Import;
*/
@AutoConfiguration(
before = { CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class },
after = MetricsAutoConfiguration.class)
after = { MetricsAutoConfiguration.class, WavefrontAutoConfiguration.class })
@ConditionalOnBean(Clock.class)
@ConditionalOnClass({ WavefrontMeterRegistry.class, WavefrontSender.class })
@ConditionalOnEnabledMetricsExport("wavefront")

@ -17,7 +17,6 @@
package org.springframework.boot.actuate.autoconfigure.tracing.wavefront;
import java.util.Collections;
import java.util.function.Supplier;
import brave.handler.SpanHandler;
import com.wavefront.sdk.common.WavefrontSender;
@ -32,8 +31,8 @@ import io.opentelemetry.sdk.trace.export.SpanExporter;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.tracing.ConditionalOnEnabledTracing;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontProperties;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontProperties.Tracing;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontSenderConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -41,12 +40,9 @@ 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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Wavefront tracing.
@ -55,52 +51,14 @@ import org.springframework.util.StringUtils;
* @author Glenn Oppegard
* @since 3.0.0
*/
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
WavefrontAutoConfiguration.class })
@ConditionalOnClass({ WavefrontSender.class, WavefrontSpanHandler.class })
@ConditionalOnEnabledTracing
@EnableConfigurationProperties(WavefrontProperties.class)
@ConditionalOnClass(WavefrontSender.class)
@Import(WavefrontSenderConfiguration.class)
@ConditionalOnEnabledTracing
public class WavefrontTracingAutoConfiguration {
/**
* Default value for the Wavefront Application name.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private static final String DEFAULT_APPLICATION_NAME = "unnamed_application";
/**
* Default value for the Wavefront Service name if {@code spring.application.name} is
* not set.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private static final String DEFAULT_SERVICE_NAME = "unnamed_service";
@Bean
@ConditionalOnMissingBean
public ApplicationTags wavefrontApplicationTags(Environment environment, WavefrontProperties properties) {
Tracing tracing = properties.getTracing();
String wavefrontServiceName = getName(tracing.getServiceName(),
() -> environment.getProperty("spring.application.name", DEFAULT_SERVICE_NAME));
String wavefrontApplicationName = getName(tracing.getApplicationName(), () -> DEFAULT_APPLICATION_NAME);
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
ApplicationTags.Builder builder = new ApplicationTags.Builder(wavefrontApplicationName, wavefrontServiceName);
map.from(tracing::getClusterName).to(builder::cluster);
map.from(tracing::getShardName).to(builder::shard);
return builder.build();
}
private String getName(String value, Supplier<String> fallback) {
return (StringUtils.hasText(value)) ? value : fallback.get();
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WavefrontSpanHandler.class)
static class WavefrontMicrometer {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(WavefrontSender.class)
@ -159,5 +117,3 @@ public class WavefrontTracingAutoConfiguration {
}
}
}

@ -0,0 +1,82 @@
/*
* Copyright 2012-2022 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.wavefront;
import java.util.function.Supplier;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for for Wavefront common
* infrastructure.
*
* @author Moritz Halbritter
* @author Glenn Oppegard
* @author Phillip Webb
* @since 3.0.0
*/
@AutoConfiguration
@ConditionalOnClass({ ApplicationTags.class, WavefrontSender.class })
@EnableConfigurationProperties(WavefrontProperties.class)
public class WavefrontAutoConfiguration {
/**
* Default value for the Wavefront Service name if {@code spring.application.name} is
* not set.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private static final String DEFAULT_SERVICE_NAME = "unnamed_service";
/**
* Default value for the Wavefront Application name.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private static final String DEFAULT_APPLICATION_NAME = "unnamed_application";
@Bean
@ConditionalOnMissingBean
public ApplicationTags wavefrontApplicationTags(Environment environment, WavefrontProperties properties) {
String wavefrontServiceName = getName(properties.getServiceName(),
() -> environment.getProperty("spring.application.name", DEFAULT_SERVICE_NAME));
String wavefrontApplicationName = getName(properties.getApplicationName(), () -> DEFAULT_APPLICATION_NAME);
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
ApplicationTags.Builder builder = new ApplicationTags.Builder(wavefrontApplicationName, wavefrontServiceName);
map.from(properties::getClusterName).to(builder::cluster);
map.from(properties::getShardName).to(builder::shard);
return builder.build();
}
private String getName(String value, Supplier<String> fallback) {
return (StringUtils.hasText(value)) ? value : fallback.get();
}
}

@ -53,6 +53,28 @@ public class WavefrontProperties {
*/
private String apiToken;
/**
* Wavefront Application name used in ApplicationTags. Defaults to
* 'unnamed_application'.
*/
private String applicationName;
/**
* Wavefront Service name used in ApplicationTags, falling back to
* 'spring.application.name'. If both are unset it defaults to 'unnamed_service'.
*/
private String serviceName;
/**
* Optional Wavefront Cluster name used in ApplicationTags.
*/
private String clusterName;
/**
* Optional Wavefront Shard name used in ApplicationTags.
*/
private String shardName;
/**
* Sender configuration.
*/
@ -63,11 +85,6 @@ public class WavefrontProperties {
*/
private final Metrics metrics = new Metrics();
/**
* Tracing configuration.
*/
private final Tracing tracing = new Tracing();
public Sender getSender() {
return this.sender;
}
@ -76,10 +93,6 @@ public class WavefrontProperties {
return this.metrics;
}
public Tracing getTracing() {
return this.tracing;
}
public URI getUri() {
return this.uri;
}
@ -104,6 +117,38 @@ public class WavefrontProperties {
this.apiToken = apiToken;
}
public String getServiceName() {
return this.serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getApplicationName() {
return this.applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String getClusterName() {
return this.clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getShardName() {
return this.shardName;
}
public void setShardName(String shardName) {
this.shardName = shardName;
}
/**
* Returns the effective URI of the wavefront instance. This will not be the same URI
* given through {@link #setUri(URI)} when a proxy is used.
@ -259,62 +304,4 @@ public class WavefrontProperties {
}
public static class Tracing {
/**
* Wavefront Application name used in ApplicationTags. Defaults to
* 'unnamed_application'.
*/
private String applicationName;
/**
* Wavefront Service name used in ApplicationTags, falling back to
* 'spring.application.name'. If both are unset it defaults to 'unnamed_service'.
*/
private String serviceName;
/**
* Optional Wavefront Cluster name used in ApplicationTags.
*/
private String clusterName;
/**
* Optional Wavefront Shard name used in ApplicationTags.
*/
private String shardName;
public String getServiceName() {
return this.serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getApplicationName() {
return this.applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String getClusterName() {
return this.clusterName;
}
public void setClusterName(String clusterName) {
this.clusterName = clusterName;
}
public String getShardName() {
return this.shardName;
}
public void setShardName(String shardName) {
this.shardName = shardName;
}
}
}

@ -32,7 +32,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;
/**
* Configuration for Wavefront common infrastructure. This configuration is imported from
* Configuration for {@link WavefrontSender}. This configuration is imported from
* {@link WavefrontMetricsExportAutoConfiguration} and
* {@link WavefrontTracingAutoConfiguration}.
*

@ -103,6 +103,7 @@ org.springframework.boot.actuate.autoconfigure.tracing.OpenTelemetryAutoConfigur
org.springframework.boot.actuate.autoconfigure.tracing.prometheus.PrometheusExemplarsAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.wavefront.WavefrontTracingAutoConfiguration
org.springframework.boot.actuate.autoconfigure.tracing.zipkin.ZipkinAutoConfiguration
org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration

@ -26,6 +26,7 @@ import io.micrometer.wavefront.WavefrontMeterRegistry;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@ -44,8 +45,8 @@ import static org.mockito.Mockito.mock;
*/
class WavefrontMetricsExportAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(WavefrontMetricsExportAutoConfiguration.class));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
AutoConfigurations.of(WavefrontAutoConfiguration.class, WavefrontMetricsExportAutoConfiguration.class));
@Test
void backsOffWithoutAClock() {
@ -87,7 +88,7 @@ class WavefrontMetricsExportAutoConfigurationTests {
}
@Test
void exportsApplicationTagsInWavefrontRegistry() {
void exportsApplicationTagsInWavefrontRegistryWhenApplicationTagsBean() {
ApplicationTags.Builder builder = new ApplicationTags.Builder("super-application", "super-service");
builder.cluster("super-cluster");
builder.shard("super-shard");
@ -103,6 +104,22 @@ class WavefrontMetricsExportAutoConfigurationTests {
});
}
@Test
void exportsApplicationTagsInWavefrontRegistryWhenInProperties() {
this.contextRunner.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.withPropertyValues("management.wavefront.service-name=super-service",
"management.wavefront.application-name=super-application",
"management.wavefront.cluster-name=super-cluster",
"management.wavefront.shard-name=super-shard")
.withUserConfiguration(BaseConfiguration.class).run((context) -> {
WavefrontMeterRegistry registry = context.getBean(WavefrontMeterRegistry.class);
registry.counter("my.counter", "env", "qa");
assertThat(registry.find("my.counter").tags("env", "qa").tags("application", "super-application")
.tags("service", "super-service").tags("cluster", "super-cluster")
.tags("shard", "super-shard").counter()).isNotNull();
});
}
@Test
void stopsMeterRegistryWhenContextIsClosed() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)

@ -26,6 +26,7 @@ import io.micrometer.tracing.reporter.wavefront.WavefrontOtelSpanExporter;
import io.micrometer.tracing.reporter.wavefront.WavefrontSpanHandler;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@ -43,8 +44,8 @@ import static org.mockito.Mockito.mock;
*/
class WavefrontTracingAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(WavefrontTracingAutoConfiguration.class));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
AutoConfigurations.of(WavefrontAutoConfiguration.class, WavefrontTracingAutoConfiguration.class));
@Test
void shouldSupplyBeans() {
@ -83,7 +84,6 @@ class WavefrontTracingAutoConfigurationTests {
void shouldNotSupplyBeansIfTracingIsDisabled() {
this.contextRunner.withPropertyValues("management.tracing.enabled=false")
.withUserConfiguration(WavefrontSenderConfiguration.class).run((context) -> {
assertThat(context).doesNotHaveBean(ApplicationTags.class);
assertThat(context).doesNotHaveBean(WavefrontSpanHandler.class);
assertThat(context).doesNotHaveBean(SpanMetrics.class);
assertThat(context).doesNotHaveBean(WavefrontBraveSpanHandler.class);
@ -137,13 +137,10 @@ class WavefrontTracingAutoConfigurationTests {
@Test
void shouldHonorConfigProperties() {
this.contextRunner.withUserConfiguration(WavefrontSenderConfiguration.class)
.withPropertyValues("spring.application.name=ignored",
"management.wavefront.tracing.application-name=super-application",
"management.wavefront.tracing.service-name=super-service",
"management.wavefront.tracing.cluster-name=super-cluster",
"management.wavefront.tracing.shard-name=super-shard")
.run((context) -> {
this.contextRunner.withUserConfiguration(WavefrontSenderConfiguration.class).withPropertyValues(
"spring.application.name=ignored", "management.wavefront.application-name=super-application",
"management.wavefront.service-name=super-service", "management.wavefront.cluster-name=super-cluster",
"management.wavefront.shard-name=super-shard").run((context) -> {
ApplicationTags applicationTags = context.getBean(ApplicationTags.class);
assertThat(applicationTags.getApplication()).isEqualTo("super-application");
assertThat(applicationTags.getService()).isEqualTo("super-service");

@ -0,0 +1,96 @@
/*
* Copyright 2012-2022 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.wavefront;
import java.util.ArrayList;
import java.util.List;
import com.wavefront.sdk.common.application.ApplicationTags;
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 static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link WavefrontAutoConfiguration}.
*
* @author Phillip Webb
*/
class WavefrontAutoConfigurationTests {
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(WavefrontAutoConfiguration.class));
@Test
void wavefrontApplicationTagsWhenHasUserBeanBacksOff() {
this.contextRunner.withUserConfiguration(TestApplicationTagsConfiguration.class).run((context) -> {
ApplicationTags tags = context.getBean(ApplicationTags.class);
assertThat(tags.getApplication()).isEqualTo("test-application");
assertThat(tags.getService()).isEqualTo("test-service");
});
}
@Test
void wavefrontApplicationTagsMapsProperties() {
List<String> properties = new ArrayList<>();
properties.add("management.wavefront.application-name=test-application");
properties.add("management.wavefront.service-name=test-service");
properties.add("management.wavefront.cluster-name=test-cluster");
properties.add("management.wavefront.shard-name=test-shard");
this.contextRunner.withPropertyValues(properties.toArray(String[]::new)).run((context) -> {
ApplicationTags tags = context.getBean(ApplicationTags.class);
assertThat(tags.getApplication()).isEqualTo("test-application");
assertThat(tags.getService()).isEqualTo("test-service");
assertThat(tags.getCluster()).isEqualTo("test-cluster");
assertThat(tags.getShard()).isEqualTo("test-shard");
});
}
@Test
void wavefrontApplicationTagsWhenNoPropertiesUsesDefaults() {
this.contextRunner.withPropertyValues("spring.application.name=spring-app").run((context) -> {
ApplicationTags tags = context.getBean(ApplicationTags.class);
assertThat(tags.getApplication()).isEqualTo("unnamed_application");
assertThat(tags.getService()).isEqualTo("spring-app");
assertThat(tags.getCluster()).isNull();
assertThat(tags.getShard()).isNull();
});
}
@Test
void wavefrontApplicationTagsWhenHasNoServiceNamePropertyAndNoSpringApplicationNameUsesDefault() {
this.contextRunner.run((context) -> {
ApplicationTags tags = context.getBean(ApplicationTags.class);
assertThat(tags.getService()).isEqualTo("unnamed_service");
});
}
@Configuration(proxyBeanMethods = false)
static class TestApplicationTagsConfiguration {
@Bean
ApplicationTags applicationTags() {
return new ApplicationTags.Builder("test-application", "test-service").build();
}
}
}
Loading…
Cancel
Save