diff --git a/.editorconfig b/.editorconfig
index ddda9782f..ffe385c6d 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,5 +1,9 @@
root = true
+[*]
+end_of_line = crlf
+insert_final_newline = true
+
[*.java]
indent_style = tab
indent_size = 4
diff --git a/pom.xml b/pom.xml
index 2fb7cb011..286d8b4e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,7 @@
3.1.3-SNAPSHOT
3.0.3-SNAPSHOT
3.0.3-SNAPSHOT
+ 2.3.2-SNAPSHOT
5.13.2
0.32.0
2.3.4.RELEASE
@@ -228,6 +229,13 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-task-dependencies
+ ${spring-cloud-task.version}
+ pom
+ import
+
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/SpanAndScope.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/SpanAndScope.java
new file mode 100644
index 000000000..e18e028d7
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/SpanAndScope.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth;
+
+/**
+ * Container object for {@link Span} and its corresponding {@link Tracer.SpanInScope}.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class SpanAndScope {
+
+ private final Span span;
+
+ private final Tracer.SpanInScope scope;
+
+ public SpanAndScope(Span span, Tracer.SpanInScope scope) {
+ this.span = span;
+ this.scope = scope;
+ }
+
+ public Span getSpan() {
+ return this.span;
+ }
+
+ public Tracer.SpanInScope getScope() {
+ return this.scope;
+ }
+
+}
diff --git a/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/ThreadLocalSpan.java b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/ThreadLocalSpan.java
new file mode 100644
index 000000000..aa2c0226b
--- /dev/null
+++ b/spring-cloud-sleuth-api/src/main/java/org/springframework/cloud/sleuth/ThreadLocalSpan.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth;
+
+import java.util.Deque;
+import java.util.NoSuchElementException;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Represents a {@link Span} stored in thread local.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class ThreadLocalSpan {
+
+ private static final Log log = LogFactory.getLog(ThreadLocalSpan.class);
+
+ private final ThreadLocal threadLocalSpan = new ThreadLocal<>();
+
+ private final Deque spans = new LinkedBlockingDeque<>();
+
+ private final Tracer tracer;
+
+ public ThreadLocalSpan(Tracer tracer) {
+ this.tracer = tracer;
+ }
+
+ /**
+ * Sets given span and scope.
+ * @param span - span to be put in scope
+ */
+ public void set(Span span) {
+ Tracer.SpanInScope spanInScope = this.tracer.withSpan(span);
+ SpanAndScope newSpanAndScope = new SpanAndScope(span, spanInScope);
+ SpanAndScope scope = this.threadLocalSpan.get();
+ if (scope != null) {
+ this.spans.addFirst(scope);
+ }
+ this.threadLocalSpan.set(newSpanAndScope);
+ }
+
+ /**
+ * @return currently stored span and scope
+ */
+ public SpanAndScope get() {
+ return this.threadLocalSpan.get();
+ }
+
+ /**
+ * Removes the current span from thread local and brings back the previous span to the
+ * current thread local.
+ */
+ public void remove() {
+ this.threadLocalSpan.remove();
+ if (this.spans.isEmpty()) {
+ return;
+ }
+ try {
+ SpanAndScope span = this.spans.removeFirst();
+ if (log.isDebugEnabled()) {
+ log.debug("Took span [" + span + "] from thread local");
+ }
+ this.threadLocalSpan.set(span);
+ }
+ catch (NoSuchElementException ex) {
+ if (log.isTraceEnabled()) {
+ log.trace("Failed to remove a span from the queue", ex);
+ }
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/pom.xml b/spring-cloud-sleuth-autoconfigure/pom.xml
index 15f4a93a0..769757ac6 100644
--- a/spring-cloud-sleuth-autoconfigure/pom.xml
+++ b/spring-cloud-sleuth-autoconfigure/pom.xml
@@ -103,6 +103,11 @@
spring-cloud-context
true
+
+ org.springframework.cloud
+ spring-cloud-starter-task
+ true
+
io.reactivex
rxjava
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceApplicationRunnerBeanPostProcessor.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceApplicationRunnerBeanPostProcessor.java
new file mode 100644
index 000000000..5efd90f4c
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceApplicationRunnerBeanPostProcessor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.task;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.cloud.sleuth.instrument.task.TraceApplicationRunner;
+
+/**
+ * Registers beans related to task scheduling.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceApplicationRunnerBeanPostProcessor implements BeanPostProcessor {
+
+ private final BeanFactory beanFactory;
+
+ public TraceApplicationRunnerBeanPostProcessor(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof ApplicationRunner && !(bean instanceof TraceApplicationRunner)) {
+ return new TraceApplicationRunner(this.beanFactory, (ApplicationRunner) bean, beanName);
+ }
+ return bean;
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceCommandLineRunnerBeanPostProcessor.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceCommandLineRunnerBeanPostProcessor.java
new file mode 100644
index 000000000..9fc69d7da
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceCommandLineRunnerBeanPostProcessor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.task;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.cloud.sleuth.instrument.task.TraceCommandLineRunner;
+
+/**
+ * Registers beans related to task scheduling.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceCommandLineRunnerBeanPostProcessor implements BeanPostProcessor {
+
+ private final BeanFactory beanFactory;
+
+ public TraceCommandLineRunnerBeanPostProcessor(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof CommandLineRunner && !(bean instanceof TraceCommandLineRunner)) {
+ return new TraceCommandLineRunner(this.beanFactory, (CommandLineRunner) bean, beanName);
+ }
+ return bean;
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceTaskAutoConfiguration.java b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceTaskAutoConfiguration.java
new file mode 100644
index 000000000..591ca5941
--- /dev/null
+++ b/spring-cloud-sleuth-autoconfigure/src/main/java/org/springframework/cloud/sleuth/autoconfig/instrument/task/TraceTaskAutoConfiguration.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.autoconfig.instrument.task;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+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.autoconfig.brave.BraveAutoConfiguration;
+import org.springframework.cloud.sleuth.instrument.task.TraceTaskExecutionListener;
+import org.springframework.cloud.task.listener.TaskExecutionListener;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Registers beans related to Spring Cloud Task scheduling.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnClass(TaskExecutionListener.class)
+@ConditionalOnProperty(value = "spring.sleuth.task.enabled", matchIfMissing = true)
+@ConditionalOnBean(Tracer.class)
+@AutoConfigureAfter(BraveAutoConfiguration.class)
+public class TraceTaskAutoConfiguration {
+
+ @Bean
+ TraceTaskExecutionListener traceTaskExecutionListener(Tracer tracer,
+ @Value("${spring.application.name:default}") String appName) {
+ return new TraceTaskExecutionListener(tracer, appName);
+ }
+
+ @Bean
+ static TraceCommandLineRunnerBeanPostProcessor traceCommandLineRunnerBeanPostProcessor(BeanFactory beanFactory) {
+ return new TraceCommandLineRunnerBeanPostProcessor(beanFactory);
+ }
+
+ @Bean
+ static TraceApplicationRunnerBeanPostProcessor traceApplicationRunnerBeanPostProcessor(BeanFactory beanFactory) {
+ return new TraceApplicationRunnerBeanPostProcessor(beanFactory);
+ }
+
+}
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index d655251ff..e46fecde3 100644
--- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -128,7 +128,13 @@
{
"name": "spring.sleuth.integration.enabled",
"type": "java.lang.Boolean",
- "description": "Enable Spring Integration sleuth instrumentation.",
+ "description": "Enable Spring Integration instrumentation.",
+ "defaultValue": true
+ },
+ {
+ "name": "spring.sleuth.task.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable Spring Cloud Task instrumentation.",
"defaultValue": true
}
]
diff --git a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories
index 1c5ba3eba..5e902cd8a 100644
--- a/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-cloud-sleuth-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -6,6 +6,7 @@ org.springframework.cloud.sleuth.autoconfig.instrument.async.TraceAsyncDefaultAu
org.springframework.cloud.sleuth.autoconfig.instrument.circuitbreaker.TraceCircuitBreakerAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.rxjava.TraceRxJavaAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.quartz.TraceQuartzAutoConfiguration,\
+org.springframework.cloud.sleuth.autoconfig.instrument.task.TraceTaskAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.TraceWebAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.TraceWebClientAutoConfiguration,\
org.springframework.cloud.sleuth.autoconfig.instrument.web.client.feign.TraceFeignClientAutoConfiguration,\
diff --git a/spring-cloud-sleuth-instrumentation/pom.xml b/spring-cloud-sleuth-instrumentation/pom.xml
index f049e8a35..6aff38d57 100644
--- a/spring-cloud-sleuth-instrumentation/pom.xml
+++ b/spring-cloud-sleuth-instrumentation/pom.xml
@@ -127,6 +127,11 @@
spring-cloud-starter-gateway
true
+
+ org.springframework.cloud
+ spring-cloud-starter-task
+ true
+
org.aspectj
aspectjrt
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java
index 9640fdb2a..ed9d3780d 100644
--- a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TracingChannelInterceptor.java
@@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.messaging;
import java.util.Iterator;
import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.concurrent.LinkedBlockingDeque;
import java.util.function.Function;
import org.apache.commons.logging.Log;
@@ -28,6 +26,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.SpanAndScope;
+import org.springframework.cloud.sleuth.ThreadLocalSpan;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.propagation.Propagator;
import org.springframework.cloud.stream.binder.BinderType;
@@ -107,7 +107,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
private final Propagator propagator;
- private final ThreadLocalSpan threadLocalSpan = new ThreadLocalSpan();
+ private final ThreadLocalSpan threadLocalSpan;
private final Function remoteServiceNameMapper;
@@ -115,6 +115,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
Propagator.Setter setter, Propagator.Getter getter,
Function remoteServiceNameMapper, MessageSpanCustomizer messageSpanCustomizer) {
this.tracer = tracer;
+ this.threadLocalSpan = new ThreadLocalSpan(tracer);
this.propagator = propagator;
this.injector = setter;
this.extractor = getter;
@@ -163,8 +164,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
}
private void setSpanInScope(Span span) {
- Tracer.SpanInScope spanInScope = this.tracer.withSpan(span);
- this.threadLocalSpan.set(new SpanAndScope(span, spanInScope));
+ this.threadLocalSpan.set(span);
if (log.isDebugEnabled()) {
log.debug("Put span in scope " + span);
}
@@ -362,8 +362,8 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
if (spanAndScope == null) {
return;
}
- Span span = spanAndScope.span;
- Tracer.SpanInScope scope = spanAndScope.scope;
+ Span span = spanAndScope.getSpan();
+ Tracer.SpanInScope scope = spanAndScope.getScope();
if (span.isNoop()) {
if (log.isDebugEnabled()) {
log.debug("Span " + span + " is noop - will stope the scope");
@@ -424,57 +424,3 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
}
}
-
-class SpanAndScope {
-
- final Span span;
-
- final Tracer.SpanInScope scope;
-
- SpanAndScope(Span span, Tracer.SpanInScope scope) {
- this.span = span;
- this.scope = scope;
- }
-
-}
-
-class ThreadLocalSpan {
-
- private static final Log log = LogFactory.getLog(ThreadLocalSpan.class);
-
- final ThreadLocal threadLocalSpan = new ThreadLocal<>();
-
- final LinkedBlockingDeque spans = new LinkedBlockingDeque<>();
-
- void set(SpanAndScope spanAndScope) {
- SpanAndScope scope = this.threadLocalSpan.get();
- if (scope != null) {
- this.spans.addFirst(scope);
- }
- this.threadLocalSpan.set(spanAndScope);
- }
-
- SpanAndScope get() {
- return this.threadLocalSpan.get();
- }
-
- void remove() {
- this.threadLocalSpan.remove();
- if (this.spans.isEmpty()) {
- return;
- }
- try {
- SpanAndScope span = this.spans.removeFirst();
- if (log.isDebugEnabled()) {
- log.debug("Took span [" + span + "] from thread local");
- }
- this.threadLocalSpan.set(span);
- }
- catch (NoSuchElementException ex) {
- if (log.isTraceEnabled()) {
- log.trace("Failed to remove a span from the queue", ex);
- }
- }
- }
-
-}
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceApplicationRunner.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceApplicationRunner.java
new file mode 100644
index 000000000..4e3f6c77d
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceApplicationRunner.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.instrument.task;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Tracer;
+
+/**
+ * Trace representation of a {@link ApplicationRunner}.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceApplicationRunner implements ApplicationRunner {
+
+ private final BeanFactory beanFactory;
+
+ private final ApplicationRunner delegate;
+
+ private final String beanName;
+
+ private Tracer tracer;
+
+ public TraceApplicationRunner(BeanFactory beanFactory, ApplicationRunner delegate, String beanName) {
+ this.beanFactory = beanFactory;
+ this.delegate = delegate;
+ this.beanName = beanName;
+ }
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ Span span = tracer().nextSpan().name(this.beanName);
+ try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
+ this.delegate.run(args);
+ }
+ finally {
+ span.end();
+ }
+ }
+
+ private Tracer tracer() {
+ if (this.tracer == null) {
+ this.tracer = this.beanFactory.getBean(Tracer.class);
+ }
+ return this.tracer;
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceCommandLineRunner.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceCommandLineRunner.java
new file mode 100644
index 000000000..70b9ee320
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceCommandLineRunner.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.instrument.task;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Tracer;
+
+/**
+ * Trace representation of a {@link CommandLineRunner}.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceCommandLineRunner implements CommandLineRunner {
+
+ private final BeanFactory beanFactory;
+
+ private final CommandLineRunner delegate;
+
+ private final String beanName;
+
+ private Tracer tracer;
+
+ public TraceCommandLineRunner(BeanFactory beanFactory, CommandLineRunner delegate, String beanName) {
+ this.beanFactory = beanFactory;
+ this.delegate = delegate;
+ this.beanName = beanName;
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ Span span = tracer().nextSpan().name(this.beanName);
+ try (Tracer.SpanInScope spanInScope = tracer().withSpan(span.start())) {
+ this.delegate.run(args);
+ }
+ finally {
+ span.end();
+ }
+ }
+
+ private Tracer tracer() {
+ if (this.tracer == null) {
+ this.tracer = this.beanFactory.getBean(Tracer.class);
+ }
+ return this.tracer;
+ }
+
+}
diff --git a/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceTaskExecutionListener.java b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceTaskExecutionListener.java
new file mode 100644
index 000000000..d50d7ab00
--- /dev/null
+++ b/spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/task/TraceTaskExecutionListener.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2018-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.instrument.task;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.SpanAndScope;
+import org.springframework.cloud.sleuth.ThreadLocalSpan;
+import org.springframework.cloud.sleuth.Tracer;
+import org.springframework.cloud.task.listener.TaskExecutionListener;
+import org.springframework.cloud.task.repository.TaskExecution;
+import org.springframework.core.Ordered;
+
+/**
+ * Sets the span upon starting and closes it upon ending a task.
+ *
+ * @author Marcin Grzejszczak
+ * @since 3.1.0
+ */
+public class TraceTaskExecutionListener implements TaskExecutionListener, Ordered {
+
+ private static final Log log = LogFactory.getLog(TraceTaskExecutionListener.class);
+
+ private final Tracer tracer;
+
+ private final ThreadLocalSpan threadLocalSpan;
+
+ private final String projectName;
+
+ public TraceTaskExecutionListener(Tracer tracer, String projectName) {
+ this.tracer = tracer;
+ this.threadLocalSpan = new ThreadLocalSpan(tracer);
+ this.projectName = projectName;
+ }
+
+ @Override
+ public void onTaskStartup(TaskExecution taskExecution) {
+ Span span = this.tracer.nextSpan().name(this.projectName).start();
+ this.threadLocalSpan.set(span);
+ if (log.isDebugEnabled()) {
+ log.debug("Put the span [" + span + "] to thread local");
+ }
+ }
+
+ @Override
+ public void onTaskEnd(TaskExecution taskExecution) {
+ SpanAndScope spanAndScope = this.threadLocalSpan.get();
+ Span span = spanAndScope.getSpan();
+ span.end();
+ spanAndScope.getScope().close();
+ if (log.isDebugEnabled()) {
+ log.debug("Removed the [" + span + "] from thread local");
+ }
+ }
+
+ @Override
+ public void onTaskFailed(TaskExecution taskExecution, Throwable throwable) {
+ SpanAndScope spanAndScope = this.threadLocalSpan.get();
+ Span span = spanAndScope.getSpan();
+ span.error(throwable);
+ span.end();
+ spanAndScope.getScope().close();
+ if (log.isDebugEnabled()) {
+ log.debug("Removed the [" + span + "] from thread local and added error");
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
+}
diff --git a/tests/brave/pom.xml b/tests/brave/pom.xml
index 09478bd12..8796d03bf 100644
--- a/tests/brave/pom.xml
+++ b/tests/brave/pom.xml
@@ -50,6 +50,7 @@
spring-cloud-sleuth-instrumentation-reactor-tests
spring-cloud-sleuth-instrumentation-rxjava-tests
spring-cloud-sleuth-instrumentation-scheduling-tests
+ spring-cloud-sleuth-instrumentation-task-tests
spring-cloud-sleuth-instrumentation-webflux-tests
spring-cloud-sleuth-zipkin-tests
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml
index da9a7a566..45d3de776 100644
--- a/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-scheduling-tests/pom.xml
@@ -51,10 +51,6 @@
-
- org.springframework.cloud
- spring-cloud-starter-sleuth
-
org.springframework.cloud
spring-cloud-starter-sleuth
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/pom.xml b/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/pom.xml
new file mode 100644
index 000000000..667d54797
--- /dev/null
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/pom.xml
@@ -0,0 +1,81 @@
+
+
+
+
+ 4.0.0
+
+ spring-cloud-sleuth-instrumentation-task-tests
+ jar
+ Spring Cloud Sleuth Brave Task Instrumentation Tests
+ Spring Cloud Sleuth Brave Task Instrumentation Tests
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-tests-brave
+ 3.1.0-SNAPSHOT
+ ..
+
+
+
+ true
+
+
+
+
+
+
+ maven-deploy-plugin
+
+ true
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-tests-common
+ ${project.version}
+
+
+ org.springframework.cloud
+ spring-cloud-starter-sleuth
+
+
+ org.springframework.cloud
+ spring-cloud-starter-task
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ io.zipkin.brave
+ brave-tests
+
+
+ org.awaitility
+ awaitility
+
+
+
+
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/task/SpringCloudTaskIntegrationTests.java b/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/task/SpringCloudTaskIntegrationTests.java
new file mode 100644
index 000000000..55d08c60a
--- /dev/null
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/src/test/java/org/springframework/cloud/sleuth/brave/instrument/task/SpringCloudTaskIntegrationTests.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.brave.instrument.task;
+
+import brave.sampler.Sampler;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.sleuth.brave.BraveTestSpanHandler;
+import org.springframework.cloud.sleuth.test.TestSpanHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+
+@SpringBootTest
+@ContextConfiguration(classes = SpringCloudTaskIntegrationTests.Config.class)
+public class SpringCloudTaskIntegrationTests
+ extends org.springframework.cloud.sleuth.instrument.task.SpringCloudTaskIntegrationTests {
+
+ @Configuration(proxyBeanMethods = false)
+ static class Config {
+
+ @Bean
+ TestSpanHandler testSpanHandlerSupplier(brave.test.TestSpanHandler testSpanHandler) {
+ return new BraveTestSpanHandler(testSpanHandler);
+ }
+
+ @Bean
+ Sampler alwaysSampler() {
+ return Sampler.ALWAYS_SAMPLE;
+ }
+
+ @Bean
+ brave.test.TestSpanHandler braveTestSpanHandler() {
+ return new brave.test.TestSpanHandler();
+ }
+
+ }
+
+}
diff --git a/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/src/test/resources/application.yml b/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/src/test/resources/application.yml
new file mode 100644
index 000000000..2b87cbf49
--- /dev/null
+++ b/tests/brave/spring-cloud-sleuth-instrumentation-task-tests/src/test/resources/application.yml
@@ -0,0 +1 @@
+logging.level.org.springframework.cloud: DEBUG
diff --git a/tests/common/pom.xml b/tests/common/pom.xml
index 628288865..01c5cdc48 100644
--- a/tests/common/pom.xml
+++ b/tests/common/pom.xml
@@ -84,6 +84,11 @@
spring-cloud-starter-gateway
true
+
+ org.springframework.cloud
+ spring-cloud-starter-task
+ true
+
org.springframework.cloud
spring-cloud-starter-circuitbreaker-resilience4j
diff --git a/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/task/SpringCloudTaskIntegrationTests.java b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/task/SpringCloudTaskIntegrationTests.java
new file mode 100644
index 000000000..e1c5f83f3
--- /dev/null
+++ b/tests/common/src/main/java/org/springframework/cloud/sleuth/instrument/task/SpringCloudTaskIntegrationTests.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.instrument.task;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.cloud.sleuth.exporter.FinishedSpan;
+import org.springframework.cloud.sleuth.test.TestSpanHandler;
+import org.springframework.cloud.task.configuration.EnableTask;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+
+import static org.assertj.core.api.BDDAssertions.then;
+
+@ContextConfiguration(classes = SpringCloudTaskIntegrationTests.TestConfig.class)
+@TestPropertySource(properties = "spring.application.name=MyApplication")
+public abstract class SpringCloudTaskIntegrationTests {
+
+ @Autowired
+ TestSpanHandler spans;
+
+ @Test
+ public void should_pass_tracing_information_when_using_spring_cloud_task() {
+ Set traceIds = this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId)
+ .collect(Collectors.toSet());
+ then(traceIds).as("There's one traceid").hasSize(1);
+ Set spanIds = this.spans.reportedSpans().stream().map(FinishedSpan::getSpanId)
+ .collect(Collectors.toSet());
+
+ then(spanIds).as("There are 3 spans").hasSize(3);
+ Iterator spanIterator = this.spans.reportedSpans().iterator();
+
+ FinishedSpan first = spanIterator.next();
+ FinishedSpan second = spanIterator.next();
+ FinishedSpan third = spanIterator.next();
+ then(first.getName()).isEqualTo("myApplicationRunner");
+ then(second.getName()).isEqualTo("myCommandLineRunner");
+ then(third.getName()).isEqualTo("MyApplication");
+ }
+
+ @Configuration(proxyBeanMethods = false)
+ @EnableAutoConfiguration
+ @EnableTask
+ public static class TestConfig {
+
+ @Bean
+ MyCommandLineRunner myCommandLineRunner() {
+ return new MyCommandLineRunner();
+ }
+
+ @Bean
+ MyApplicationRunner myApplicationRunner() {
+ return new MyApplicationRunner();
+ }
+
+ }
+
+ static class MyCommandLineRunner implements CommandLineRunner {
+
+ private static final Log log = LogFactory.getLog(MyCommandLineRunner.class);
+
+ @Override
+ public void run(String... args) throws Exception {
+ log.info("Ran MyCommandLineRunner");
+ }
+
+ }
+
+ static class MyApplicationRunner implements ApplicationRunner {
+
+ private static final Log log = LogFactory.getLog(MyApplicationRunner.class);
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ log.info("Ran MyApplicationRunner");
+ }
+
+ }
+
+}