Skip to content

Commit ca0affa

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 96f1aa3 + 54e4183 commit ca0affa

File tree

20 files changed

+779
-3
lines changed

20 files changed

+779
-3
lines changed

.github/repository-settings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ settings](https://github.com/open-telemetry/community/blob/main/docs/how-to-conf
66

77
## General > Pull Requests
88

9-
* Allow squash merging > Default to pull request title and description
9+
* Allow squash merging > Default to pull request title
1010

1111
* Allow auto-merge
1212

docs/supported-libraries.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ These are the supported libraries and frameworks:
132132
| [Vert.x Kafka Client](https://vertx.io/docs/vertx-kafka-client/java/) | 3.6+ | N/A | [Messaging Spans] |
133133
| [Vert.x RxJava2](https://vertx.io/docs/vertx-rx/java2/) | 3.5+ | N/A | context propagation only |
134134
| [Vibur DBCP](https://www.vibur.org/) | 11.0+ | [opentelemetry-vibur-dbcp-11.0](../instrumentation/vibur-dbcp-11.0/library) | [Database Pool Metrics] |
135+
| [ZIO](https://zio.dev/) | 2.0.0+ | N/A | Context propagation |
135136

136137
**[1]** Standalone library instrumentation refers to instrumentation that can be used without the Java agent.
137138

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.spring.scheduling.v3_1;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
9+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
10+
import static net.bytebuddy.matcher.ElementMatchers.named;
11+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
12+
13+
import io.opentelemetry.context.Context;
14+
import io.opentelemetry.context.Scope;
15+
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
17+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
18+
import net.bytebuddy.asm.Advice;
19+
import net.bytebuddy.description.type.TypeDescription;
20+
import net.bytebuddy.matcher.ElementMatcher;
21+
import org.springframework.util.ErrorHandler;
22+
23+
public class DelegatingErrorHandlingRunnableInstrumentation implements TypeInstrumentation {
24+
@Override
25+
public ElementMatcher<TypeDescription> typeMatcher() {
26+
return named("org.springframework.scheduling.support.DelegatingErrorHandlingRunnable");
27+
}
28+
29+
@Override
30+
public void transform(TypeTransformer transformer) {
31+
transformer.applyAdviceToMethod(
32+
isConstructor().and(takesArgument(1, named("org.springframework.util.ErrorHandler"))),
33+
this.getClass().getName() + "$WrapErrorHandlerAdvice");
34+
35+
transformer.applyAdviceToMethod(
36+
isPublic().and(named("run")), this.getClass().getName() + "$RunAdvice");
37+
}
38+
39+
@SuppressWarnings("unused")
40+
public static class WrapErrorHandlerAdvice {
41+
42+
@Advice.OnMethodEnter(suppress = Throwable.class)
43+
public static void onEnter(
44+
@Advice.Argument(value = 1, readOnly = false) ErrorHandler errorHandler) {
45+
if (errorHandler != null) {
46+
errorHandler = new ErrorHandlerWrapper(errorHandler);
47+
}
48+
}
49+
}
50+
51+
@SuppressWarnings("unused")
52+
public static class RunAdvice {
53+
54+
@Advice.OnMethodEnter(suppress = Throwable.class)
55+
public static Scope onEnter() {
56+
Context parentContext = Java8BytecodeBridge.currentContext();
57+
return TaskContextHolder.init(parentContext).makeCurrent();
58+
}
59+
60+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
61+
public static void onExit(@Advice.Enter Scope scope) {
62+
if (scope != null) {
63+
scope.close();
64+
}
65+
}
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.spring.scheduling.v3_1;
7+
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.Scope;
10+
import org.springframework.util.ErrorHandler;
11+
12+
public final class ErrorHandlerWrapper implements ErrorHandler {
13+
private final ErrorHandler errorHandler;
14+
15+
public ErrorHandlerWrapper(ErrorHandler errorHandler) {
16+
this.errorHandler = errorHandler;
17+
}
18+
19+
@Override
20+
public void handleError(Throwable throwable) {
21+
Context taskContext = TaskContextHolder.getTaskContext(Context.current());
22+
// run the error handler with the same context as task execution
23+
try (Scope ignore = taskContext != null ? taskContext.makeCurrent() : null) {
24+
errorHandler.handleError(throwable);
25+
}
26+
}
27+
}

instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/v3_1/SpringSchedulingInstrumentationModule.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
package io.opentelemetry.javaagent.instrumentation.spring.scheduling.v3_1;
77

8-
import static java.util.Collections.singletonList;
8+
import static java.util.Arrays.asList;
99

1010
import com.google.auto.service.AutoService;
1111
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
@@ -21,6 +21,7 @@ public SpringSchedulingInstrumentationModule() {
2121

2222
@Override
2323
public List<TypeInstrumentation> typeInstrumentations() {
24-
return singletonList(new TaskSchedulerInstrumentation());
24+
return asList(
25+
new TaskSchedulerInstrumentation(), new DelegatingErrorHandlingRunnableInstrumentation());
2526
}
2627
}

instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/v3_1/SpringSchedulingRunnableWrapper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public void run() {
3131
}
3232

3333
Context context = instrumenter().start(parentContext, runnable);
34+
// remember the context, so it could be reused in error handler
35+
TaskContextHolder.set(context);
3436
try (Scope ignored = context.makeCurrent()) {
3537
runnable.run();
3638
instrumenter().end(context, runnable, null, null);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.spring.scheduling.v3_1;
7+
8+
import static io.opentelemetry.context.ContextKey.named;
9+
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.context.ContextKey;
12+
import io.opentelemetry.context.ImplicitContextKeyed;
13+
import javax.annotation.Nullable;
14+
15+
public final class TaskContextHolder implements ImplicitContextKeyed {
16+
17+
private static final ContextKey<TaskContextHolder> KEY =
18+
named("opentelemetry-spring-scheduling-task");
19+
20+
private Context taskContext;
21+
22+
private TaskContextHolder() {}
23+
24+
public static Context init(Context context) {
25+
if (context.get(KEY) != null) {
26+
return context;
27+
}
28+
return context.with(new TaskContextHolder());
29+
}
30+
31+
public static void set(Context taskContext) {
32+
TaskContextHolder holder = taskContext.get(KEY);
33+
if (holder != null) {
34+
holder.taskContext = taskContext;
35+
}
36+
}
37+
38+
@Nullable
39+
public static Context getTaskContext(Context context) {
40+
Context taskContext = null;
41+
TaskContextHolder holder = context.get(KEY);
42+
if (holder != null) {
43+
taskContext = holder.taskContext;
44+
}
45+
return taskContext;
46+
}
47+
48+
@Override
49+
public Context storeInContext(Context context) {
50+
return context.with(KEY, this);
51+
}
52+
}

instrumentation/spring/spring-scheduling-3.1/javaagent/src/test/groovy/SpringSchedulingTest.groovy

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import io.opentelemetry.api.trace.StatusCode
67
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
8+
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
79
import org.springframework.context.annotation.AnnotationConfigApplicationContext
810

911
import java.util.concurrent.CountDownLatch
@@ -121,4 +123,41 @@ class SpringSchedulingTest extends AgentInstrumentationSpecification {
121123
}
122124
}
123125
}
126+
127+
def "task with error test"() {
128+
setup:
129+
def context = new AnnotationConfigApplicationContext(TaskWithErrorConfig)
130+
def task = context.getBean(TaskWithError)
131+
132+
task.blockUntilExecute()
133+
134+
expect:
135+
assert task != null
136+
assertTraces(1) {
137+
trace(0, 2) {
138+
span(0) {
139+
name "TaskWithError.run"
140+
hasNoParent()
141+
status StatusCode.ERROR
142+
attributes {
143+
"job.system" "spring_scheduling"
144+
"code.namespace" "TaskWithError"
145+
"code.function" "run"
146+
}
147+
event(0) {
148+
eventName "$SemanticAttributes.EXCEPTION_EVENT_NAME"
149+
attributes {
150+
"$SemanticAttributes.EXCEPTION_TYPE" IllegalStateException.getName()
151+
"$SemanticAttributes.EXCEPTION_MESSAGE" "failure"
152+
"$SemanticAttributes.EXCEPTION_STACKTRACE" String
153+
}
154+
}
155+
}
156+
span(1) {
157+
name "error-handler"
158+
childOf(span(0))
159+
}
160+
}
161+
}
162+
}
124163
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import java.util.concurrent.CountDownLatch;
7+
import java.util.concurrent.TimeUnit;
8+
import org.springframework.scheduling.annotation.Scheduled;
9+
import org.springframework.stereotype.Component;
10+
11+
@Component
12+
public class TaskWithError implements Runnable {
13+
14+
private final CountDownLatch latch = new CountDownLatch(1);
15+
16+
@Scheduled(fixedRate = 5000)
17+
@Override
18+
public void run() {
19+
latch.countDown();
20+
throw new IllegalStateException("failure");
21+
}
22+
23+
public void blockUntilExecute() throws InterruptedException {
24+
latch.await(5, TimeUnit.SECONDS);
25+
}
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import io.opentelemetry.instrumentation.testing.GlobalTraceUtil;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.scheduling.TaskScheduler;
10+
import org.springframework.scheduling.annotation.EnableScheduling;
11+
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
12+
13+
@Configuration
14+
@EnableScheduling
15+
public class TaskWithErrorConfig {
16+
@Bean
17+
public TaskWithError task() {
18+
return new TaskWithError();
19+
}
20+
21+
@Bean
22+
public TaskScheduler taskScheduler() {
23+
ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler();
24+
scheduler.setErrorHandler(throwable -> GlobalTraceUtil.runWithSpan("error-handler", () -> {}));
25+
return scheduler;
26+
}
27+
}

0 commit comments

Comments
 (0)