From cf6f62966382a72fb4202ad4cf43414b01bcd433 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 17 Feb 2016 17:10:54 +0100 Subject: [PATCH] [#168] Introduced local components for async Added changes following review * added comments * added javadocs * added naming basing on @SpanName and toString() * added default 'async' naming if there is no overriden span name * removed TraceDelegate after review * introduced SpanNamer fixes #168 --- .../cloud/sleuth/DefaultSpanNamer.java | 53 ++++++++ .../springframework/cloud/sleuth/Span.java | 1 + .../cloud/sleuth/SpanName.java | 45 +++++++ .../cloud/sleuth/SpanNamer.java | 37 ++++++ .../autoconfig/TraceAutoConfiguration.java | 20 +++- .../cloud/sleuth/instrument/TraceKeys.java | 113 ++++++++++++++++++ .../async/AsyncDefaultAutoConfiguration.java | 5 +- .../instrument/async/LazyTraceExecutor.java | 41 ++++++- .../async/LocalComponentTraceCallable.java | 69 +++++++++++ .../async/LocalComponentTraceRunnable.java | 67 +++++++++++ .../instrument/async/TraceAsyncAspect.java | 13 +- .../instrument/async/TraceCallable.java | 57 ++++++++- .../async/TraceContinuingCallable.java | 45 +++++++ .../instrument/async/TraceDelegate.java | 84 ------------- .../instrument/async/TraceRunnable.java | 59 ++++++++- .../async/TraceableExecutorService.java | 27 +++-- .../TraceableScheduledExecutorService.java | 17 +-- .../SleuthHystrixAutoConfiguration.java | 5 +- .../SleuthHystrixConcurrencyStrategy.java | 27 +++-- .../instrument/hystrix/TraceCommand.java | 12 +- .../sleuth/instrument/web/TraceWebAspect.java | 29 ++--- .../web/TraceWebAutoConfiguration.java | 5 +- .../AbstractTraceHttpRequestInterceptor.java | 2 +- .../SleuthHystrixInvocationHandler.java | 22 ++-- .../TraceFeignClientAutoConfiguration.java | 5 +- .../cloud/sleuth/trace/DefaultTracer.java | 10 +- .../cloud/sleuth/DefaultSpanNamerTest.java | 63 ++++++++++ .../cloud/sleuth/assertions/SpanAssert.java | 39 ++++++ .../instrument/async/TraceCallableTests.java | 47 +++++++- .../instrument/async/TraceRunnableTests.java | 53 ++++++-- .../async/TraceableExecutorServiceTests.java | 9 +- .../HystrixAnnotationsIntegrationTests.java | 42 +++++-- .../instrument/hystrix/TraceCommandTests.java | 22 +++- .../web/TraceAsyncIntegrationTests.java | 26 ++-- .../TraceFilterMockChainIntegrationTests.java | 3 +- .../instrument/web/TraceFilterTests.java | 3 +- .../TraceRestTemplateInterceptorTests.java | 10 +- .../zuul/TracePostZuulFilterTests.java | 5 +- .../zuul/TracePreZuulFilterTests.java | 3 +- .../sleuth/trace/DefaultTracerTests.java | 17 +-- 40 files changed, 991 insertions(+), 221 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/DefaultSpanNamer.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanName.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanNamer.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceCallable.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceRunnable.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceContinuingCallable.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceDelegate.java create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultSpanNamerTest.java diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/DefaultSpanNamer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/DefaultSpanNamer.java new file mode 100644 index 000000000..9fdb34f9b --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/DefaultSpanNamer.java @@ -0,0 +1,53 @@ +/* + * Copyright 2013-2016 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; + +import org.springframework.core.annotation.AnnotationUtils; + +/** + * Default implementation of SpanNamer that tries to get the Span name as follows: + * + *
  • + * + * + * + *
  • + * + * @see org.springframework.cloud.sleuth.SpanName + * + * @author Marcin Grzejszczak + */ +public class DefaultSpanNamer implements SpanNamer { + + @Override + public String name(Object object, String defaultValue) { + SpanName annotation = AnnotationUtils + .findAnnotation(object.getClass(), SpanName.class); + String spanName = annotation != null ? annotation.value() : object.toString(); + // If there is no overridden toString method we'll put a constant value + if (isDefaultToString(object, spanName)) { + return defaultValue; + } + return spanName; + } + + private static boolean isDefaultToString(Object delegate, String spanName) { + return (delegate.getClass().getName() + "@" + + Integer.toHexString(delegate.hashCode())).equals(spanName); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java index e3b91edd7..fb46f25f4 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java @@ -53,6 +53,7 @@ public class Span { public static final List HEADERS = Arrays.asList(SPAN_ID_NAME, TRACE_ID_NAME, SPAN_NAME_NAME, PARENT_ID_NAME, PROCESS_ID_NAME, NOT_SAMPLED_NAME); public static final String SPAN_EXPORT_NAME = "X-Span-Export"; + public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc"; private final long begin; private long end = 0; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanName.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanName.java new file mode 100644 index 000000000..a7e2d75f1 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanName.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2016 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; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * Annotation to provide the name for the Span. You should annotate all your + * custom {@link java.lang.Runnable} or {@link java.util.concurrent.Callable} classes + * for the instrumentation logic to pick up how to name the span. + * + * If you're using anonymous instances for those classes then you should override the + * {@code toString()} method. That way that value will be picked as a span name at + * runtime. + * + * @author Marcin Grzejszczak + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SpanName { + /** + * Name of the span to be resolved at runtime + */ + String value(); +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanNamer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanNamer.java new file mode 100644 index 000000000..3dd2327bb --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanNamer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013-2016 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; + +/** + * Describes how for a given object a span should be named. In the vast majority + * of cases a name should be provided explicitly. In case of instrumentation + * where the name has to be resolved at runtime this interface will provide + * the name of the span. + * + * @author Marcin Grzejszczak + */ +public interface SpanNamer { + + /** + * Retrieves the span name for the given object. + * + * @param object - object for which span name should be picked + * @param defaultValue - the default valued to be returned if span name can't be calculated + * @return span name + */ + String name(Object object, String defaultValue); +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java index 80625d519..8895f1c06 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java @@ -25,8 +25,11 @@ 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.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Sampler; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.cloud.sleuth.metric.CounterServiceBasedSpanReporterService; import org.springframework.cloud.sleuth.metric.NoOpSpanReporterService; import org.springframework.cloud.sleuth.metric.SleuthMetricProperties; @@ -60,8 +63,9 @@ public class TraceAutoConfiguration { @Bean @ConditionalOnMissingBean(Tracer.class) public DefaultTracer traceManager(Sampler sampler, Random random, - ApplicationEventPublisher publisher) { - return new DefaultTracer(sampler, random, publisher); + ApplicationEventPublisher publisher, + SpanNamer spanNamer) { + return new DefaultTracer(sampler, random, publisher, spanNamer); } @Bean @@ -70,6 +74,18 @@ public class TraceAutoConfiguration { return new SleuthMetricProperties(); } + @Bean + @ConditionalOnMissingBean + public TraceKeys traceKeys() { + return new TraceKeys(); + } + + @Bean + @ConditionalOnMissingBean + public SpanNamer spanNamer() { + return new DefaultSpanNamer(); + } + @Configuration @ConditionalOnClass(CounterService.class) @ConditionalOnMissingBean(SpanReporterService.class) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceKeys.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceKeys.java index 5616b4f22..dd5ebed39 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceKeys.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceKeys.java @@ -53,6 +53,10 @@ public class TraceKeys { private Message message = new Message(); + private Hystrix hystrix = new Hystrix(); + + private Async async = new Async(); + public Http getHttp() { return this.http; } @@ -61,6 +65,14 @@ public class TraceKeys { return this.message; } + public Hystrix getHystrix() { + return this.hystrix; + } + + public Async getAsync() { + return this.async; + } + public void setHttp(Http http) { this.http = http; } @@ -69,6 +81,14 @@ public class TraceKeys { this.message = message; } + public void setHystrix(Hystrix hystrix) { + this.hystrix = hystrix; + } + + public void setAsync(Async async) { + this.async = async; + } + public static class Message { private Payload payload = new Payload(); @@ -283,4 +303,97 @@ public class TraceKeys { } } + /** + * Trace keys related to Hystrix processing + */ + public static class Hystrix { + + /** + * Prefix for header names if they are added as tags. + */ + private String prefix = ""; + + /** + * Name of the command key + */ + private String commandKey = "commandKey"; + + public String getPrefix() { + return this.prefix; + } + + public String getCommandKey() { + return this.commandKey; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public void setCommandKey(String commandKey) { + this.commandKey = commandKey; + } + + } + + /** + * Trace keys related to async processing + */ + public static class Async { + + /** + * Prefix for header names if they are added as tags. + */ + private String prefix = ""; + + /** + * Name of the thread that executed the async method + */ + private String threadNameKey = "thread"; + + /** + * Simple name of the class with a method annotated with {@code @Async} + * from which the asynchronous process started + */ + private String classNameKey = "class"; + + /** + * Name of the method annotated with {@code @Async} + */ + private String methodNameKey = "method"; + + public String getPrefix() { + return this.prefix; + } + + public String getThreadNameKey() { + return this.threadNameKey; + } + + public String getClassNameKey() { + return this.classNameKey; + } + + public String getMethodNameKey() { + return this.methodNameKey; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public void setThreadNameKey(String threadNameKey) { + this.threadNameKey = threadNameKey; + } + + public void setClassNameKey(String classNameKey) { + this.classNameKey = classNameKey; + } + + public void setMethodNameKey(String methodNameKey) { + this.methodNameKey = methodNameKey; + } + + } + } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java index 5eb1278e6..1826c8cd7 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/AsyncDefaultAutoConfiguration.java @@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.SimpleAsyncTaskExecutor; @@ -49,8 +50,8 @@ public class AsyncDefaultAutoConfiguration extends AsyncConfigurerSupport { } @Bean - public TraceAsyncAspect traceAsyncAspect(Tracer tracer) { - return new TraceAsyncAspect(tracer); + public TraceAsyncAspect traceAsyncAspect(Tracer tracer, TraceKeys traceKeys) { + return new TraceAsyncAspect(tracer, traceKeys); } } \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java index b11cfbc3a..f524c4814 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LazyTraceExecutor.java @@ -16,11 +16,17 @@ package org.springframework.cloud.sleuth.instrument.async; +import java.lang.invoke.MethodHandles; import java.util.concurrent.Executor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.cloud.sleuth.DefaultSpanNamer; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; /** * @author Dave Syer @@ -28,9 +34,13 @@ import org.springframework.cloud.sleuth.Tracer; */ public class LazyTraceExecutor implements Executor { + private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()); + private Tracer tracer; private final BeanFactory beanFactory; private final Executor delegate; + private TraceKeys traceKeys; + private SpanNamer spanNamer; public LazyTraceExecutor(BeanFactory beanFactory, Executor delegate) { this.beanFactory = beanFactory; @@ -45,9 +55,38 @@ public class LazyTraceExecutor implements Executor { } catch (NoSuchBeanDefinitionException e) { this.delegate.execute(command); + return; } } - this.delegate.execute(new TraceRunnable(this.tracer, command)); + this.delegate.execute(new LocalComponentTraceRunnable(this.tracer, traceKeys(), spanNamer(), command)); + } + + // due to some race conditions trace keys might not be ready yet + private TraceKeys traceKeys() { + if (this.traceKeys == null) { + try { + this.traceKeys = this.beanFactory.getBean(TraceKeys.class); + } + catch (NoSuchBeanDefinitionException e) { + log.warn("TraceKeys bean not found - will provide a manually created instance"); + return new TraceKeys(); + } + } + return this.traceKeys; + } + + // due to some race conditions trace keys might not be ready yet + private SpanNamer spanNamer() { + if (this.spanNamer == null) { + try { + this.spanNamer = this.beanFactory.getBean(SpanNamer.class); + } + catch (NoSuchBeanDefinitionException e) { + log.warn("SpanNamer bean not found - will provide a manually created instance"); + return new DefaultSpanNamer(); + } + } + return this.spanNamer; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceCallable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceCallable.java new file mode 100644 index 000000000..a5e9754f0 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceCallable.java @@ -0,0 +1,69 @@ +/* + * Copyright 2013-2016 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.instrument.async; + +import java.util.concurrent.Callable; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; + +/** + * + * Callable that starts a span that is a local component span. + * + * @author Marcin Grzejszczak + */ +public class LocalComponentTraceCallable extends TraceCallable { + + protected static final String ASYNC_COMPONENT = "async"; + + private final TraceKeys traceKeys; + + public LocalComponentTraceCallable(Tracer tracer, TraceKeys traceKeys, + SpanNamer spanNamer, Callable delegate) { + super(tracer, spanNamer, delegate); + this.traceKeys = traceKeys; + } + + public LocalComponentTraceCallable(Tracer tracer, TraceKeys traceKeys, + SpanNamer spanNamer, String name, Callable delegate) { + super(tracer, spanNamer, delegate, name); + this.traceKeys = traceKeys; + } + + @Override + public V call() throws Exception { + Span span = startSpan(); + try { + return this.getDelegate().call(); + } + finally { + close(span); + } + } + + @Override + protected Span startSpan() { + Span span = getTracer().joinTrace(getSpanName(), getParent()); + getTracer().addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT); + getTracer().addTag(this.traceKeys.getAsync().getPrefix() + + this.traceKeys.getAsync().getThreadNameKey(), Thread.currentThread().getName()); + return span; + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceRunnable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceRunnable.java new file mode 100644 index 000000000..8439d2eb2 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/LocalComponentTraceRunnable.java @@ -0,0 +1,67 @@ +/* + * Copyright 2013-2016 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.instrument.async; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; + +/** + * + * Runnable that starts a span that is a local component span. + * + * @author Marcin Grzejszczak + */ +public class LocalComponentTraceRunnable extends TraceRunnable { + + protected static final String ASYNC_COMPONENT = "async"; + + private final TraceKeys traceKeys; + + public LocalComponentTraceRunnable(Tracer tracer, TraceKeys traceKeys, + SpanNamer spanNamer, Runnable delegate) { + super(tracer, spanNamer, delegate); + this.traceKeys = traceKeys; + } + + public LocalComponentTraceRunnable(Tracer tracer, TraceKeys traceKeys, + SpanNamer spanNamer, Runnable delegate, String name) { + super(tracer, spanNamer, delegate, name); + this.traceKeys = traceKeys; + } + + @Override + public void run() { + Span span = startSpan(); + try { + this.getDelegate().run(); + } + finally { + close(span); + } + } + + @Override + protected Span startSpan() { + Span span = getTracer().joinTrace(getSpanName(), getParent()); + getTracer().addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT); + getTracer().addTag(this.traceKeys.getAsync().getPrefix() + + this.traceKeys.getAsync().getThreadNameKey(), Thread.currentThread().getName()); + return span; + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java index 4c2d694e5..3cd7f50dc 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceAsyncAspect.java @@ -21,6 +21,7 @@ import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; /** * Aspect that creates a new Span for running threads executing methods annotated with @@ -36,15 +37,21 @@ public class TraceAsyncAspect { private static final String ASYNC_COMPONENT = "async"; private final Tracer tracer; + private final TraceKeys traceKeys; - public TraceAsyncAspect(Tracer tracer) { + public TraceAsyncAspect(Tracer tracer, TraceKeys traceKeys) { this.tracer = tracer; + this.traceKeys = traceKeys; } @Around("execution (@org.springframework.scheduling.annotation.Async * *.*(..))") public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable { - String spanName = ASYNC_COMPONENT + ":" + pjp.getTarget().getClass().getSimpleName(); - Span span = this.tracer.startTrace(spanName); + Span span = this.tracer.startTrace(pjp.getSignature().getName()); + this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ASYNC_COMPONENT); + this.tracer.addTag(this.traceKeys.getAsync().getPrefix() + + this.traceKeys.getAsync().getClassNameKey(), pjp.getTarget().getClass().getSimpleName()); + this.tracer.addTag(this.traceKeys.getAsync().getPrefix() + + this.traceKeys.getAsync().getMethodNameKey(), pjp.getSignature().getName()); try { return pjp.proceed(); } finally { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java index 8b1b5968c..d57c26c80 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceCallable.java @@ -19,19 +19,35 @@ package org.springframework.cloud.sleuth.instrument.async; import java.util.concurrent.Callable; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; /** + * Callable that passes Span between threads. The Span name is + * taken either from the passed value or from the {@link SpanNamer} + * interface. + * * @author Spencer Gibb + * @author Marcin Grzejszczak */ -public class TraceCallable extends TraceDelegate> implements Callable { +public class TraceCallable implements Callable { - public TraceCallable(Tracer tracer, Callable delegate) { - super(tracer, delegate); + private final Tracer tracer; + private final SpanNamer spanNamer; + private final Callable delegate; + private final String name; + private final Span parent; + + public TraceCallable(Tracer tracer, SpanNamer spanNamer, Callable delegate) { + this(tracer, spanNamer, delegate, null); } - public TraceCallable(Tracer tracer, Callable delegate, String name) { - super(tracer, delegate, name); + public TraceCallable(Tracer tracer, SpanNamer spanNamer, Callable delegate, String name) { + this.tracer = tracer; + this.spanNamer = spanNamer; + this.delegate = delegate; + this.name = name; + this.parent = tracer.getCurrentSpan(); } @Override @@ -45,4 +61,35 @@ public class TraceCallable extends TraceDelegate> implements Call } } + protected Span startSpan() { + return this.tracer.joinTrace(getSpanName(), this.parent); + } + + protected String getSpanName() { + if (this.name != null) { + return this.name; + } + return this.spanNamer.name(this.delegate, "async"); + } + + protected void close(Span span) { + this.tracer.close(span); + } + + public Tracer getTracer() { + return this.tracer; + } + + public Callable getDelegate() { + return this.delegate; + } + + public String getName() { + return this.name; + } + + public Span getParent() { + return this.parent; + } + } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceContinuingCallable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceContinuingCallable.java new file mode 100644 index 000000000..fb3a37c5f --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceContinuingCallable.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013-2016 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.instrument.async; + +import java.util.concurrent.Callable; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; +import org.springframework.cloud.sleuth.Tracer; + +/** + * Trace Callable that continues a span instead of creating a new one + * + * @author Marcin Grzejszczak + */ +public class TraceContinuingCallable extends TraceCallable implements Callable { + + public TraceContinuingCallable(Tracer tracer, SpanNamer spanNamer, Callable delegate) { + super(tracer, spanNamer, delegate); + } + + @Override + protected Span startSpan() { + return getTracer().continueSpan(getParent()); + } + + @Override + protected void close(Span span) { + getTracer().detach(span); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceDelegate.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceDelegate.java deleted file mode 100644 index f5febe13c..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceDelegate.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013-2015 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.instrument.async; - -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Tracer; - -/** - * @author Spencer Gibb - */ -public abstract class TraceDelegate { - - private static final String ASYNC_COMPONENT = "async"; - - private final Tracer tracer; - private final T delegate; - private final String name; - private final Span parent; - - public TraceDelegate(Tracer tracer, T delegate) { - this(tracer, delegate, null); - } - - public TraceDelegate(Tracer tracer, T delegate, String name) { - this.tracer = tracer; - this.delegate = delegate; - this.name = name; - this.parent = tracer.getCurrentSpan(); - } - - protected void close(Span span) { - this.tracer.close(span); - } - - protected Span startSpan() { - return this.tracer.joinTrace(getSpanName(), this.parent); - } - - protected String getSpanName() { - return this.name == null ? - ASYNC_COMPONENT + ":" + Thread.currentThread().getName() - : this.name; - } - - public Tracer getTracer() { - return this.tracer; - } - - public T getDelegate() { - return this.delegate; - } - - public String getName() { - return this.name; - } - - public Span getParent() { - return this.parent; - } - - @Override - public String toString() { - return "TraceDelegate{" + - "tracer=" + this.tracer + - ", delegate=" + this.delegate + - ", name=" + this.name + - ", parent=" + this.parent + - '}'; - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java index 693a4dd65..d79ce6991 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java @@ -17,23 +17,39 @@ package org.springframework.cloud.sleuth.instrument.async; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; /** + * Runnable that passes Span between threads. The Span name is + * taken either from the passed value or from the {@link SpanNamer} + * interface. + * * @author Spencer Gibb + * @author Marcin Grzejszczak */ -public class TraceRunnable extends TraceDelegate implements Runnable { +public class TraceRunnable implements Runnable { - public TraceRunnable(Tracer tracer, Runnable delegate) { - super(tracer, delegate); + private final Tracer tracer; + private final SpanNamer spanNamer; + private final Runnable delegate; + private final String name; + private final Span parent; + + public TraceRunnable(Tracer tracer, SpanNamer spanNamer, Runnable delegate) { + this(tracer, spanNamer, delegate, null); } - public TraceRunnable(Tracer tracer, Runnable delegate, String name) { - super(tracer, delegate, name); + public TraceRunnable(Tracer tracer, SpanNamer spanNamer, Runnable delegate, String name) { + this.tracer = tracer; + this.spanNamer = spanNamer; + this.delegate = delegate; + this.name = name; + this.parent = tracer.getCurrentSpan(); } @Override - public void run() { + public void run() { Span span = startSpan(); try { this.getDelegate().run(); @@ -42,4 +58,35 @@ public class TraceRunnable extends TraceDelegate implements Runnable { close(span); } } + + protected Span startSpan() { + return this.tracer.joinTrace(getSpanName(), this.parent); + } + + protected String getSpanName() { + if (this.name != null) { + return this.name; + } + return this.spanNamer.name(this.delegate, "async"); + } + + protected void close(Span span) { + this.tracer.close(span); + } + + public Tracer getTracer() { + return this.tracer; + } + + public Runnable getDelegate() { + return this.delegate; + } + + public String getName() { + return this.name; + } + + public Span getParent() { + return this.parent; + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java index 0e2aa0f41..99f885f77 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorService.java @@ -24,7 +24,10 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; + /** * A decorator class for {@link ExecutorService} to support tracing in Executors * @author Gaurav Rai Mazra @@ -34,20 +37,27 @@ public class TraceableExecutorService implements ExecutorService { final ExecutorService delegate; final Tracer tracer; private final String spanName; + final TraceKeys traceKeys; + final SpanNamer spanNamer; - public TraceableExecutorService(final ExecutorService delegate, final Tracer tracer) { - this(delegate, tracer, null); + public TraceableExecutorService(final ExecutorService delegate, final Tracer tracer, + TraceKeys traceKeys, SpanNamer spanNamer) { + this(delegate, tracer, traceKeys, spanNamer, null); } - public TraceableExecutorService(final ExecutorService delegate, final Tracer tracer, String spanName) { + public TraceableExecutorService(final ExecutorService delegate, final Tracer tracer, + TraceKeys traceKeys, SpanNamer spanNamer, String spanName) { this.delegate = delegate; this.tracer = tracer; this.spanName = spanName; + this.traceKeys = traceKeys; + this.spanNamer = spanNamer; } @Override public void execute(Runnable command) { - final Runnable r = new TraceRunnable(this.tracer, command, this.spanName); + final Runnable r = new LocalComponentTraceRunnable(this.tracer, this.traceKeys, + this.spanNamer, command, this.spanName); this.delegate.execute(r); } @@ -78,19 +88,22 @@ public class TraceableExecutorService implements ExecutorService { @Override public Future submit(Callable task) { - Callable c = new TraceCallable<>(this.tracer, task, this.spanName); + Callable c = new LocalComponentTraceCallable<>(this.tracer, this.traceKeys, + this.spanNamer, this.spanName, task); return this.delegate.submit(c); } @Override public Future submit(Runnable task, T result) { - Runnable r = new TraceRunnable(this.tracer, task, this.spanName); + Runnable r = new LocalComponentTraceRunnable(this.tracer, this.traceKeys, + this.spanNamer, task, this.spanName); return this.delegate.submit(r, result); } @Override public Future submit(Runnable task) { - Runnable r = new TraceRunnable(this.tracer, task, this.spanName); + Runnable r = new LocalComponentTraceRunnable(this.tracer, this.traceKeys, + this.spanNamer, task, this.spanName); return this.delegate.submit(r); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java index 5e271ebce..3c6c044e8 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceableScheduledExecutorService.java @@ -21,7 +21,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; /** * A decorator class for {@link ScheduledExecutorService} to support tracing in Executors @@ -30,8 +32,9 @@ import org.springframework.cloud.sleuth.Tracer; */ public class TraceableScheduledExecutorService extends TraceableExecutorService implements ScheduledExecutorService { - public TraceableScheduledExecutorService(final ScheduledExecutorService delegate, final Tracer tracer) { - super(delegate, tracer); + public TraceableScheduledExecutorService(final ScheduledExecutorService delegate, + final Tracer tracer, TraceKeys traceKeys, SpanNamer spanNamer) { + super(delegate, tracer, traceKeys, spanNamer); } private ScheduledExecutorService getScheduledExecutorService() { @@ -40,27 +43,25 @@ public class TraceableScheduledExecutorService extends TraceableExecutorService @Override public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - Runnable r = new TraceRunnable(this.tracer, command); + Runnable r = new LocalComponentTraceRunnable(this.tracer, this.traceKeys, this.spanNamer, command); return getScheduledExecutorService().schedule(r, delay, unit); } - - @Override public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { - Callable c = new TraceCallable<>(this.tracer,callable); + Callable c = new LocalComponentTraceCallable<>(this.tracer, this.traceKeys, this.spanNamer, callable); return getScheduledExecutorService().schedule(c, delay, unit); } @Override public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - Runnable r = new TraceRunnable(this.tracer, command); + Runnable r = new LocalComponentTraceRunnable(this.tracer, this.traceKeys, this.spanNamer, command); return getScheduledExecutorService().scheduleAtFixedRate(r, initialDelay, period, unit); } @Override public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { - Runnable r = new TraceRunnable(this.tracer, command); + Runnable r = new LocalComponentTraceRunnable(this.tracer, this.traceKeys, this.spanNamer, command); return getScheduledExecutorService().scheduleWithFixedDelay(r, initialDelay, delay, unit); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java index 4017f602f..4b341eccb 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixAutoConfiguration.java @@ -3,6 +3,7 @@ package org.springframework.cloud.sleuth.instrument.hystrix; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,7 +15,7 @@ import com.netflix.hystrix.HystrixCommand; public class SleuthHystrixAutoConfiguration { @Bean - SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracer tracer) { - return new SleuthHystrixConcurrencyStrategy(tracer); + SleuthHystrixConcurrencyStrategy sleuthHystrixConcurrencyStrategy(Tracer tracer, TraceKeys traceKeys) { + return new SleuthHystrixConcurrencyStrategy(tracer, traceKeys); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java index d9ac5b109..8394b3b19 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/SleuthHystrixConcurrencyStrategy.java @@ -1,15 +1,14 @@ package org.springframework.cloud.sleuth.instrument.hystrix; -import java.util.concurrent.Callable; - import javax.annotation.PreDestroy; - -import org.slf4j.Logger; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Tracer; +import java.util.concurrent.Callable; import com.netflix.hystrix.strategy.HystrixPlugins; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; +import org.slf4j.Logger; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { @@ -18,9 +17,11 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy .getLogger(SleuthHystrixConcurrencyStrategy.class); private final Tracer tracer; + private final TraceKeys traceKeys; - public SleuthHystrixConcurrencyStrategy(Tracer tracer) { + public SleuthHystrixConcurrencyStrategy(Tracer tracer, TraceKeys traceKeys) { this.tracer = tracer; + this.traceKeys = traceKeys; try { HystrixPlugins.getInstance().registerConcurrencyStrategy(this); } @@ -41,17 +42,19 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy @Override public Callable wrapCallable(Callable callable) { - return new HystrixTraceCallable(this.tracer, callable); + return new HystrixTraceCallable(this.tracer, this.traceKeys, callable); } private static class HystrixTraceCallable implements Callable { private Tracer tracer; + private TraceKeys traceKeys; private Callable callable; private Span parent; - public HystrixTraceCallable(Tracer tracer, Callable callable) { + public HystrixTraceCallable(Tracer tracer, TraceKeys traceKeys, Callable callable) { this.tracer = tracer; + this.traceKeys = traceKeys; this.callable = callable; this.parent = tracer.getCurrentSpan(); } @@ -64,8 +67,10 @@ public class SleuthHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy span = this.tracer.continueSpan(span); } else { - span = this.tracer.startTrace(HYSTRIX_COMPONENT + - ":" + Thread.currentThread().getName()); + span = this.tracer.startTrace(HYSTRIX_COMPONENT); + this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, HYSTRIX_COMPONENT); + this.tracer.addTag(this.traceKeys.getAsync().getPrefix() + + this.traceKeys.getAsync().getThreadNameKey(), Thread.currentThread().getName()); created = true; } try { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommand.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommand.java index ee51dee65..6c89f8bc6 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommand.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommand.java @@ -20,6 +20,7 @@ import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; import com.netflix.hystrix.HystrixCommand; +import org.springframework.cloud.sleuth.instrument.TraceKeys; /** * Abstraction over {@code HystrixCommand} that wraps command execution with Trace setting @@ -36,18 +37,23 @@ public abstract class TraceCommand extends HystrixCommand { private static final String HYSTRIX_COMPONENT = "hystrix"; private final Tracer tracer; + private final TraceKeys traceKeys; private final Span parentSpan; - protected TraceCommand(Tracer tracer, Setter setter) { + protected TraceCommand(Tracer tracer, TraceKeys traceKeys, Setter setter) { super(setter); this.tracer = tracer; + this.traceKeys = traceKeys; this.parentSpan = tracer.getCurrentSpan(); } @Override protected R run() throws Exception { - String spanName = HYSTRIX_COMPONENT + ":" + getCommandKey().name(); - Span span = this.tracer.joinTrace(spanName, this.parentSpan); + String commandKeyName = getCommandKey().name(); + Span span = this.tracer.joinTrace(commandKeyName, this.parentSpan); + this.tracer.addTag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, HYSTRIX_COMPONENT); + this.tracer.addTag(this.traceKeys.getHystrix().getPrefix() + + this.traceKeys.getHystrix().getCommandKey(), commandKeyName); try { return doRun(); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java index e5bd45be4..391183717 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java @@ -25,8 +25,9 @@ import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; -import org.springframework.cloud.sleuth.instrument.async.TraceCallable; +import org.springframework.cloud.sleuth.instrument.async.TraceContinuingCallable; import org.springframework.web.context.request.async.WebAsyncTask; /** @@ -43,14 +44,19 @@ import org.springframework.web.context.request.async.WebAsyncTask; * *

    * For controllers an around aspect is created that wraps the {@link Callable#call()} - * method execution in {@link TraceCallable} + * method execution in {@link org.springframework.cloud.sleuth.instrument.async.TraceCallable} *

    * + * This aspect will continue a span created by the TraceFilter. It will not create + * a new span - since the one in TraceFilter will wait until processing has been + * finished + * * @see org.springframework.web.bind.annotation.RestController * @see org.springframework.stereotype.Controller * @see org.springframework.web.client.RestOperations - * @see TraceCallable + * @see org.springframework.cloud.sleuth.instrument.async.TraceCallable * @see Tracer + * @see TraceFilter * * @author Tomasz Nurkewicz, 4financeIT * @author Marcin Grzejszczak, 4financeIT @@ -60,16 +66,17 @@ import org.springframework.web.context.request.async.WebAsyncTask; @Aspect public class TraceWebAspect { - private static final String ASYNC_COMPONENT = "async"; private static final Log log = org.apache.commons.logging.LogFactory .getLog(TraceWebAspect.class); private final Tracer tracer; private final SpanAccessor accessor; + private final SpanNamer spanNamer; - public TraceWebAspect(Tracer tracer, SpanAccessor accessor) { + public TraceWebAspect(Tracer tracer, SpanAccessor accessor, SpanNamer spanNamer) { this.tracer = tracer; this.accessor = accessor; + this.spanNamer = spanNamer; } @Pointcut("@within(org.springframework.web.bind.annotation.RestController)") @@ -103,19 +110,13 @@ public class TraceWebAspect { if (this.accessor.isTracing()) { log.debug("Wrapping callable with span [" + this.accessor.getCurrentSpan() + "]"); - return new TraceCallable<>(this.tracer, callable); + return new TraceContinuingCallable<>(this.tracer, this.spanNamer, callable); } else { return callable; } } - private String spanName(ProceedingJoinPoint pjp) { - return ASYNC_COMPONENT + ":" + - pjp.getTarget().getClass().getSimpleName() + "#" + - "method=" + pjp.getSignature().getName(); - } - @Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()") public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { final WebAsyncTask webAsyncTask = (WebAsyncTask) pjp.proceed(); @@ -125,8 +126,8 @@ public class TraceWebAspect { + this.accessor.getCurrentSpan() + "]"); Field callableField = WebAsyncTask.class.getDeclaredField("callable"); callableField.setAccessible(true); - callableField.set(webAsyncTask, new TraceCallable<>(this.tracer, - webAsyncTask.getCallable(), spanName(pjp))); + callableField.set(webAsyncTask, new TraceContinuingCallable<>(this.tracer, + this.spanNamer, webAsyncTask.getCallable())); } catch (NoSuchFieldException ex) { log.warn("Cannot wrap webAsyncTask's callable with TraceCallable", ex); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java index b144bce01..76ddcc029 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.cloud.sleuth.instrument.TraceKeys; @@ -67,8 +68,8 @@ public class TraceWebAutoConfiguration { private TraceKeys traceKeys; @Bean - public TraceWebAspect traceWebAspect() { - return new TraceWebAspect(this.tracer, this.accessor); + public TraceWebAspect traceWebAspect(SpanNamer spanNamer) { + return new TraceWebAspect(this.tracer, this.accessor, spanNamer); } @Bean diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java index c8fdc2092..f22d69d75 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java @@ -53,7 +53,7 @@ abstract class AbstractTraceHttpRequestInterceptor if (!span.isExportable()) { setHeader(request, Span.NOT_SAMPLED_NAME, "true"); } - setHeader(request, Span.SPAN_NAME_NAME, span.getName().toString()); + setHeader(request, Span.SPAN_NAME_NAME, span.getName()); setIdHeader(request, Span.PARENT_ID_NAME, getParentId(span)); setHeader(request, Span.PROCESS_ID_NAME, span.getProcessId()); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthHystrixInvocationHandler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthHystrixInvocationHandler.java index c4eac4a68..76e38ddb1 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthHystrixInvocationHandler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/SleuthHystrixInvocationHandler.java @@ -15,6 +15,10 @@ */ package org.springframework.cloud.sleuth.instrument.web.client; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Map; + import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; @@ -22,12 +26,9 @@ import feign.InvocationHandlerFactory; import feign.InvocationHandlerFactory.MethodHandler; import feign.Target; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.cloud.sleuth.instrument.hystrix.TraceCommand; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.util.Map; - import static feign.Util.checkNotNull; /** @@ -38,12 +39,14 @@ final class SleuthHystrixInvocationHandler implements InvocationHandler { private final Target target; private final Map dispatch; private final Tracer tracer; + private final TraceKeys traceKeys; SleuthHystrixInvocationHandler(Target target, Map dispatch, - Tracer tracer) { + Tracer tracer, TraceKeys traceKeys) { this.tracer = checkNotNull(tracer, "traceManager"); this.target = checkNotNull(target, "target"); this.dispatch = checkNotNull(dispatch, "dispatch"); + this.traceKeys = checkNotNull(traceKeys, "traceKeys"); } @Override public Object invoke(final Object proxy, final Method method, @@ -54,7 +57,7 @@ final class SleuthHystrixInvocationHandler implements InvocationHandler { .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); - HystrixCommand hystrixCommand = new TraceCommand(this.tracer, + HystrixCommand hystrixCommand = new TraceCommand(this.tracer, this.traceKeys, setter) { @Override public Object doRun() throws Exception { try { @@ -79,15 +82,18 @@ final class SleuthHystrixInvocationHandler implements InvocationHandler { static final class Factory implements InvocationHandlerFactory { private final Tracer tracer; + private final TraceKeys traceKeys; - public Factory(Tracer tracer) { + public Factory(Tracer tracer, TraceKeys traceKeys) { this.tracer = tracer; + this.traceKeys = traceKeys; } @Override public InvocationHandler create( @SuppressWarnings("rawtypes") Target target, Map dispatch) { - return new SleuthHystrixInvocationHandler(target, dispatch, this.tracer); + return new SleuthHystrixInvocationHandler(target, dispatch, this.tracer, + this.traceKeys); } } } \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java index 56e81e872..511a60c02 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java @@ -40,6 +40,7 @@ import org.springframework.cloud.sleuth.SpanAccessor; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.event.ClientReceivedEvent; import org.springframework.cloud.sleuth.event.ClientSentEvent; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration; import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixConcurrencyStrategy; import org.springframework.context.ApplicationEvent; @@ -88,9 +89,9 @@ public class TraceFeignClientAutoConfiguration { @ConditionalOnClass(HystrixCommand.class) @ConditionalOnMissingBean(SleuthHystrixConcurrencyStrategy.class) @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true) - public Feign.Builder feignHystrixBuilder(Tracer tracer) { + public Feign.Builder feignHystrixBuilder(Tracer tracer, TraceKeys traceKeys) { return HystrixFeign.builder().invocationHandlerFactory( - new SleuthHystrixInvocationHandler.Factory(tracer)); + new SleuthHystrixInvocationHandler.Factory(tracer, traceKeys)); } @Bean diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java index 399e9303f..aad7dc401 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java @@ -21,6 +21,7 @@ import java.util.concurrent.Callable; import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.event.SpanAcquiredEvent; import org.springframework.cloud.sleuth.event.SpanContinuedEvent; @@ -41,11 +42,14 @@ public class DefaultTracer implements Tracer { private final Random random; + private final SpanNamer spanNamer; + public DefaultTracer(Sampler defaultSampler, Random random, - ApplicationEventPublisher publisher) { + ApplicationEventPublisher publisher, SpanNamer spanNamer) { this.defaultSampler = defaultSampler; this.random = random; this.publisher = publisher; + this.spanNamer = spanNamer; } @Override @@ -198,7 +202,7 @@ public class DefaultTracer implements Tracer { @Override public Callable wrap(Callable callable) { if (isTracing()) { - return new TraceCallable<>(this, callable); + return new TraceCallable<>(this, this.spanNamer, callable); } return callable; } @@ -211,7 +215,7 @@ public class DefaultTracer implements Tracer { @Override public Runnable wrap(Runnable runnable) { if (isTracing()) { - return new TraceRunnable(this, runnable); + return new TraceRunnable(this, this.spanNamer, runnable); } return runnable; } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultSpanNamerTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultSpanNamerTest.java new file mode 100644 index 000000000..0c86fabac --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultSpanNamerTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2016 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; + +import org.junit.Test; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * @author Marcin Grzejszczak + */ +public class DefaultSpanNamerTest { + + DefaultSpanNamer defaultSpanNamer = new DefaultSpanNamer(); + + @Test + public void should_return_value_of_span_name_from_annotation() throws Exception { + then(this.defaultSpanNamer.name(new ClassWithAnnotation(), "default")).isEqualTo("somevalue"); + } + + @Test + public void should_return_value_of_span_name_from_to_string_if_annotation_is_missing() throws Exception { + then(this.defaultSpanNamer.name(fromAnonymousClassWithCustomToString(), "default")).isEqualTo("some-other-value"); + } + + @Test + public void should_return_default_value_if_tostring_wasnt_overridden() throws Exception { + then(this.defaultSpanNamer.name(new ClassWithoutToString(), "default")).isEqualTo("default"); + } + + @SpanName("somevalue") + static class ClassWithAnnotation {} + + private Runnable fromAnonymousClassWithCustomToString() { + return new Runnable() { + @Override + public void run() { + + } + + @Override + public String toString() { + return "some-other-value"; + } + }; + } + + static class ClassWithoutToString {} +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java index 7afaea734..5489204bf 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java @@ -54,6 +54,16 @@ public class SpanAssert extends AbstractAssert { return this; } + public SpanAssert nameStartsWith(String string) { + isNotNull(); + if (!this.actual.getName().startsWith(string)) { + String message = String.format("Expected span's name to start with <%s> but it was equal to <%s>", string, this.actual.getName()); + log.error(message); + failWithMessage(message); + } + return this; + } + public SpanAssert hasNameNotEqualTo(String name) { isNotNull(); if (Objects.equals(this.actual.getName(), name)) { @@ -63,4 +73,33 @@ public class SpanAssert extends AbstractAssert { } return this; } + + public SpanAssert isALocalComponentSpan() { + isNotNull(); + if (!this.actual.tags().containsKey(Span.SPAN_LOCAL_COMPONENT_TAG_NAME)) { + String message = String.format("Expected span to be a local component. " + + "LC tag is missing. Found tags are <%s>", this.actual.tags()); + log.error(message); + failWithMessage(message); + } + return this; + } + + public SpanAssert hasATag(String tagKey, String tagValue) { + isNotNull(); + if (!this.actual.tags().containsKey(tagKey)) { + String message = String.format("Expected span to have the tag with key <%s>. " + + "Found tags are <%s>", tagKey, this.actual.tags()); + log.error(message); + failWithMessage(message); + } + String foundTagValue = this.actual.tags().get(tagKey); + if (!foundTagValue.equals(tagValue)) { + String message = String.format("Expected span to have the tag with key <%s> and value <%s>. " + + "Found value for that tag is <%s>", tagKey, tagValue, foundTagValue); + log.error(message); + failWithMessage(message); + } + return this; + } } \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java index 08608ba1a..90ce7746b 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceCallableTests.java @@ -10,22 +10,23 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanName; import org.springframework.cloud.sleuth.Tracer; -import org.springframework.cloud.sleuth.instrument.async.TraceCallable; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; import org.springframework.context.ApplicationEventPublisher; -import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; @RunWith(MockitoJUnitRunner.class) public class TraceCallableTests { ExecutorService executor = Executors.newSingleThreadExecutor(); Tracer tracer = new DefaultTracer(new AlwaysSampler(), - new Random(), Mockito.mock(ApplicationEventPublisher.class)); + new Random(), Mockito.mock(ApplicationEventPublisher.class), new DefaultSpanNamer()); @After public void clean() { @@ -71,6 +72,23 @@ public class TraceCallableTests { then(secondSpan).isNull(); } + @Test + public void should_take_name_of_span_from_span_name_annotation() + throws Exception { + Span span = whenATraceKeepingCallableGetsSubmitted(); + + then(span).hasNameEqualTo("some-callable-name-from-annotation"); + } + + @Test + public void should_take_name_of_span_from_to_string_if_span_name_annotation_is_missing() + throws Exception { + Span span = whenCallableGetsSubmitted( + thatRetrievesTraceFromThreadLocal()); + + then(span).hasNameEqualTo("some-callable-name-from-to-string"); + } + private Span givenSpanIsAlreadyActive() { return this.tracer.startTrace("http:parent"); } @@ -81,6 +99,11 @@ public class TraceCallableTests { public Span call() throws Exception { return TestSpanContextHolder.getCurrentSpan(); } + + @Override + public String toString() { + return "some-callable-name-from-to-string"; + } }; } @@ -91,13 +114,29 @@ public class TraceCallableTests { private Span whenCallableGetsSubmitted(Callable callable) throws InterruptedException, java.util.concurrent.ExecutionException { - return this.executor.submit(new TraceCallable<>(this.tracer, callable)) + return this.executor.submit(new TraceCallable<>(this.tracer, new DefaultSpanNamer(), callable)) .get(); } + private Span whenATraceKeepingCallableGetsSubmitted() + throws InterruptedException, java.util.concurrent.ExecutionException { + return this.executor.submit(new TraceCallable<>(this.tracer, new DefaultSpanNamer(), + new TraceKeepingCallable())).get(); + } private Span whenNonTraceableCallableGetsSubmitted(Callable callable) throws InterruptedException, java.util.concurrent.ExecutionException { return this.executor.submit(callable).get(); } + @SpanName("some-callable-name-from-annotation") + static class TraceKeepingCallable implements Callable { + public Span span; + + @Override + public Span call() throws Exception { + this.span = TestSpanContextHolder.getCurrentSpan(); + return this.span; + } + } + } \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java index 9b4b21514..4fa550747 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnableTests.java @@ -1,22 +1,24 @@ package org.springframework.cloud.sleuth.instrument.async; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanName; import org.springframework.cloud.sleuth.Tracer; -import org.springframework.cloud.sleuth.instrument.async.TraceRunnable; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; import org.springframework.context.ApplicationEventPublisher; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; @RunWith(MockitoJUnitRunner.class) @@ -24,7 +26,7 @@ public class TraceRunnableTests { ExecutorService executor = Executors.newSingleThreadExecutor(); Tracer tracer = new DefaultTracer(new AlwaysSampler(), - new Random(), Mockito.mock(ApplicationEventPublisher.class)); + new Random(), Mockito.mock(ApplicationEventPublisher.class), new DefaultSpanNamer()); @After public void cleanup() { @@ -70,6 +72,27 @@ public class TraceRunnableTests { then(secondSpan).as("unexpected span").isNull(); } + @Test + public void should_take_name_of_span_from_span_name_annotation() + throws Exception { + TraceKeepingRunnable traceKeepingRunnable = runnableThatRetrievesTraceFromThreadLocal(); + + whenRunnableGetsSubmitted(traceKeepingRunnable); + + then(traceKeepingRunnable.span).hasNameEqualTo("some-runnable-name-from-annotation"); + } + + @Test + public void should_take_name_of_span_from_to_string_if_span_name_annotation_is_missing() + throws Exception { + final AtomicReference span = new AtomicReference<>(); + Runnable runnable = runnableWithCustomToString(span); + + whenRunnableGetsSubmitted(runnable); + + then(span.get()).hasNameEqualTo("some-runnable-name-from-to-string"); + } + private TraceKeepingRunnable runnableThatRetrievesTraceFromThreadLocal() { return new TraceKeepingRunnable(); } @@ -78,8 +101,8 @@ public class TraceRunnableTests { whenRunnableGetsSubmitted(runnable); } - private void whenRunnableGetsSubmitted(Runnable callable) throws Exception { - this.executor.submit(new TraceRunnable(this.tracer, callable)).get(); + private void whenRunnableGetsSubmitted(Runnable runnable) throws Exception { + this.executor.submit(new TraceRunnable(this.tracer, new DefaultSpanNamer(), runnable)).get(); } private void whenNonTraceableRunnableGetsSubmitted(Runnable callable) @@ -87,6 +110,20 @@ public class TraceRunnableTests { this.executor.submit(callable).get(); } + private Runnable runnableWithCustomToString(final AtomicReference span) { + return new Runnable() { + @Override + public void run() { + span.set(TestSpanContextHolder.getCurrentSpan()); + } + + @Override public String toString() { + return "some-runnable-name-from-to-string"; + } + }; + } + + @SpanName("some-runnable-name-from-annotation") static class TraceKeepingRunnable implements Runnable { public Span span; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java index e590a164d..44815aee3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java @@ -16,7 +16,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; @@ -30,6 +32,7 @@ public class TraceableExecutorServiceTests { private static int TOTAL_THREADS = 10; @Mock ApplicationEventPublisher publisher; + @Mock SpanNamer spanNamer; Tracer tracer; ExecutorService executorService = Executors.newFixedThreadPool(3); ExecutorService traceManagerableExecutorService; @@ -37,8 +40,10 @@ public class TraceableExecutorServiceTests { @Before public void setup() { - this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), this.publisher); - this.traceManagerableExecutorService = new TraceableExecutorService(this.executorService, this.tracer); + this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), this.publisher, + spanNamer); + this.traceManagerableExecutorService = new TraceableExecutorService(this.executorService, + this.tracer, new TraceKeys(), this.spanNamer); TestSpanContextHolder.removeCurrentSpan(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/HystrixAnnotationsIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/HystrixAnnotationsIntegrationTests.java index 282c11fda..23dbf9179 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/HystrixAnnotationsIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/HystrixAnnotationsIntegrationTests.java @@ -6,15 +6,18 @@ import com.jayway.awaitility.Awaitility; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.strategy.HystrixPlugins; import org.junit.After; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.cloud.netflix.hystrix.EnableHystrix; +import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration; +import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -36,6 +39,7 @@ public class HystrixAnnotationsIntegrationTests { Tracer tracer; @BeforeClass + @AfterClass public static void reset() { HystrixPlugins.reset(); } @@ -43,29 +47,33 @@ public class HystrixAnnotationsIntegrationTests { @After public void cleanTrace() { TestSpanContextHolder.removeCurrentSpan(); - HystrixPlugins.reset(); } @Test - public void should_set_span_on_an_hystrix_command_annotated_method() { + public void should_continue_current_span_when_executed_a_hystrix_command_annotated_method() { Span span = givenASpanInCurrentThread(); whenHystrixCommandAnnotatedMethodGetsExecuted(); - thenTraceIdIsPassedFromTheCurrentThreadToTheHystrixOne(span); + thenSpanInHystrixThreadIsContinued(span); + } + + @Test + public void should_create_new_span_with_thread_name_when_executed_a_hystrix_command_annotated_method() { + whenHystrixCommandAnnotatedMethodGetsExecuted(); + + thenSpanInHystrixThreadIsCreated(); } private Span givenASpanInCurrentThread() { - Span span = this.tracer.startTrace("http:existing"); - this.tracer.continueSpan(span); - return span; + return this.tracer.startTrace("http:existing"); } private void whenHystrixCommandAnnotatedMethodGetsExecuted() { this.catcher.invokeLogicWrappedInHystrixCommand(); } - private void thenTraceIdIsPassedFromTheCurrentThreadToTheHystrixOne(final Span span) { + private void thenSpanInHystrixThreadIsContinued(final Span span) { then(span).isNotNull(); Awaitility.await().until(new Runnable() { @Override @@ -80,6 +88,17 @@ public class HystrixAnnotationsIntegrationTests { }); } + private void thenSpanInHystrixThreadIsCreated() { + Awaitility.await().until(new Runnable() { + @Override + public void run() { + then(HystrixAnnotationsIntegrationTests.this.catcher.getSpan()) + .nameStartsWith("hystrix") + .isALocalComponentSpan(); + } + }); + } + @DefaultTestAutoConfiguration @EnableHystrix @Configuration @@ -90,6 +109,11 @@ public class HystrixAnnotationsIntegrationTests { return new HystrixCommandInvocationSpanCatcher(); } + @Bean + Sampler sampler() { + return new AlwaysSampler(); + } + } static class HystrixCommandInvocationSpanCatcher { @@ -119,5 +143,9 @@ public class HystrixAnnotationsIntegrationTests { } return this.spanCaughtFromHystrixThread.get().getName(); } + + public Span getSpan() { + return this.spanCaughtFromHystrixThread.get(); + } } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommandTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommandTests.java index 6e85ccd9d..569062216 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommandTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/hystrix/TraceCommandTests.java @@ -2,6 +2,7 @@ package org.springframework.cloud.sleuth.instrument.hystrix; import java.util.Random; +import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.strategy.HystrixPlugins; @@ -9,8 +10,10 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.TraceKeys; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; @@ -18,13 +21,13 @@ import org.springframework.context.ApplicationEventPublisher; import static com.netflix.hystrix.HystrixCommand.Setter.withGroupKey; import static com.netflix.hystrix.HystrixCommandGroupKey.Factory.asKey; -import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; public class TraceCommandTests { static final long EXPECTED_TRACE_ID = 1L; Tracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - Mockito.mock(ApplicationEventPublisher.class)); + Mockito.mock(ApplicationEventPublisher.class), new DefaultSpanNamer()); @Before public void setup() { @@ -49,6 +52,16 @@ public class TraceCommandTests { then(secondSpanFromHystrix.getSavedSpan()) .as("saved span as remnant of first span").isNull(); } + @Test + public void should_create_a_local_span_with_proper_tags_when_hystrix_command_gets_executed() + throws Exception { + Span spanFromHystrix = whenCommandIsExecuted(traceReturningCommand()); + + then(spanFromHystrix) + .isALocalComponentSpan() + .hasNameEqualTo("traceCommandKey") + .hasATag("commandKey", "traceCommandKey"); + } @Test public void should_run_Hystrix_command_with_span_passed_from_parent_thread() { @@ -67,12 +80,13 @@ public class TraceCommandTests { } private TraceCommand traceReturningCommand() { - return new TraceCommand(this.tracer, + return new TraceCommand(this.tracer, new TraceKeys(), withGroupKey(asKey("group")) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties .Setter().withCoreSize(1).withMaxQueueSize(1)) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() - .withExecutionTimeoutEnabled(false))) { + .withExecutionTimeoutEnabled(false)) + .andCommandKey(HystrixCommandKey.Factory.asKey("traceCommandKey"))) { @Override public Span doRun() throws Exception { return TestSpanContextHolder.getCurrentSpan(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java index 42d76b7c4..d09f4b6aa 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceAsyncIntegrationTests.java @@ -3,6 +3,7 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.concurrent.atomic.AtomicReference; +import com.jayway.awaitility.Awaitility; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; @@ -20,8 +21,6 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.jayway.awaitility.Awaitility; - import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; @RunWith(SpringJUnit4ClassRunner.class) @@ -53,9 +52,12 @@ public class TraceAsyncIntegrationTests { Awaitility.await().until(new Runnable() { @Override public void run() { - then(span) - .hasTraceIdEqualTo(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getTraceId()) - .hasNameNotEqualTo(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpanName()); + then(TraceAsyncIntegrationTests.this.classPerformingAsyncLogic.getSpan()) + .hasTraceIdEqualTo(span.getTraceId()) + .hasNameEqualTo("invokeAsynchronousLogic") + .isALocalComponentSpan() + .hasATag("class", "ClassPerformingAsyncLogic") + .hasATag("method", "invokeAsynchronousLogic"); } }); } @@ -91,18 +93,8 @@ public class TraceAsyncIntegrationTests { this.span.set(TestSpanContextHolder.getCurrentSpan()); } - public Long getTraceId() { - if (this.span.get() == null) { - return null; - } - return this.span.get().getTraceId(); - } - - public String getSpanName() { - if (this.span.get() != null && this.span.get().getName() == null) { - return null; - } - return this.span.get().getName(); + public Span getSpan() { + return this.span.get(); } } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java index 8566648c0..6fc722c61 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java @@ -20,6 +20,7 @@ import java.util.Random; import org.junit.Before; import org.junit.Test; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.TraceKeys; @@ -46,7 +47,7 @@ public class TraceFilterMockChainIntegrationTests { private StaticApplicationContext context = new StaticApplicationContext(); private Tracer tracer = new DefaultTracer(new AlwaysSampler(), - new Random(), this.context); + new Random(), this.context, new DefaultSpanNamer()); private TraceKeys traceKeys = new TraceKeys(); private MockHttpServletRequest request; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java index 204f22e60..dfe8c688d 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java @@ -21,6 +21,7 @@ import java.util.Random; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; @@ -68,7 +69,7 @@ public class TraceFilterTests { public void init() { initMocks(this); this.tracer = new DefaultTracer(new DelegateSampler(), new Random(), - this.publisher) { + this.publisher, new DefaultSpanNamer()) { @Override protected Span createSpan(Span span, Span saved) { TraceFilterTests.this.span = super.createSpan(span, saved); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java index 839ebb272..f8c4b5c5f 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java @@ -16,9 +16,6 @@ package org.springframework.cloud.sleuth.instrument.web.client; -import static org.assertj.core.api.BDDAssertions.then; -import static org.junit.Assert.assertFalse; - import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -27,6 +24,7 @@ import java.util.Random; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; @@ -42,6 +40,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; +import static org.assertj.core.api.BDDAssertions.then; +import static org.junit.Assert.assertFalse; + /** * @author Dave Syer * @@ -61,7 +62,8 @@ public class TraceRestTemplateInterceptorTests { @Before public void setup() { this.publisher.refresh(); - this.traces = new DefaultTracer(new AlwaysSampler(), new Random(), this.publisher); + this.traces = new DefaultTracer(new AlwaysSampler(), new Random(), this.publisher, + new DefaultSpanNamer()); this.template.setInterceptors(Arrays.asList( new TraceRestTemplateInterceptor(this.traces))); TestSpanContextHolder.removeCurrentSpan(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilterTests.java index 4cbf201a3..ca94ba076 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostZuulFilterTests.java @@ -23,6 +23,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.event.ClientReceivedEvent; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; @@ -40,7 +41,9 @@ public class TracePostZuulFilterTests { private ApplicationEventPublisher publisher = Mockito.mock(ApplicationEventPublisher.class); - private DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), Mockito.mock(ApplicationEventPublisher.class)); + private DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), + new Random(), Mockito.mock(ApplicationEventPublisher.class), + new DefaultSpanNamer()); private TracePostZuulFilter filter = new TracePostZuulFilter(this.tracer); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java index 2152b5d76..b1a2899d3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java @@ -23,6 +23,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.sampler.NeverSampler; @@ -45,7 +46,7 @@ public class TracePreZuulFilterTests { .mock(ApplicationEventPublisher.class); private DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - this.publisher); + this.publisher, new DefaultSpanNamer()); private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java index 553d0b947..d7b82e267 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java @@ -24,7 +24,9 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.event.SpanAcquiredEvent; import org.springframework.cloud.sleuth.event.SpanReleasedEvent; @@ -54,6 +56,7 @@ public class DefaultTracerTests { public static final String IMPORTANT_WORK_2 = "http:important work 2"; public static final int NUM_SPANS = 3; private ApplicationEventPublisher publisher; + private SpanNamer spanNamer = new DefaultSpanNamer(); @Before public void setup() { @@ -70,7 +73,7 @@ public class DefaultTracerTests { public void tracingWorks() { DefaultTracer tracer = new DefaultTracer(NeverSampler.INSTANCE, new Random(), - this.publisher); + this.publisher, new DefaultSpanNamer()); Span span = tracer.startTrace(CREATE_SIMPLE_TRACE, new AlwaysSampler()); try { @@ -109,7 +112,7 @@ public class DefaultTracerTests { @Test public void nonExportable() { DefaultTracer tracer = new DefaultTracer(NeverSampler.INSTANCE, new Random(), - this.publisher); + this.publisher, this.spanNamer); Span span = tracer.startTrace(CREATE_SIMPLE_TRACE); assertThat(span.isExportable(), is(false)); } @@ -117,7 +120,7 @@ public class DefaultTracerTests { @Test public void exportable() { DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - this.publisher); + this.publisher, this.spanNamer); Span span = tracer.startTrace(CREATE_SIMPLE_TRACE); assertThat(span.isExportable(), is(true)); } @@ -125,7 +128,7 @@ public class DefaultTracerTests { @Test public void exportableInheritedFromParent() { DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - this.publisher); + this.publisher, this.spanNamer); Span span = tracer.startTrace(CREATE_SIMPLE_TRACE, NeverSampler.INSTANCE); assertThat(span.isExportable(), is(false)); Span child = tracer.joinTrace(CREATE_SIMPLE_TRACE_SPAN_NAME + "/child", span); @@ -135,7 +138,7 @@ public class DefaultTracerTests { @Test public void parentNotRemovedIfActiveOnJoin() { DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - this.publisher); + this.publisher, this.spanNamer); Span parent = tracer.startTrace(CREATE_SIMPLE_TRACE); Span span = tracer.joinTrace(IMPORTANT_WORK_1, parent); tracer.close(span); @@ -145,7 +148,7 @@ public class DefaultTracerTests { @Test public void parentRemovedIfNotActiveOnJoin() { DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - this.publisher); + this.publisher, this.spanNamer); Span parent = Span.builder().name(CREATE_SIMPLE_TRACE).traceId(1L).spanId(1L) .build(); Span span = tracer.joinTrace(IMPORTANT_WORK_1, parent); @@ -156,7 +159,7 @@ public class DefaultTracerTests { @Test public void grandParentRestoredAfterAutoClose() { DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), - this.publisher); + this.publisher, this.spanNamer); Span grandParent = tracer.startTrace(CREATE_SIMPLE_TRACE); Span parent = Span.builder().name(IMPORTANT_WORK_1).traceId(1L).spanId(1L) .build();