Skip to content

Commit

Permalink
Align Wavefront application tags support with Spring Boot 2.x
Browse files Browse the repository at this point in the history
Update `application-name` and `service-name` `management.wavefront`
properties to align with Spring Boot 2.x support. The properties now
use the wavefront terminology where `application` refers to an a
collection of (micro)`services`.

This commit also adds `cluster-name` and `shard-name` properties that
can be used to add additional items to the `ApplicationTags`.

See gh-32844
  • Loading branch information
oppegard authored and philwebb committed Nov 17, 2022
1 parent a50d1f0 commit 42bb4c0
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 13 deletions.
Expand Up @@ -16,12 +16,18 @@

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

import java.util.Map;

import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.wavefront.WavefrontConfig;
import io.micrometer.wavefront.WavefrontMeterRegistry;

import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
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;
Expand All @@ -42,6 +48,7 @@
* @author Jon Schneider
* @author Artsiom Yudovin
* @author Stephane Nicoll
* @author Glenn Oppegard
* @since 2.0.0
*/
@AutoConfiguration(
Expand All @@ -68,4 +75,16 @@ public WavefrontMeterRegistry wavefrontMeterRegistry(WavefrontConfig wavefrontCo
return WavefrontMeterRegistry.builder(wavefrontConfig).clock(clock).wavefrontSender(wavefrontSender).build();
}

@Bean
@ConditionalOnBean(ApplicationTags.class)
MeterRegistryCustomizer<WavefrontMeterRegistry> applicationTagsCustomizer(ApplicationTags applicationTags) {
Tags commonTags = Tags.of(applicationTags.toPointTags().entrySet().stream()
.map(WavefrontMetricsExportAutoConfiguration::asTag).toList());
return (registry) -> registry.config().commonTags(commonTags);
}

private static Tag asTag(Map.Entry<String, String> entry) {
return Tag.of(entry.getKey(), entry.getValue());
}

}
Expand Up @@ -49,6 +49,7 @@
* {@link EnableAutoConfiguration Auto-configuration} for Wavefront tracing.
*
* @author Moritz Halbritter
* @author Glenn Oppegard
* @since 3.0.0
*/
@AutoConfiguration(after = { MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class })
Expand All @@ -59,19 +60,40 @@
public class WavefrontTracingAutoConfiguration {

/**
* Default value for application name if {@code spring.application.name} is not set.
* 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 = "application";
private static final String DEFAULT_WAVEFRONT_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_WAVEFRONT_SERVICE_NAME = "unnamed_service";

@Bean
@ConditionalOnMissingBean
public ApplicationTags applicationTags(Environment environment, WavefrontProperties properties) {
String springApplicationName = environment.getProperty("spring.application.name", DEFAULT_APPLICATION_NAME);
String fallbackWavefrontServiceName = environment.getProperty("spring.application.name",
DEFAULT_WAVEFRONT_SERVICE_NAME);
Tracing tracing = properties.getTracing();
String applicationName = (tracing.getApplicationName() != null) ? tracing.getApplicationName()
: springApplicationName;
String serviceName = (tracing.getServiceName() != null) ? tracing.getServiceName() : springApplicationName;
return new ApplicationTags.Builder(applicationName, serviceName).build();
String wavefrontServiceName = (tracing.getServiceName() != null) ? tracing.getServiceName()
: fallbackWavefrontServiceName;
String wavefrontApplicationName = (tracing.getApplicationName() != null) ? tracing.getApplicationName()
: DEFAULT_WAVEFRONT_APPLICATION_NAME;
ApplicationTags.Builder builder = new ApplicationTags.Builder(wavefrontApplicationName, wavefrontServiceName);
if (tracing.getClusterName() != null) {
builder.cluster(tracing.getClusterName());
}
if (tracing.getShardName() != null) {
builder.shard(tracing.getShardName());
}
return builder.build();
}

@Configuration(proxyBeanMethods = false)
Expand Down
Expand Up @@ -21,6 +21,8 @@
import java.net.UnknownHostException;
import java.time.Duration;

import com.wavefront.sdk.common.application.ApplicationTags;

import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PushRegistryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
Expand All @@ -30,6 +32,7 @@
* Configuration properties to configure Wavefront.
*
* @author Moritz Halbritter
* @author Glenn Oppegard
* @since 3.0.0
*/
@ConfigurationProperties(prefix = "management.wavefront")
Expand Down Expand Up @@ -261,15 +264,40 @@ public void setBatchSize(Integer batchSize) {
public static class Tracing {

/**
* Application name. Defaults to 'spring.application.name'.
* Wavefront Application name used in {@link ApplicationTags}. Defaults to
* 'unnamed_application'.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private String applicationName;

/**
* Service name. Defaults to 'spring.application.name'.
* Wavefront Service name used in {@link ApplicationTags}, falling back to
* {@code spring.application.name}. If both are unset it defaults to
* 'unnamed_service'.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private String serviceName;

/**
* Optional Wavefront Cluster name used in {@link ApplicationTags}.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private String clusterName;

/**
* Optional Wavefront Shard name used in {@link ApplicationTags}.
* @see <a href=
* "https://docs.wavefront.com/trace_data_details.html#application-tags">Wavefront
* Application Tags</a>
*/
private String shardName;

public String getServiceName() {
return this.serviceName;
}
Expand All @@ -286,6 +314,22 @@ 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;
}

}

}
Expand Up @@ -16,12 +16,16 @@

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

