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:
Dave Syer
2016-02-01 17:00:00 +00:00
parent 065338118b
commit 70b18054e1
16 changed files with 73 additions and 97 deletions

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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";

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -12,4 +12,4 @@ spring:
sample:
zipkin:
# When enabled=false, traces log to the console. Comment to send to zipkin
enabled: true
enabled: false

View File

@@ -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) {

View File

@@ -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

View File

@@ -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;