Added an option to disable refresh scope on samplers; fixes gh-1557

This commit is contained in:
Marcin Grzejszczak
2020-08-07 13:57:13 +02:00
parent c091f4f337
commit 9d0ee1428d
5 changed files with 66 additions and 7 deletions

View File

@@ -88,6 +88,8 @@ include::{project-root}/spring-cloud-sleuth-core/src/test/java/org/springframewo
TIP: You can set the HTTP header `b3` to `1`, or, when doing messaging, you can set the `spanFlags` header to `1`.
Doing so forces the current request to be sampled regardless of configuration.
By default samplers will work with the refresh scope mechanism. That means that you can change the sampling properties at runtime, refresh the application and the changes will be reflected. However, sometimes the fact of creating a proxy around samplers and calling it from too early (from `@PostConstruct` annotated method) may lead to dead locks. In such a case either create a sampler bean explicitly, or set the property `spring.sleuth.sampler.refresh.enabled` to `false` to disable the refresh scope support.
== Baggage
Baggage are fields that are propagated with the trace, optionally out of process. You can use
properties to define fields that have no special configuration such as name mapping:

View File

@@ -39,7 +39,8 @@ import org.springframework.util.StringUtils;
*/
class TraceGatewayEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final Log log = LogFactory.getLog(TraceGatewayEnvironmentPostProcessor.class);
private static final Log log = LogFactory
.getLog(TraceGatewayEnvironmentPostProcessor.class);
private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
@@ -51,12 +52,14 @@ class TraceGatewayEnvironmentPostProcessor implements EnvironmentPostProcessor {
String instrumentationType = environment
.getProperty("spring.sleuth.reactor.instrumentation-type");
if (log.isDebugEnabled()) {
log.debug("Found the following instrumentation type [" + instrumentationType + "]");
log.debug("Found the following instrumentation type ["
+ instrumentationType + "]");
}
if (StringUtils.isEmpty(instrumentationType)) {
instrumentationType = "manual";
if (log.isDebugEnabled()) {
log.debug("No instrumentation type passed, will force it to [" + instrumentationType + "]");
log.debug("No instrumentation type passed, will force it to ["
+ instrumentationType + "]");
}
}
map.put("spring.sleuth.reactor.instrumentation-type", instrumentationType);

View File

@@ -18,9 +18,11 @@ package org.springframework.cloud.sleuth.sampler;
import brave.sampler.CountingSampler;
import brave.sampler.Sampler;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
@@ -65,7 +67,22 @@ public class SamplerAutoConfiguration {
@Bean
@RefreshScope
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.sleuth.sampler.refresh.enabled",
matchIfMissing = true)
public Sampler defaultTraceSampler(SamplerProperties config) {
return sampler(config);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.sleuth.sampler.refresh.enabled",
havingValue = "false")
public Sampler defaultNonRefreshScopeTraceSampler(SamplerProperties config) {
return sampler(config);
}
@NotNull
private Sampler sampler(SamplerProperties config) {
// TODO: Rewrite: refresh should replace the sampler, not change its state
// internally
if (config.getProbability() != null) {

View File

@@ -83,6 +83,12 @@
"type": "java.lang.Boolean",
"description": "Enable tracing of RPC.",
"defaultValue": true
},
{
"name": "spring.sleuth.sampler.refresh.enabled",
"type": "java.lang.Boolean",
"description": "Enable refresh scope for sampler.",
"defaultValue": true
}
]
}

View File

@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -59,10 +60,30 @@ public class SamplerAutoConfigurationTests {
@Test
void should_use_RateLimitedSampler_withTracingCustomizer() {
this.contextRunner.withUserConfiguration(WithTracingCustomizer.class)
.run((context -> {
final Sampler bean = context.getBean(Sampler.class);
BDDAssertions.then(bean).isInstanceOf(RateLimitingSampler.class);
this.contextRunner.withUserConfiguration(WithTracingCustomizer.class).run((context -> {
final Sampler bean = context.getBean(Sampler.class);
BDDAssertions.then(bean).isInstanceOf(RateLimitingSampler.class);
}));
}
@Test
void should_use_refresh_scope_sampler_when_no_property_passed_and_refresh_scope_present() {
this.contextRunner.withUserConfiguration(WithTracingCustomizer.class, WithRefreshScope.class).run((context -> {
BDDAssertions.then(context.containsBean("defaultTraceSampler")).as("refresh scope bean should be set")
.isTrue();
BDDAssertions.then(context.containsBean("defaultNonRefreshScopeTraceSampler"))
.as("non refresh scope bean should not be picked").isFalse();
}));
}
@Test
void should_use_non_refresh_scope_sampler_when_property_passed_and_refresh_scope_present() {
this.contextRunner.withUserConfiguration(WithTracingCustomizer.class, WithRefreshScope.class)
.withPropertyValues("spring.sleuth.sampler.refresh.enabled=false").run((context -> {
BDDAssertions.then(context.containsBean("defaultNonRefreshScopeTraceSampler"))
.as("non refresh scope bean should be picked").isTrue();
BDDAssertions.then(context.containsBean("defaultTraceSampler"))
.as("refresh scope bean should not be set").isFalse();
}));
}
@@ -142,4 +163,14 @@ public class SamplerAutoConfigurationTests {
}
@Configuration
static class WithRefreshScope {
@Bean
RefreshScope refreshScope() {
return new RefreshScope();
}
}
}