Added support for scope decorators (#1068)

fixes gh-1063
This commit is contained in:
Marcin Grzejszczak
2018-08-22 17:06:10 +02:00
committed by GitHub
parent c5e043af71
commit 5b6a4f2767
5 changed files with 148 additions and 38 deletions

View File

@@ -23,11 +23,11 @@ import brave.CurrentSpanCustomizer;
import brave.ErrorParser;
import brave.Tracer;
import brave.Tracing;
import brave.context.log4j2.ThreadContextCurrentTraceContext;
import brave.propagation.B3Propagation;
import brave.propagation.CurrentTraceContext;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.Propagation;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.sampler.Sampler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -58,6 +58,7 @@ public class TraceAutoConfiguration {
public static final String TRACER_BEAN_NAME = "tracer";
@Autowired(required = false) List<SpanAdjuster> spanAdjusters = new ArrayList<>();
@Autowired(required = false) List<CurrentTraceContext.ScopeDecorator> scopeDecorators = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
@@ -132,10 +133,18 @@ public class TraceAutoConfiguration {
return factoryBuilder.build();
}
@Bean
CurrentTraceContext currentTraceContext(CurrentTraceContext.Builder builder) {
for (CurrentTraceContext.ScopeDecorator scopeDecorator : this.scopeDecorators) {
builder.addScopeDecorator(scopeDecorator);
}
return builder.build();
}
@Bean
@ConditionalOnMissingBean
CurrentTraceContext sleuthCurrentTraceContext() {
return ThreadContextCurrentTraceContext.create();
CurrentTraceContext.Builder sleuthCurrentTraceContextBuilder() {
return ThreadLocalCurrentTraceContext.newBuilder();
}
@Bean

View File

@@ -18,12 +18,8 @@ package org.springframework.cloud.sleuth.log;
import brave.propagation.CurrentTraceContext;
import org.slf4j.MDC;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.autoconfig.TraceAutoConfiguration;
@@ -51,32 +47,8 @@ public class SleuthLogAutoConfiguration {
@Bean
@ConditionalOnProperty(value = "spring.sleuth.log.slf4j.enabled", matchIfMissing = true)
@ConditionalOnMissingBean
public CurrentTraceContext slf4jSpanLogger() {
return Slf4jCurrentTraceContext.create();
}
@Bean
@ConditionalOnProperty(value = "spring.sleuth.log.slf4j.enabled", matchIfMissing = true)
@ConditionalOnBean(CurrentTraceContext.class)
public static BeanPostProcessor slf4jSpanLoggerBPP() {
return new Slf4jBeanPostProcessor();
}
static class Slf4jBeanPostProcessor implements BeanPostProcessor {
@Override public Object postProcessBeforeInitialization(Object bean,
String beanName) throws BeansException {
return bean;
}
@Override public Object postProcessAfterInitialization(Object bean,
String beanName) throws BeansException {
if (bean instanceof CurrentTraceContext && !(bean instanceof Slf4jCurrentTraceContext)) {
return Slf4jCurrentTraceContext.create((CurrentTraceContext) bean);
}
return bean;
}
public CurrentTraceContext.ScopeDecorator slf4jSpanDecorator() {
return new Slf4jScopeDecorator();
}
}
}

View File

@@ -30,10 +30,16 @@ import org.slf4j.MDC;
* Supports backward compatibility of MDC entries by adding legacy "X-B3" entries to MDC context
* "X-B3-TraceId", "X-B3-ParentSpanId", "X-B3-SpanId" and "X-B3-Sampled"
*
* Due to the migration to {@link brave.propagation.CurrentTraceContext.ScopeDecorator} approach,
* we are making the default implementation package scope since you can register your
* own implementation of the Scope Decorator.
*
* @author Marcin Grzejszczak
*
* @since 2.0.0
* @deprecated {@link Slf4jScopeDecorator} will be used
*/
@Deprecated
public final class Slf4jCurrentTraceContext extends CurrentTraceContext {
// Backward compatibility for all logging patterns

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2013-2018 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
*
* http://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.log;
import brave.internal.HexCodec;
import brave.internal.Nullable;
import brave.propagation.CurrentTraceContext;
import brave.propagation.TraceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/**
* Adds {@linkplain MDC} properties "traceId", "parentId", "spanId" and "spanExportable" when a {@link
* brave.Tracer#currentSpan() span is current}. These can be used in log correlation.
* Supports backward compatibility of MDC entries by adding legacy "X-B3" entries to MDC context
* "X-B3-TraceId", "X-B3-ParentSpanId", "X-B3-SpanId" and "X-B3-Sampled"
*
* @author Marcin Grzejszczak
*
* @since 2.1.0
*/
final class Slf4jScopeDecorator implements CurrentTraceContext.ScopeDecorator {
// Backward compatibility for all logging patterns
private static final String LEGACY_EXPORTABLE_NAME = "X-Span-Export";
private static final String LEGACY_PARENT_ID_NAME = "X-B3-ParentSpanId";
private static final String LEGACY_TRACE_ID_NAME = "X-B3-TraceId";
private static final String LEGACY_SPAN_ID_NAME = "X-B3-SpanId";
private static final Logger log = LoggerFactory.getLogger(Slf4jScopeDecorator.class);
@Override public CurrentTraceContext.Scope decorateScope(TraceContext currentSpan,
CurrentTraceContext.Scope scope) {
final String previousTraceId = MDC.get("traceId");
final String previousParentId = MDC.get("parentId");
final String previousSpanId = MDC.get("spanId");
final String spanExportable = MDC.get("spanExportable");
final String legacyPreviousTraceId = MDC.get(LEGACY_TRACE_ID_NAME);
final String legacyPreviousParentId = MDC.get(LEGACY_PARENT_ID_NAME);
final String legacyPreviousSpanId = MDC.get(LEGACY_SPAN_ID_NAME);
final String legacySpanExportable = MDC.get(LEGACY_EXPORTABLE_NAME);
if (currentSpan != null) {
String traceIdString = currentSpan.traceIdString();
MDC.put("traceId", traceIdString);
MDC.put(LEGACY_TRACE_ID_NAME, traceIdString);
String parentId = currentSpan.parentId() != null ?
HexCodec.toLowerHex(currentSpan.parentId()) :
null;
replace("parentId", parentId);
replace(LEGACY_PARENT_ID_NAME, parentId);
String spanId = HexCodec.toLowerHex(currentSpan.spanId());
MDC.put("spanId", spanId);
MDC.put(LEGACY_SPAN_ID_NAME, spanId);
String sampled = String.valueOf(currentSpan.sampled());
MDC.put("spanExportable", sampled);
MDC.put(LEGACY_EXPORTABLE_NAME, sampled);
log("Starting scope for span: {}", currentSpan);
if (currentSpan.parentId() != null) {
if (log.isTraceEnabled()) {
log.trace("With parent: {}", currentSpan.parentId());
}
}
}
else {
MDC.remove("traceId");
MDC.remove("parentId");
MDC.remove("spanId");
MDC.remove("spanExportable");
MDC.remove(LEGACY_TRACE_ID_NAME);
MDC.remove(LEGACY_PARENT_ID_NAME);
MDC.remove(LEGACY_SPAN_ID_NAME);
MDC.remove(LEGACY_EXPORTABLE_NAME);
}
class ThreadContextCurrentTraceContextScope implements CurrentTraceContext.Scope {
@Override public void close() {
log("Closing scope for span: {}", currentSpan);
scope.close();
replace("traceId", previousTraceId);
replace("parentId", previousParentId);
replace("spanId", previousSpanId);
replace("spanExportable", spanExportable);
replace(LEGACY_TRACE_ID_NAME, legacyPreviousTraceId);
replace(LEGACY_PARENT_ID_NAME, legacyPreviousParentId);
replace(LEGACY_SPAN_ID_NAME, legacyPreviousSpanId);
replace(LEGACY_EXPORTABLE_NAME, legacySpanExportable);
}
}
return new ThreadContextCurrentTraceContextScope();
}
private void log(String text, TraceContext span) {
if (span == null) {
return;
}
if (log.isTraceEnabled()) {
log.trace(text, span);
}
}
static void replace(String key, @Nullable String value) {
if (value != null) {
MDC.put(key, value);
}
else {
MDC.remove(key);
}
}
}

View File

@@ -40,8 +40,7 @@ public class Slf4JSpanLoggerTest {
.build();
Span span = this.tracing.tracer().nextSpan().name("span").start();
Slf4jCurrentTraceContext slf4jCurrentTraceContext =
new Slf4jCurrentTraceContext(new StrictCurrentTraceContext());
Slf4jScopeDecorator slf4jScopeDecorator = new Slf4jScopeDecorator();
@Before
@After
@@ -51,7 +50,7 @@ public class Slf4JSpanLoggerTest {
@Test
public void should_set_entries_to_mdc_from_span() throws Exception {
Scope scope = this.slf4jCurrentTraceContext.newScope(this.span.context());
Scope scope = this.slf4jScopeDecorator.decorateScope(this.span.context(), () -> { });
assertThat(MDC.get("X-B3-TraceId")).isEqualTo(span.context().traceIdString());
assertThat(MDC.get("traceId")).isEqualTo(span.context().traceIdString());
@@ -67,8 +66,7 @@ public class Slf4JSpanLoggerTest {
MDC.put("X-B3-TraceId", "A");
MDC.put("traceId", "A");
Scope scope = this.slf4jCurrentTraceContext
.newScope(null);
Scope scope = this.slf4jScopeDecorator.decorateScope(null, () -> { });
assertThat(MDC.get("X-B3-TraceId")).isNullOrEmpty();
assertThat(MDC.get("traceId")).isNullOrEmpty();