Added spanHandler that skips span name via patterns; fixes gh-1720

This commit is contained in:
Marcin Grzejszczak
2020-08-27 16:32:53 +02:00
parent f252a6f3ae
commit 368eabb412
5 changed files with 291 additions and 6 deletions

View File

@@ -381,12 +381,12 @@ anything not already done by Sleuth with auto-configuration or properties.
If you define one of the following as a `Bean`, Sleuth will invoke it to
customize behaviour:
* RpcTracingCustomizer - for RPC tagging and sampling policy
* HttpTracingCustomizer - for HTTP tagging and sampling policy
* MessagingTracingCustomizer - for messaging tagging and sampling policy
* CurrentTraceContextCustomizer - to integrate decorators such as correlation.
* BaggagePropagationCustomizer - for propagating baggage fields in process and over headers
* CorrelationScopeDecoratorCustomizer - for scope decorations such as MDC (logging) field correlation
* `RpcTracingCustomizer` - for RPC tagging and sampling policy
* `HttpTracingCustomizer` - for HTTP tagging and sampling policy
* `MessagingTracingCustomizer` - for messaging tagging and sampling policy
* `CurrentTraceContextCustomizer` - to integrate decorators such as correlation.
* `BaggagePropagationCustomize`r - for propagating baggage fields in process and over headers
* `CorrelationScopeDecoratorCustomizer` - for scope decorations such as MDC (logging) field correlation
=== HTTP
@@ -540,6 +540,8 @@ include::{project-root}//spring-cloud-sleuth-core/src/test/java/org/springframew
The preceding example results in changing the name of the reported span to `foo bar`, just before it gets reported (for example, to Zipkin).
Sleuth registers a `SpanHandler` bean that can automatically skip reporting spans of given name patterns. The property `spring.sleuth.span-handler.span-name-patterns-to-skip` contains the default skip patterns for span names. The property `spring.sleuth.span-handler.additional-span-name-patterns-to-skip` will append the provided span name patterns to the existing ones. In order to disable this functionality just set `spring.sleuth.span-handler.enabled` to `false`.
=== Host Locator
IMPORTANT: This section is about defining *host* from service discovery.

View File

