This commit is contained in:
Marcin Grzejszczak
2021-02-15 14:27:38 +01:00
parent a1c136b69f
commit d7b5b5aafe
7 changed files with 120 additions and 185 deletions

View File

@@ -371,11 +371,6 @@
<artifactId>wavefront-opentracing-sdk-java</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.wavefront</groupId>
<artifactId>wavefront-spring-boot</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.wavefront</groupId>
<artifactId>wavefront-runtime-sdk-jvm</artifactId>

View File

@@ -25,8 +25,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Advanced configuration properties for Wavefront.
*
* @author Stephane Nicoll
* @since 3.0.2
*/
@ConfigurationProperties("wavefront.tracing")
@ConfigurationProperties("spring.sleuth.wavefront")
public class WavefrontProperties {
/**

View File

@@ -21,8 +21,6 @@ import brave.TracingCustomizer;
import brave.handler.SpanHandler;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import com.wavefront.spring.autoconfigure.WavefrontAutoConfiguration;
import com.wavefront.spring.autoconfigure.WavefrontTracerBla;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.wavefront.WavefrontConfig;
@@ -34,7 +32,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.sleuth.SpanNamer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -47,24 +44,25 @@ import org.springframework.context.annotation.Configuration;
* @since 3.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ SpanNamer.class, MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
@ConditionalOnBean(WavefrontSender.class)
@AutoConfigureBefore({ BraveAutoConfiguration.class, WavefrontAutoConfiguration.class })
@ConditionalOnClass({ MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
@ConditionalOnBean({ WavefrontSender.class, ApplicationTags.class })
@AutoConfigureAfter(WavefrontMetricsExportAutoConfiguration.class)
@AutoConfigureBefore(BraveAutoConfiguration.class)
@EnableConfigurationProperties(WavefrontProperties.class)
@ConditionalOnProperty(value = "wavefront.tracing.enabled", matchIfMissing = true)
@ConditionalOnProperty(value = { "spring.sleuth.enabled", "spring.sleuth.wavefront.enabled" }, matchIfMissing = true)
public class WavefrontSleuthAutoConfiguration {
static final String BEAN_NAME = "wavefrontTracingCustomizer";
@Bean
@ConditionalOnBean({ MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
@ConditionalOnBean({ MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class, ApplicationTags.class })
WavefrontSleuthSpanHandler wavefrontSleuthSpanHandler(MeterRegistry meterRegistry, WavefrontSender wavefrontSender,
ApplicationTags applicationTags, WavefrontConfig wavefrontConfig, WavefrontProperties wavefrontProperties) {
return new WavefrontSleuthSpanHandler(
// https://github.com/wavefrontHQ/wavefront-opentracing-sdk-java/blob/f1f08d8daf7b692b9b61dcd5bc24ca6befa8e710/src/main/java/com/wavefront/opentracing/reporting/WavefrontSpanReporter.java#L54
50000, // TODO: maxQueueSize should be a property, ya?
wavefrontSender, meterRegistry, wavefrontConfig.source(), applicationTags, wavefrontProperties);
wavefrontSender, meterRegistry, wavefrontConfig.source(), applicationTags,
wavefrontProperties.getRedMetricsCustomTagKeys());
}
@Configuration(proxyBeanMethods = false)
@@ -72,17 +70,10 @@ public class WavefrontSleuthAutoConfiguration {
static class BraveCustomizerConfiguration {
@Bean(BEAN_NAME)
@ConditionalOnMissingBean(name = BEAN_NAME)
@ConditionalOnMissingBean(WavefrontTracingCustomizer.class)
@ConditionalOnBean({ MeterRegistry.class, WavefrontConfig.class, WavefrontSender.class })
TracingCustomizer wavefrontTracingCustomizer(WavefrontSleuthSpanHandler spanHandler) {
return t -> t.traceId128Bit(true).supportsJoin(false)
.addSpanHandler(new WavefrontSleuthBraveSpanHandler(spanHandler));
}
@Bean
WavefrontTracerBla wavefrontTracerBla() {
return new WavefrontTracerBla() {
};
WavefrontTracingCustomizer wavefrontTracingCustomizer(WavefrontSleuthSpanHandler spanHandler) {
return new WavefrontTracingCustomizer(spanHandler);
}
}

View File

@@ -147,7 +147,7 @@ public final class WavefrontSleuthSpanHandler implements Runnable, Closeable {
final ApplicationTags applicationTags;
WavefrontSleuthSpanHandler(int maxQueueSize, WavefrontSender wavefrontSender, MeterRegistry meterRegistry,
String source, ApplicationTags applicationTags, WavefrontProperties wavefrontProperties) {
String source, ApplicationTags applicationTags, Set<String> redMetricsCustomTagKeys) {
this.wavefrontSender = wavefrontSender;
this.applicationTags = applicationTags;
this.discoveredHeartbeatMetrics = Sets.newConcurrentHashSet();
@@ -165,7 +165,7 @@ public final class WavefrontSleuthSpanHandler implements Runnable, Closeable {
}
}, 1, 60, TimeUnit.SECONDS);
this.traceDerivedCustomTagKeys = new HashSet<>(wavefrontProperties.getRedMetricsCustomTagKeys());
this.traceDerivedCustomTagKeys = new HashSet<>(redMetricsCustomTagKeys);
// Start the reporter
wfInternalReporter = new WavefrontInternalReporter.Builder().prefixedWith(TRACING_DERIVED_PREFIX)

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2013-2020 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.cloud.sleuth.autoconfig.wavefront;
import brave.Tracing;
import brave.TracingCustomizer;
/**
* {@link TracingCustomizer} for Wavefront.
*
* @author Marcin Grzejszczak
* @since 3.0.2
*/
public class WavefrontTracingCustomizer implements TracingCustomizer {
private final WavefrontSleuthSpanHandler spanHandler;
public WavefrontTracingCustomizer(WavefrontSleuthSpanHandler spanHandler) {
this.spanHandler = spanHandler;
}
@Override
public void customize(Tracing.Builder builder) {
builder.traceId128Bit(true).supportsJoin(false)
.addSpanHandler(new WavefrontSleuthBraveSpanHandler(spanHandler));
}
}

View File

@@ -21,14 +21,10 @@ import java.util.function.Function;
import java.util.function.Supplier;
import brave.Tracer;
import brave.TracingCustomizer;
import brave.handler.SpanHandler;
import com.wavefront.sdk.appagent.jvm.reporter.WavefrontJvmReporter;
import com.wavefront.sdk.common.Pair;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import com.wavefront.spring.autoconfigure.ApplicationTagsBuilderCustomizer;
import com.wavefront.spring.autoconfigure.WavefrontAutoConfiguration;
import io.micrometer.core.instrument.MeterRegistry;
import org.junit.jupiter.api.Test;
@@ -37,27 +33,31 @@ import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfigu
import org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.export.wavefront.WavefrontMetricsExportAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.AbstractApplicationContextRunner;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.cloud.sleuth.autoconfig.brave.BraveAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WavefrontAutoConfiguration}.
* Tests for {@link WavefrontSleuthAutoConfiguration}.
*
* @author Stephane Nicoll
* @author Tommy Ludwig
*/
class WavefrontAutoConfigurationTests {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
AutoConfigurations.of(WavefrontAutoConfiguration.class, WavefrontSleuthAutoConfiguration.class));
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(WavefrontSleuthAutoConfiguration.class))
.withUserConfiguration(Config.class);
@Test
void applicationTagsIsConfiguredFromPropertiesWhenNoneExists() {
@@ -74,66 +74,6 @@ class WavefrontAutoConfigurationTests {
});
}
@Test
void applicationTagsCanBeCustomized() {
this.contextRunner
.withPropertyValues("wavefront.application.name=test-app", "wavefront.application.service=test-service")
.withBean(ApplicationTagsBuilderCustomizer.class,
() -> (builder) -> builder.cluster("test-cluster").shard("test-shard"))
.run((context) -> {
assertThat(context).hasSingleBean(ApplicationTags.class);
ApplicationTags tags = context.getBean(ApplicationTags.class);
assertThat(tags.getApplication()).isEqualTo("test-app");
assertThat(tags.getService()).isEqualTo("test-service");
assertThat(tags.getCluster()).isEqualTo("test-cluster");
assertThat(tags.getShard()).isEqualTo("test-shard");
assertThat(tags.getCustomTags()).isEmpty();
});
}
@Test
void applicationTagsIsReusedWhenCustomInstanceExists() {
this.contextRunner
.withPropertyValues("wavefront.application.name=test-app", "wavefront.application.service=test-service")
.withBean(ApplicationTags.class,
() -> new ApplicationTags.Builder("another-app", "another-service").build())
.run((context) -> {
assertThat(context).hasSingleBean(ApplicationTags.class);
ApplicationTags tags = context.getBean(ApplicationTags.class);
assertThat(tags.getApplication()).isEqualTo("another-app");
assertThat(tags.getService()).isEqualTo("another-service");
assertThat(tags.getCluster()).isNull();
assertThat(tags.getShard()).isNull();
assertThat(tags.getCustomTags()).isEmpty();
});
}
@Test
void applicationTagsAreExportedToWavefrontRegistry() {
this.contextRunner
.withPropertyValues("wavefront.application.name=test-app", "wavefront.application.service=test-service")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class))).run((context) -> {
MeterRegistry registry = context.getBean(MeterRegistry.class);
registry.counter("my.counter", "env", "qa");
assertThat(registry.find("my.counter").tags("env", "qa").tags("application", "test-app")
.tags("service", "test-service").counter()).isNotNull();
});
}
@Test
void applicationTagsWithFullInformationAreExportedToWavefrontRegistry() {
this.contextRunner
.withPropertyValues("wavefront.application.name=test-app", "wavefront.application.service=test-service",
"wavefront.application.cluster=test-cluster", "wavefront.application.shard=test-shard")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class))).run((context) -> {
MeterRegistry registry = context.getBean(MeterRegistry.class);
registry.counter("my.counter", "env", "qa");
assertThat(registry.find("my.counter").tags("env", "qa").tags("application", "test-app")
.tags("service", "test-service").tags("cluster", "test-cluster").tags("shard", "test-shard")
.counter()).isNotNull();
});
}
@Test
void applicationTagsAreNotExportedToNonWavefrontRegistry() {
this.contextRunner
@@ -149,49 +89,6 @@ class WavefrontAutoConfigurationTests {
});
}
@Test
void jvmReporterIsConfiguredWhenNoneExists() {
this.contextRunner.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.run((context) -> assertThat(context).hasSingleBean(WavefrontJvmReporter.class));
}
@Test
void jvmReporterCanBeDisabled() {
this.contextRunner.withPropertyValues("wavefront.metrics.extract-jvm-metrics=false")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.run(context -> assertThat(context).doesNotHaveBean(WavefrontJvmReporter.class));
}
@Test
void jvmReporterCanBeCustomized() {
WavefrontJvmReporter reporter = mock(WavefrontJvmReporter.class);
this.contextRunner.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.withBean(WavefrontJvmReporter.class, () -> reporter)
.run((context) -> assertThat(context).getBean(WavefrontJvmReporter.class).isEqualTo(reporter));
}
@Test
void jvmReporterNotConfiguredWithoutWavefrontSender() {
this.contextRunner.with(metrics())
.run(context -> assertThat(context).doesNotHaveBean(WavefrontJvmReporter.class));
}
@Test
void tracingWithSleuthIsConfiguredWithWavefrontSender() {
WavefrontSender sender = mock(WavefrontSender.class);
this.contextRunner.withPropertyValues().with(wavefrontMetrics(() -> sender)).with(sleuth()).run((context) -> {
assertThat(context).hasSingleBean(TracingCustomizer.class);
WavefrontSleuthBraveSpanHandler braveSpanHandler = extractSpanHandler(context.getBean(Tracer.class));
assertThat(braveSpanHandler.spanHandler).hasFieldOrPropertyWithValue("wavefrontSender", sender);
});
}
@Test
void tracingWithSleuthWithEmptyEnvironmentUseDefaultTags() {
this.contextRunner.with(wavefrontMetrics(() -> mock(WavefrontSender.class))).with(sleuth())
.run(assertSleuthSpanDefaultTags("unnamed_application", "unnamed_service"));
}
@Test
void tracingWithSleuthWithWavefrontTagsAndSpringApplicationNameUseWavefrontTags() {
this.contextRunner
@@ -201,38 +98,6 @@ class WavefrontAutoConfigurationTests {
.run(assertSleuthSpanDefaultTags("wavefront-application", "wavefront-service"));
}
@Test
void tracingWithSleuthWithSpringApplicationNameUseItRatherThanDefault() {
this.contextRunner.withPropertyValues("spring.application.name=spring-service")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class))).with(sleuth())
.run(assertSleuthSpanDefaultTags("unnamed_application", "spring-service"));
}
@Test
void tracingWithSleuthWithCustomApplicationTagsUseThat() {
this.contextRunner
.withPropertyValues("wavefront.application.name=wavefront-application",
"wavefront.application.service=wavefront-service")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.withBean(ApplicationTags.class,
() -> new ApplicationTags.Builder("custom-application", "custom-service")
.cluster("custom-cluster").shard("custom-shard").build())
.with(sleuth()).run(assertSleuthSpanDefaultTags("custom-application", "custom-service",
"custom-cluster", "custom-shard"));
}
@Test
void tracingWithSleuthWithCustomApplicationTagsAndEmptyValuesFallbackToDefaults() {
this.contextRunner
.withPropertyValues("wavefront.application.name=wavefront-application",
"wavefront.application.service=wavefront-service")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.withBean(ApplicationTags.class,
() -> new ApplicationTags.Builder("custom-application", "custom-service").build())
.with(sleuth())
.run(assertSleuthSpanDefaultTags("custom-application", "custom-service", "none", "none"));
}
private ContextConsumer<AssertableApplicationContext> assertSleuthSpanDefaultTags(String applicationName,
String serviceName) {
return assertSleuthSpanDefaultTags(applicationName, serviceName, "none", "none");
@@ -241,7 +106,7 @@ class WavefrontAutoConfigurationTests {
private ContextConsumer<AssertableApplicationContext> assertSleuthSpanDefaultTags(String applicationName,
String serviceName, String cluster, String shard) {
return (context) -> {
assertThat(context).hasSingleBean(TracingCustomizer.class);
assertThat(context).hasSingleBean(WavefrontTracingCustomizer.class);
WavefrontSleuthBraveSpanHandler braveSpanHandler = extractSpanHandler(context.getBean(Tracer.class));
assertThat(braveSpanHandler.spanHandler.getDefaultTags()).contains(
new Pair<>("application", applicationName), new Pair<>("service", serviceName),
@@ -254,9 +119,9 @@ class WavefrontAutoConfigurationTests {
void tracingWithSleuthCanBeConfigured() {
WavefrontSender sender = mock(WavefrontSender.class);
this.contextRunner.withPropertyValues()
.withPropertyValues("wavefront.tracing.red-metrics-custom-tag-keys=region,test")
.withPropertyValues("spring.sleuth.wavefront.red-metrics-custom-tag-keys=region,test")
.with(wavefrontMetrics(() -> sender)).with(sleuth()).run((context) -> {
assertThat(context).hasSingleBean(TracingCustomizer.class);
assertThat(context).hasSingleBean(WavefrontTracingCustomizer.class);
WavefrontSleuthBraveSpanHandler braveSpanHandler = extractSpanHandler(
context.getBean(Tracer.class));
WavefrontSleuthSpanHandler spanHandler = braveSpanHandler.spanHandler;
@@ -269,23 +134,16 @@ class WavefrontAutoConfigurationTests {
@Test
void tracingWithOpenTracingBacksOffWhenSpringCloudSleuthIsAvailable() {
this.contextRunner.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.run((context) -> assertThat(context).hasSingleBean(TracingCustomizer.class)
.run((context) -> assertThat(context).hasSingleBean(WavefrontTracingCustomizer.class)
.doesNotHaveBean(io.opentracing.Tracer.class));
}
@Test
void tracingIsDisabledWhenOpenTracingAndSleuthAreNotAvailable() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(WavefrontAutoConfiguration.class))
.withClassLoader(new FilteredClassLoader("org.springframework.cloud.sleuth", "io.opentracing"))
.with(wavefrontMetrics(() -> mock(WavefrontSender.class))).run((context) -> assertThat(context)
.doesNotHaveBean(TracingCustomizer.class).doesNotHaveBean(io.opentracing.Tracer.class));
}
@Test
void tracingCanBeDisabled() {
this.contextRunner.withPropertyValues("wavefront.tracing.enabled=false")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class))).run((context) -> assertThat(context)
.doesNotHaveBean(TracingCustomizer.class).doesNotHaveBean(io.opentracing.Tracer.class));
this.contextRunner.withPropertyValues("spring.sleuth.wavefront.enabled=false")
.with(wavefrontMetrics(() -> mock(WavefrontSender.class)))
.run((context) -> assertThat(context).doesNotHaveBean(WavefrontTracingCustomizer.class)
.doesNotHaveBean(io.opentracing.Tracer.class));
}
@Test
@@ -321,4 +179,29 @@ class WavefrontAutoConfigurationTests {
return (runner) -> (T) runner.withConfiguration(AutoConfigurations.of(BraveAutoConfiguration.class));
}
@Configuration
static class Config {
@Bean
ApplicationTags applicationTags(Environment environment) {
return createFromProperties(environment);
}
public ApplicationTags createFromProperties(Environment environment) {
String application = environment.getProperty("wavefront.application.name");
String service = environment.getProperty("wavefront.application.service");
service = (StringUtils.hasText(application)) ? service : defaultServiceName(environment);
ApplicationTags.Builder builder = new ApplicationTags.Builder(application, service);
builder.cluster(environment.getProperty("wavefront.application.cluster"));
builder.shard(environment.getProperty("wavefront.application.shard"));
return builder.build();
}
private String defaultServiceName(Environment environment) {
String applicationName = environment.getProperty("spring.application.name");
return (StringUtils.hasText(applicationName)) ? applicationName : "unnamed_service";
}
}
}

View File

@@ -30,6 +30,7 @@ import brave.opentracing.BraveTracer;
import brave.sampler.Sampler;
import com.wavefront.sdk.common.Pair;
import com.wavefront.sdk.common.WavefrontSender;
import com.wavefront.sdk.common.application.ApplicationTags;
import com.wavefront.sdk.entities.histograms.HistogramGranularity;
import com.wavefront.sdk.entities.tracing.SpanLog;
import io.opentracing.Tracer;
@@ -45,11 +46,13 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -277,6 +280,26 @@ public class WavefrontTracingIntegrationTests {
};
}
@Bean
ApplicationTags applicationTags(Environment environment) {
return createFromProperties(environment);
}
public ApplicationTags createFromProperties(Environment environment) {
String application = environment.getProperty("wavefront.application.name");
String service = environment.getProperty("wavefront.application.service");
service = (StringUtils.hasText(service)) ? service : defaultServiceName(environment);
ApplicationTags.Builder builder = new ApplicationTags.Builder(application, service);
builder.cluster(environment.getProperty("wavefront.application.cluster"));
builder.shard(environment.getProperty("wavefront.application.shard"));
return builder.build();
}
private String defaultServiceName(Environment environment) {
String applicationName = environment.getProperty("spring.application.name");
return (StringUtils.hasText(applicationName)) ? applicationName : "unnamed_service";
}
}
@Controller