Add Span to Sampler method params
Also allows us to actually create a PercentageBasedSampler (I don't think anyone tried it before) without resorting to lazy beans and proxies. Another freature added here is a default percentage sampler if we know that spans need to be exported (zipkin or stream is present). Fixes gh-138
This commit is contained in:
@@ -17,8 +17,11 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
/**
|
||||
* Extremely simple callback to determine the frequency that an action should be
|
||||
* Extremely simple callback to determine the frequency that an action should be traced.
|
||||
*/
|
||||
public interface Sampler {
|
||||
boolean isSampled();
|
||||
/**
|
||||
* @param span the current span (or null if there is none)
|
||||
*/
|
||||
boolean isSampled(Span span);
|
||||
}
|
||||
|
||||
@@ -17,13 +17,14 @@
|
||||
package org.springframework.cloud.sleuth.sampler;
|
||||
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class AlwaysSampler implements Sampler {
|
||||
@Override
|
||||
public boolean isSampled() {
|
||||
public boolean isSampled(Span span) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.sleuth.sampler;
|
||||
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.trace.SpanContextHolder;
|
||||
|
||||
/**
|
||||
@@ -27,7 +28,7 @@ public class IsTracingSampler implements Sampler {
|
||||
public static IsTracingSampler INSTANCE = new IsTracingSampler();
|
||||
|
||||
@Override
|
||||
public boolean isSampled() {
|
||||
public boolean isSampled(Span span) {
|
||||
return SpanContextHolder.isTracing();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.springframework.cloud.sleuth.sampler;
|
||||
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
|
||||
/**
|
||||
* Sampler that based on the given percentage rate will allow sampling.
|
||||
@@ -23,16 +22,13 @@ import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
public class PercentageBasedSampler implements Sampler {
|
||||
|
||||
private final SamplerConfiguration configuration;
|
||||
private final SpanAccessor spanAccessor;
|
||||
|
||||
public PercentageBasedSampler(SamplerConfiguration configuration, SpanAccessor spanAccessor) {
|
||||
public PercentageBasedSampler(SamplerConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
this.spanAccessor = spanAccessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSampled() {
|
||||
Span currentSpan = this.spanAccessor.getCurrentSpan();
|
||||
public boolean isSampled(Span currentSpan) {
|
||||
long threshold = Math.abs(Long.MAX_VALUE * (int) (this.configuration.getPercentage() * 100)); // drops fractional percentage.
|
||||
if (currentSpan == null || threshold == 0L) {
|
||||
return false;
|
||||
|
||||
@@ -13,15 +13,9 @@ import lombok.Data;
|
||||
public class SamplerConfiguration {
|
||||
|
||||
/**
|
||||
* Percentage of requests that should be sampled.
|
||||
* E.g.
|
||||
* <ul>
|
||||
* <li> 1.0 - 100% requests should be sampled </li>
|
||||
* <li> 0.8 - 80% of requests should be sampled </li>
|
||||
* <li> 0.0 - 0% requests should be sampled </li>
|
||||
* </ul>
|
||||
*
|
||||
* The precision is whole-numbers only. We don't support 0.1% of the trace rate.
|
||||
* Percentage of requests that should be sampled. E.g. 1.0 - 100% requests should be
|
||||
* sampled. The precision is whole-numbers only (i.e. there's no support for 0.1% of
|
||||
* the traces).
|
||||
*/
|
||||
private float percentage = 0.1f;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class DefaultTracer implements Tracer {
|
||||
@Override
|
||||
public Span startTrace(String name, Sampler sampler) {
|
||||
Span span;
|
||||
if (isTracing() || sampler.isSampled()) {
|
||||
if (isTracing() || sampler.isSampled(getCurrentSpan())) {
|
||||
span = createChild(getCurrentSpan(), name);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -16,7 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
@@ -37,15 +46,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
@@ -216,8 +217,8 @@ public class TraceFilterTests {
|
||||
|
||||
private class DelegateSampler implements Sampler {
|
||||
@Override
|
||||
public boolean isSampled() {
|
||||
return TraceFilterTests.this.sampler.isSampled();
|
||||
public boolean isSampled(Span span) {
|
||||
return TraceFilterTests.this.sampler.isSampled(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package org.springframework.cloud.sleuth.sampler;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.assertj.core.data.Percentage.withPercentage;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
public class PercentageBasedSamplerTests {
|
||||
|
||||
SamplerConfiguration samplerConfiguration = new SamplerConfiguration();
|
||||
SpanAccessor spanAccessor = traceReturningSpanWithUuid();
|
||||
private Span span;
|
||||
private static Random RANDOM = new Random();
|
||||
|
||||
@Test
|
||||
@@ -20,17 +20,20 @@ public class PercentageBasedSamplerTests {
|
||||
this.samplerConfiguration.setPercentage(1f);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
then(new PercentageBasedSampler(this.samplerConfiguration, this.spanAccessor).isSampled()).isTrue();
|
||||
then(new PercentageBasedSampler(this.samplerConfiguration).isSampled(this.span))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_reject_all_samples_when_config_has_0_percentage() throws Exception {
|
||||
public void should_reject_all_samples_when_config_has_0_percentage()
|
||||
throws Exception {
|
||||
this.samplerConfiguration.setPercentage(0f);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
then(new PercentageBasedSampler(this.samplerConfiguration, this.spanAccessor).isSampled()).isFalse();
|
||||
then(new PercentageBasedSampler(this.samplerConfiguration).isSampled(this.span))
|
||||
.isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,30 +45,23 @@ public class PercentageBasedSamplerTests {
|
||||
|
||||
int numberOfSampledElements = countNumberOfSampledElements(numberOfIterations);
|
||||
|
||||
then(numberOfSampledElements).isCloseTo((int) (numberOfIterations * percentage), withPercentage(3));
|
||||
then(numberOfSampledElements).isCloseTo((int) (numberOfIterations * percentage),
|
||||
withPercentage(3));
|
||||
}
|
||||
|
||||
private int countNumberOfSampledElements(int numberOfIterations) {
|
||||
int passedCounter = 0;
|
||||
for (int i = 0; i < numberOfIterations; i++) {
|
||||
boolean passed = new PercentageBasedSampler(this.samplerConfiguration, traceReturningSpanWithUuid()).isSampled();
|
||||
boolean passed = new PercentageBasedSampler(this.samplerConfiguration)
|
||||
.isSampled(this.span);
|
||||
passedCounter = passedCounter + (passed ? 1 : 0);
|
||||
}
|
||||
return passedCounter;
|
||||
}
|
||||
|
||||
private SpanAccessor traceReturningSpanWithUuid() {
|
||||
return new SpanAccessor() {
|
||||
@Override
|
||||
public Span getCurrentSpan() {
|
||||
return Span.builder().traceId(RANDOM.nextLong()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTracing() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@Before
|
||||
public void setupSpan() {
|
||||
this.span = Span.builder().traceId(RANDOM.nextLong()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,8 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
@@ -48,11 +46,6 @@ public class SampleMessagingApplication {
|
||||
@Autowired
|
||||
private SampleRequestResponse transformer;
|
||||
|
||||
@Bean
|
||||
public Sampler defaultSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
String msg = "Hello";
|
||||
|
||||
@@ -20,8 +20,6 @@ import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@@ -38,11 +36,6 @@ import com.github.kristofa.brave.SpanCollector;
|
||||
@EnableZuulProxy
|
||||
public class SampleRibbonApplication {
|
||||
|
||||
@Bean
|
||||
public Sampler defaultSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleRibbonApplication.class, args);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ package sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@@ -34,11 +32,6 @@ public class SampleSleuthApplication {
|
||||
|
||||
public static final String CLIENT_NAME = "testApp";
|
||||
|
||||
@Bean
|
||||
public Sampler defaultSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SampleController sampleController() {
|
||||
return new SampleController();
|
||||
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
package sample;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -36,14 +35,6 @@ import org.springframework.scheduling.annotation.EnableAsync;
|
||||
@Slf4j
|
||||
public class SampleZipkinApplication {
|
||||
|
||||
/**
|
||||
* Sleuth will not report trace data unless you define a sampler like below.
|
||||
*/
|
||||
@Bean
|
||||
public Sampler defaultSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleZipkinApplication.class, args);
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ spring:
|
||||
sample:
|
||||
zipkin:
|
||||
# When enabled=false, traces log to the console. Comment to send to zipkin
|
||||
enabled: true
|
||||
enabled: false
|
||||
|
||||
@@ -20,13 +20,17 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.metric.SpanReporterService;
|
||||
import org.springframework.cloud.sleuth.sampler.PercentageBasedSampler;
|
||||
import org.springframework.cloud.sleuth.sampler.SamplerConfiguration;
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||
import org.springframework.cloud.stream.config.ChannelBindingAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -48,12 +52,18 @@ import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(SleuthStreamProperties.class)
|
||||
@EnableConfigurationProperties({SleuthStreamProperties.class, SamplerConfiguration.class})
|
||||
@AutoConfigureBefore(ChannelBindingAutoConfiguration.class)
|
||||
@EnableBinding(SleuthSource.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.stream.enabled", matchIfMissing = true)
|
||||
public class SleuthStreamAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Sampler defaultTraceSampler(SamplerConfiguration config) {
|
||||
return new PercentageBasedSampler(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@GlobalChannelInterceptor(patterns = SleuthSource.OUTPUT, order = Ordered.HIGHEST_PRECEDENCE)
|
||||
public ChannelInterceptor zipkinChannelInterceptor(final SpanReporterService spanReporterService) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.sleuth.zipkin;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
@@ -25,7 +26,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.metric.SpanReporterService;
|
||||
import org.springframework.cloud.sleuth.sampler.PercentageBasedSampler;
|
||||
import org.springframework.cloud.sleuth.sampler.SamplerConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -34,21 +39,22 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties
|
||||
@EnableConfigurationProperties({ZipkinProperties.class, SamplerConfiguration.class})
|
||||
@ConditionalOnProperty(value = "spring.zipkin.enabled", matchIfMissing = true)
|
||||
@AutoConfigureBefore(TraceAutoConfiguration.class)
|
||||
public class ZipkinAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ZipkinSpanReporter.class)
|
||||
public ZipkinSpanReporter reporter(SpanReporterService spanReporterService) {
|
||||
ZipkinProperties zipkin = zipkinProperties();
|
||||
public ZipkinSpanReporter reporter(SpanReporterService spanReporterService, ZipkinProperties zipkin) {
|
||||
return new HttpZipkinSpanReporter(zipkin.getBaseUrl(), zipkin.getFlushInterval(),
|
||||
spanReporterService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZipkinProperties zipkinProperties() {
|
||||
return new ZipkinProperties();
|
||||
@ConditionalOnMissingBean
|
||||
public Sampler defaultTraceSampler(SamplerConfiguration config) {
|
||||
return new PercentageBasedSampler(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -26,8 +26,6 @@ import lombok.Data;
|
||||
@ConfigurationProperties("spring.zipkin")
|
||||
@Data
|
||||
public class ZipkinProperties {
|
||||
// Sample rate = 1.0 means 100% of requests will get traced.
|
||||
private float fixedSampleRate = 1.0f;
|
||||
/** URL of the zipkin query server instance. */
|
||||
private String baseUrl = "http://localhost:9411/";
|
||||
private boolean enabled = true;
|
||||
|
||||
Reference in New Issue
Block a user