Added spring-cloud-task support

fixes gh-1903
This commit is contained in:
Marcin Grzejszczak
2021-04-21 10:11:28 +02:00
parent 6e8f86ed35
commit 2fca60a112
22 changed files with 792 additions and 66 deletions

View File

@@ -1,5 +1,9 @@
root = true
[*]
end_of_line = crlf
insert_final_newline = true
[*.java]
indent_style = tab
indent_size = 4

View File

@@ -70,6 +70,7 @@
<spring-cloud-function.version>3.1.3-SNAPSHOT</spring-cloud-function.version>
<spring-cloud-netflix.version>3.0.3-SNAPSHOT</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>3.0.3-SNAPSHOT</spring-cloud-openfeign.version>
<spring-cloud-task.version>2.3.2-SNAPSHOT</spring-cloud-task.version>
<brave.version>5.13.2</brave.version>
<opentracing.version>0.32.0</opentracing.version>
<spring-security-boot-autoconfigure.version>2.3.4.RELEASE</spring-security-boot-autoconfigure.version>
@@ -228,6 +229,13 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-dependencies</artifactId>
<version>${spring-cloud-task.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>

View File

@@ -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;
}
}

View File

@@ -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<SpanAndScope> threadLocalSpan = new ThreadLocal<>();
private final Deque<SpanAndScope> 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);
}
}
}
}

View File

@@ -103,6 +103,11 @@
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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
}
]

View File

@@ -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,\

View File

@@ -127,6 +127,11 @@
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>

View File

@@ -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<String, String> remoteServiceNameMapper;
@@ -115,6 +115,7 @@ public final class TracingChannelInterceptor extends ChannelInterceptorAdapter
Propagator.Setter<MessageHeaderAccessor> setter, Propagator.Getter<MessageHeaderAccessor> getter,
Function<String, String> 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<SpanAndScope> threadLocalSpan = new ThreadLocal<>();
final LinkedBlockingDeque<SpanAndScope> 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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -50,6 +50,7 @@
<module>spring-cloud-sleuth-instrumentation-reactor-tests</module>
<module>spring-cloud-sleuth-instrumentation-rxjava-tests</module>
<module>spring-cloud-sleuth-instrumentation-scheduling-tests</module>
<module>spring-cloud-sleuth-instrumentation-task-tests</module>
<module>spring-cloud-sleuth-instrumentation-webflux-tests</module>
<module>spring-cloud-sleuth-zipkin-tests</module>
</modules>

View File

@@ -51,10 +51,6 @@
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
~
~
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-sleuth-instrumentation-task-tests</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud Sleuth Brave Task Instrumentation Tests</name>
<description>Spring Cloud Sleuth Brave Task Instrumentation Tests</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-tests-brave</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<properties>
<sonar.skip>true</sonar.skip>
</properties>
<build>
<plugins>
<plugin>
<!--skip deploy -->
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-tests-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-tests</artifactId>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1 @@
logging.level.org.springframework.cloud: DEBUG

View File

@@ -84,6 +84,11 @@
<artifactId>spring-cloud-starter-gateway</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>

View File

@@ -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<String> traceIds = this.spans.reportedSpans().stream().map(FinishedSpan::getTraceId)
.collect(Collectors.toSet());
then(traceIds).as("There's one traceid").hasSize(1);
Set<String> spanIds = this.spans.reportedSpans().stream().map(FinishedSpan::getSpanId)
.collect(Collectors.toSet());
then(spanIds).as("There are 3 spans").hasSize(3);
Iterator<FinishedSpan> 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");
}
}
}