Skip to content

Commit 97ed58b

Browse files
authored
Spring scheduling: ensure spans have no parent (#7583)
* Spring scheduling: ensure spans have no parent * simplify * add more tests
1 parent d1d0aa8 commit 97ed58b

File tree

3 files changed

+162
-13
lines changed

3 files changed

+162
-13
lines changed

dd-java-agent/instrumentation/spring-scheduling-3.1/src/main/java/datadog/trace/instrumentation/springscheduling/SpringSchedulingRunnableWrapper.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
import static datadog.trace.instrumentation.springscheduling.SpringSchedulingDecorator.DECORATE;
88
import static datadog.trace.instrumentation.springscheduling.SpringSchedulingDecorator.SCHEDULED_CALL;
99

10+
import datadog.trace.api.Config;
1011
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
1112
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
1213

1314
public class SpringSchedulingRunnableWrapper implements Runnable {
15+
private static final boolean LEGACY_TRACING =
16+
Config.get().isLegacyTracingEnabled(false, "spring-scheduling");
1417
private final Runnable runnable;
1518

1619
private SpringSchedulingRunnableWrapper(final Runnable runnable) {
@@ -19,7 +22,8 @@ private SpringSchedulingRunnableWrapper(final Runnable runnable) {
1922

2023
@Override
2124
public void run() {
22-
final AgentSpan span = startSpan(SCHEDULED_CALL);
25+
final AgentSpan span =
26+
LEGACY_TRACING ? startSpan(SCHEDULED_CALL) : startSpan(SCHEDULED_CALL, null);
2327
DECORATE.afterStart(span);
2428

2529
try (final AgentScope scope = activateSpan(span)) {

dd-java-agent/instrumentation/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import static datadog.trace.agent.test.utils.TraceUtils.basicSpan
2+
13
import datadog.trace.agent.test.AgentTestRunner
24
import datadog.trace.bootstrap.instrumentation.api.Tags
35
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint
@@ -7,21 +9,42 @@ import java.util.concurrent.TimeUnit
79

810
class SpringSchedulingTest extends AgentTestRunner {
911

12+
def legacyTracing() {
13+
false
14+
}
15+
16+
@Override
17+
protected void configurePreAgent() {
18+
super.configurePreAgent()
19+
if (legacyTracing()) {
20+
injectSysConfig("spring-scheduling.legacy.tracing.enabled", "true")
21+
}
22+
}
23+
1024
def "schedule trigger test according to cron expression"() {
1125
setup:
12-
def context = new AnnotationConfigApplicationContext(TriggerTaskConfig)
26+
def context = new AnnotationConfigApplicationContext(TriggerTaskConfig, SchedulingConfig)
1327
def task = context.getBean(TriggerTask)
1428

1529
task.blockUntilExecute()
1630

1731
expect:
1832
assert task != null
19-
assertTraces(1) {
20-
trace(1) {
33+
def hasParent = legacyTracing()
34+
assertTraces(hasParent ? 1 : 2) {
35+
if (!hasParent) {
36+
trace(1) {
37+
basicSpan(it, "parent")
38+
}
39+
}
40+
trace(hasParent ? 2 : 1) {
41+
if (hasParent) {
42+
basicSpan(it, "parent")
43+
}
2144
span {
2245
resourceName "TriggerTask.run"
2346
operationName "scheduled.call"
24-
parent()
47+
hasParent ? childOfPrevious() : parent()
2548
errored false
2649
tags {
2750
"$Tags.COMPONENT" "spring-scheduling"
@@ -42,19 +65,29 @@ class SpringSchedulingTest extends AgentTestRunner {
4265

4366
def "schedule interval test"() {
4467
setup:
45-
def context = new AnnotationConfigApplicationContext(IntervalTaskConfig)
68+
def context = new AnnotationConfigApplicationContext(IntervalTaskConfig, SchedulingConfig)
4669
def task = context.getBean(IntervalTask)
4770

4871
task.blockUntilExecute()
4972

5073
expect:
5174
assert task != null
52-
assertTraces(1) {
53-
trace(1) {
75+
def hasParent = legacyTracing()
76+
77+
assertTraces(hasParent ? 1 : 2) {
78+
if (!hasParent) {
79+
trace(1) {
80+
basicSpan(it, "parent")
81+
}
82+
}
83+
trace(hasParent ? 2 : 1) {
84+
if (hasParent) {
85+
basicSpan(it, "parent")
86+
}
5487
span {
5588
resourceName "IntervalTask.run"
5689
operationName "scheduled.call"
57-
parent()
90+
hasParent ? childOfPrevious() : parent()
5891
errored false
5992
tags {
6093
"$Tags.COMPONENT" "spring-scheduling"
@@ -69,18 +102,27 @@ class SpringSchedulingTest extends AgentTestRunner {
69102

70103
def "schedule lambda test"() {
71104
setup:
72-
def context = new AnnotationConfigApplicationContext(LambdaTaskConfig)
105+
def context = new AnnotationConfigApplicationContext(LambdaTaskConfig, SchedulingConfig)
73106
def configurer = context.getBean(LambdaTaskConfigurer)
74107

75108
configurer.singleUseLatch.await(2000, TimeUnit.MILLISECONDS)
76109

77110
expect:
78-
assertTraces(1) {
79-
trace(1) {
111+
def hasParent = legacyTracing()
112+
assertTraces(hasParent ? 1 : 2) {
113+
if (!hasParent) {
114+
trace(1) {
115+
basicSpan(it, "parent")
116+
}
117+
}
118+
trace(hasParent ? 2 : 1) {
119+
if (hasParent) {
120+
basicSpan(it, "parent")
121+
}
80122
span {
81123
resourceNameContains("LambdaTaskConfigurer\$\$Lambda")
82124
operationName "scheduled.call"
83-
parent()
125+
hasParent ? childOfPrevious() : parent()
84126
errored false
85127
tags {
86128
"$Tags.COMPONENT" "spring-scheduling"
@@ -94,3 +136,11 @@ class SpringSchedulingTest extends AgentTestRunner {
94136
context.close()
95137
}
96138
}
139+
140+
class SpringSchedulingLegacyTracingForkedTest extends SpringSchedulingTest {
141+
@Override
142+
def legacyTracing() {
143+
true
144+
}
145+
}
146+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import static datadog.trace.agent.test.utils.TraceUtils.runnableUnderTrace;
2+
3+
import java.time.Clock;
4+
import java.time.Duration;
5+
import java.time.Instant;
6+
import java.util.Date;
7+
import java.util.concurrent.ScheduledFuture;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.scheduling.TaskScheduler;
10+
import org.springframework.scheduling.Trigger;
11+
import org.springframework.scheduling.annotation.SchedulingConfigurer;
12+
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
13+
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
14+
15+
@Configuration
16+
public class SchedulingConfig implements SchedulingConfigurer {
17+
static class TracingTaskScheduler implements TaskScheduler {
18+
final ThreadPoolTaskScheduler scheduler;
19+
20+
TracingTaskScheduler() {
21+
scheduler = new ThreadPoolTaskScheduler();
22+
scheduler.initialize();
23+
}
24+
25+
@Override
26+
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
27+
return scheduler.scheduleWithFixedDelay(() -> runnableUnderTrace("parent", task), delay);
28+
}
29+
30+
@Override
31+
public Clock getClock() {
32+
return Clock.systemUTC();
33+
}
34+
35+
@Override
36+
public ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
37+
return scheduler.schedule(() -> runnableUnderTrace("parent", task), startTime);
38+
}
39+
40+
@Override
41+
public ScheduledFuture<?> scheduleAtFixedRate(
42+
Runnable task, Instant startTime, Duration period) {
43+
return scheduler.scheduleAtFixedRate(
44+
() -> runnableUnderTrace("parent", task), startTime, period);
45+
}
46+
47+
@Override
48+
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
49+
return scheduler.scheduleAtFixedRate(() -> runnableUnderTrace("parent", task), period);
50+
}
51+
52+
@Override
53+
public ScheduledFuture<?> scheduleWithFixedDelay(
54+
Runnable task, Instant startTime, Duration delay) {
55+
return scheduler.scheduleWithFixedDelay(
56+
() -> runnableUnderTrace("parent", task), startTime, delay);
57+
}
58+
59+
@Override
60+
public ScheduledFuture<?> schedule(Runnable runnable, Trigger trigger) {
61+
return scheduler.schedule(() -> runnableUnderTrace("parent", runnable), trigger);
62+
}
63+
64+
@Override
65+
public ScheduledFuture<?> schedule(Runnable runnable, Date date) {
66+
return scheduler.schedule(() -> runnableUnderTrace("parent", runnable), date);
67+
}
68+
69+
@Override
70+
public ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, Date date, long l) {
71+
return scheduler.scheduleAtFixedRate(() -> runnableUnderTrace("parent", runnable), date, l);
72+
}
73+
74+
@Override
75+
public ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, long l) {
76+
return scheduler.scheduleAtFixedRate(() -> runnableUnderTrace("parent", runnable), l);
77+
}
78+
79+
@Override
80+
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable, Date date, long l) {
81+
return scheduler.scheduleWithFixedDelay(
82+
() -> runnableUnderTrace("parent", runnable), date, l);
83+
}
84+
85+
@Override
86+
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable, long l) {
87+
return scheduler.scheduleWithFixedDelay(() -> runnableUnderTrace("parent", runnable), l);
88+
}
89+
}
90+
91+
@Override
92+
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
93+
scheduledTaskRegistrar.setTaskScheduler(new TracingTaskScheduler());
94+
}
95+
}

0 commit comments

Comments
 (0)