@@ -16,6 +16,10 @@
package org.springframework.cloud.sleuth.autoconfig;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@@ -38,6 +42,11 @@ class SleuthProperties {
*/
private boolean supportsJoin = true;
/**
* Properties related to handling of spans.
*/
private SpanHandler spanHandler = new SpanHandler();
public boolean isEnabled() {
return this.enabled;
}
@@ -62,4 +71,62 @@ class SleuthProperties {
this.supportsJoin = supportsJoin;
}
public SpanHandler getSpanHandler() {
return this.spanHandler;
}
public void setSpanHandler(SpanHandler spanHandler) {
this.spanHandler = spanHandler;
}
/**
* Properties related to handling of spans.
*/
public static class SpanHandler {
/**
* Will turn on the default Sleuth handler mechanism. Might ignore exporting of
* certain spans;
*/
private boolean enabled;
/**
* List of span names to ignore. They will not be sent to external systems.
*/
private List<String> spanNamePatternsToSkip = Arrays
.asList("^catalogWatchTaskScheduler$");
/**
* Additional list of span names to ignore. Will be appended to
* {@link #spanNamePatternsToSkip}.
*/
private List<String> additionalSpanNamePatternsToIgnore = Collections.emptyList();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getSpanNamePatternsToSkip() {
return this.spanNamePatternsToSkip;
}
public void setSpanNamePatternsToSkip(List<String> spanNamePatternsToSkip) {
this.spanNamePatternsToSkip = spanNamePatternsToSkip;
}
public List<String> getAdditionalSpanNamePatternsToIgnore() {
return this.additionalSpanNamePatternsToIgnore;
}
public void setAdditionalSpanNamePatternsToIgnore(
List<String> additionalSpanNamePatternsToIgnore) {
this.additionalSpanNamePatternsToIgnore = additionalSpanNamePatternsToIgnore;
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import brave.propagation.TraceContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
/**
* {@link SpanHandler} that ignores spans via names.
*
* @author Marcin Grzejszczak
* @since 3.0.0
*/
class SpanIgnoringSpanHandler extends SpanHandler {
private static final Log log = LogFactory.getLog(SpanIgnoringSpanHandler.class);
private final SleuthProperties sleuthProperties;
static final Map<String, Pattern> cache = new ConcurrentHashMap<>();
SpanIgnoringSpanHandler(SleuthProperties sleuthProperties) {
this.sleuthProperties = sleuthProperties;
}
@Override
public boolean end(TraceContext context, MutableSpan span, Cause cause) {
if (cause != Cause.FINISHED) {
return true;
}
List<Pattern> spanNamesToIgnore = spanNamesToIgnore();
String name = span.name();
if (StringUtils.hasText(name)
&& spanNamesToIgnore.stream().anyMatch(p -> p.matcher(name).matches())) {
if (log.isDebugEnabled()) {
log.debug("Will ignore a span with name [" + name + "]");
}
return false;
}
return super.end(context, span, cause);
}
private List<Pattern> spanNamesToIgnore() {
return spanNames().stream()
.map(regex -> cache.computeIfAbsent(regex, Pattern::compile))
.collect(Collectors.toList());
}
private List<String> spanNames() {
List<String> spanNamesToIgnore = new ArrayList<>(
this.sleuthProperties.getSpanHandler().getSpanNamePatternsToSkip());
spanNamesToIgnore.addAll(this.sleuthProperties.getSpanHandler()
.getAdditionalSpanNamePatternsToIgnore());
return spanNamesToIgnore;
}
}

View File

@@ -142,4 +142,10 @@ public class TraceAutoConfiguration {
return CurrentSpanCustomizer.create(tracing);
}
@Bean
@ConditionalOnProperty(value = "spring.sleuth.span-handler.enabled", matchIfMissing = true)
SpanHandler spanIgnoringSpanHandler(SleuthProperties sleuthProperties) {
return new SpanIgnoringSpanHandler(sleuthProperties);
}
}

View File

@@ -0,0 +1,127 @@
/*
* 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;
import java.util.Collections;
import brave.handler.MutableSpan;
import brave.handler.SpanHandler;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.BDDAssertions.then;
class SpanIgnoringSpanHandlerTests {
@Test
void should_handle_span_when_not_yet_finished() {
SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(new SleuthProperties());
then(handler.end(null, null, SpanHandler.Cause.ABANDONED)).isTrue();
}
@Test
void should_handle_span_when_name_null() {
SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(new SleuthProperties());
then(handler.end(null, new MutableSpan(), SpanHandler.Cause.FINISHED)).isTrue();
}
@Test
void should_handle_span_when_not_present_in_main_list_of_spans_to_skip() {
SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(new SleuthProperties());
then(handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED)).isTrue();
}
private MutableSpan namedSpan() {
MutableSpan span = new MutableSpan();
span.name("someName");
return span;
}
@Test
void should_not_handle_span_when_present_in_main_list_of_spans_to_skip() {
SleuthProperties sleuthProperties = new SleuthProperties();
sleuthProperties.getSpanHandler().setSpanNamePatternsToSkip(Collections.singletonList("someName"));
SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(sleuthProperties);
then(handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED)).isFalse();
}
@Test
void should_not_handle_span_when_present_in_additional_list_of_spans_to_skip() {
SleuthProperties sleuthProperties = sleuthPropertiesWithAdditionalEntries();
SpanIgnoringSpanHandler handler = new SpanIgnoringSpanHandler(sleuthProperties);
then(handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED)).isFalse();
}
@Test
void should_use_cached_entry_for_same_patterns() {
end(handler(sleuthPropertiesWithAdditionalEntries("someOtherName")));
end(handler(sleuthPropertiesWithAdditionalEntries("someOtherName")));
end(handler(sleuthPropertiesWithAdditionalEntries("someOtherName")));
then(SpanIgnoringSpanHandler.cache).containsKey("someOtherName");
end(handler(sleuthPropertiesWithAdditionalEntries("a")));
end(handler(sleuthPropertiesWithAdditionalEntries("b")));
end(handler(sleuthPropertiesWithAdditionalEntries("c")));
then(SpanIgnoringSpanHandler.cache).containsKey("someOtherName").containsKey("a").containsKey("b")
.containsKey("c");
}
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class));
@Test
void should_not_register_span_handler_when_property_passed() {
this.contextRunner.withPropertyValues("spring.sleuth.span-handler.enabled=false")
.run((context) -> BDDAssertions.thenThrownBy(() -> context.getBean(SpanIgnoringSpanHandler.class))
.isInstanceOf(NoSuchBeanDefinitionException.class));
}
@Test
void should_register_span_handler_by_default() {
this.contextRunner.run((context) -> context.getBean(SpanIgnoringSpanHandler.class));
}
private SleuthProperties sleuthPropertiesWithAdditionalEntries() {
return sleuthPropertiesWithAdditionalEntries("someName");
}
private SleuthProperties sleuthPropertiesWithAdditionalEntries(String name) {
SleuthProperties sleuthProperties = new SleuthProperties();
sleuthProperties.getSpanHandler().setAdditionalSpanNamePatternsToIgnore(Collections.singletonList(name));
return sleuthProperties;
}
private void end(SpanIgnoringSpanHandler handler) {
handler.end(null, namedSpan(), SpanHandler.Cause.FINISHED);
}
private SpanIgnoringSpanHandler handler(SleuthProperties sleuthProperties) {
return new SpanIgnoringSpanHandler(sleuthProperties);
}
}