import java.util.Map;

import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import io.micrometer.core.instrument.Clock;
import io.micrometer.wavefront.WavefrontConfig;
import io.micrometer.wavefront.WavefrontMeterRegistry;
import org.junit.jupiter.api.Test;

import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
Expand All @@ -36,6 +40,7 @@
*
* @author Jon Schneider
* @author Stephane Nicoll
* @author Glenn Oppegard
*/
class WavefrontMetricsExportAutoConfigurationTests {

Expand Down Expand Up @@ -81,6 +86,24 @@ void allowsRegistryToBeCustomized() {
.hasSingleBean(WavefrontMeterRegistry.class).hasBean("customRegistry"));
}

@Test
void exportsApplicationTagsInWavefrontRegistry() {
ApplicationTags.Builder appTagsBuilder = new ApplicationTags.Builder("super-application", "super-service");
appTagsBuilder.cluster("super-cluster");
appTagsBuilder.shard("super-shard");
appTagsBuilder.customTags(Map.of("custom-key", "custom-val"));

this.contextRunner.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.withUserConfiguration(BaseConfiguration.class).withBean(ApplicationTags.class, appTagsBuilder::build)
.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").tags("custom-key", "custom-val").counter()).isNotNull();
});
}

@Test
void stopsMeterRegistryWhenContextIsClosed() {
this.contextRunner.withUserConfiguration(BaseConfiguration.class)
Expand Down
Expand Up @@ -39,6 +39,7 @@
* Tests for {@link WavefrontTracingAutoConfiguration}.
*
* @author Moritz Halbritter
* @author Glenn Oppegard
*/
class WavefrontTracingAutoConfigurationTests {

Expand Down Expand Up @@ -114,22 +115,40 @@ void shouldNotSupplyWavefrontOtelSpanExporterIfOtelIsMissing() {
}

@Test
void shouldHaveADefaultApplicationName() {
void shouldHaveADefaultApplicationNameAndServiceName() {
this.contextRunner.withUserConfiguration(WavefrontSenderConfiguration.class).run((context) -> {
ApplicationTags applicationTags = context.getBean(ApplicationTags.class);
assertThat(applicationTags.getApplication()).isEqualTo("application");
assertThat(applicationTags.getApplication()).isEqualTo("unnamed_application");
assertThat(applicationTags.getService()).isEqualTo("unnamed_service");
assertThat(applicationTags.getCluster()).isNull();
assertThat(applicationTags.getShard()).isNull();
});
}

@Test
void shouldUseSpringApplicationNameForServiceName() {
this.contextRunner.withUserConfiguration(WavefrontSenderConfiguration.class)
.withPropertyValues("spring.application.name=super-service").run((context) -> {
ApplicationTags applicationTags = context.getBean(ApplicationTags.class);
assertThat(applicationTags.getApplication()).isEqualTo("unnamed_application");
assertThat(applicationTags.getService()).isEqualTo("super-service");
});
}

@Test
void shouldHonorConfigProperties() {
this.contextRunner.withUserConfiguration(WavefrontSenderConfiguration.class)
.withPropertyValues("spring.application.name=super-application",
"management.wavefront.tracing.service-name=super-service")
.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) -> {
ApplicationTags applicationTags = context.getBean(ApplicationTags.class);
assertThat(applicationTags.getApplication()).isEqualTo("super-application");
assertThat(applicationTags.getService()).isEqualTo("super-service");
assertThat(applicationTags.getCluster()).isEqualTo("super-cluster");
assertThat(applicationTags.getShard()).isEqualTo("super-shard");
});
}

Expand Down

0 comments on commit 42bb4c0

Please sign in to comment.