Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wavefront application tags differ from those used in a Spring Boot 2.x application #32844

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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