diff --git a/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/v3_1/SpringSchedulingCodeAttributesGetter.java b/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/v3_1/SpringSchedulingCodeAttributesGetter.java index b88e0c5ee2c8..30af42a0de38 100644 --- a/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/v3_1/SpringSchedulingCodeAttributesGetter.java +++ b/instrumentation/spring/spring-scheduling-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/spring/scheduling/v3_1/SpringSchedulingCodeAttributesGetter.java @@ -6,12 +6,49 @@ package io.opentelemetry.javaagent.instrumentation.spring.scheduling.v3_1; import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter; +import java.lang.reflect.Field; import org.springframework.scheduling.support.ScheduledMethodRunnable; public class SpringSchedulingCodeAttributesGetter implements CodeAttributesGetter { + private static final Class outcomeTrackingRunnableClass = getOutcomeTrackingRunnableClass(); + private static final Field outcomeTrackingRunnableField = + getOutcomeTrackingRunnableField(outcomeTrackingRunnableClass); + + private static Class getOutcomeTrackingRunnableClass() { + try { + return Class.forName("org.springframework.scheduling.config.Task$OutcomeTrackingRunnable"); + } catch (ClassNotFoundException exception) { + return null; + } + } + + private static Field getOutcomeTrackingRunnableField(Class clazz) { + try { + Field field = clazz.getDeclaredField("runnable"); + field.setAccessible(true); + return field; + } catch (Exception exception) { + return null; + } + } + + private static Runnable unwrap(Runnable runnable) { + if (outcomeTrackingRunnableClass != null + && outcomeTrackingRunnableField != null + && outcomeTrackingRunnableClass.isAssignableFrom(runnable.getClass())) { + try { + // task may be wrapped multiple times so + return unwrap((Runnable) outcomeTrackingRunnableField.get(runnable)); + } catch (IllegalAccessException ignore) { + // should not happen because setAccessible was called + } + } + return runnable; + } @Override public Class getCodeClass(Runnable runnable) { + runnable = unwrap(runnable); if (runnable instanceof ScheduledMethodRunnable) { ScheduledMethodRunnable scheduledMethodRunnable = (ScheduledMethodRunnable) runnable; return scheduledMethodRunnable.getMethod().getDeclaringClass(); @@ -22,6 +59,7 @@ public Class getCodeClass(Runnable runnable) { @Override public String getMethodName(Runnable runnable) { + runnable = unwrap(runnable); if (runnable instanceof ScheduledMethodRunnable) { ScheduledMethodRunnable scheduledMethodRunnable = (ScheduledMethodRunnable) runnable; return scheduledMethodRunnable.getMethod().getName(); diff --git a/instrumentation/spring/spring-security-config-6.0/javaagent/build.gradle.kts b/instrumentation/spring/spring-security-config-6.0/javaagent/build.gradle.kts index f30b3eaad1f3..d23da79c852c 100644 --- a/instrumentation/spring/spring-security-config-6.0/javaagent/build.gradle.kts +++ b/instrumentation/spring/spring-security-config-6.0/javaagent/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { library("io.projectreactor:reactor-core:3.5.0") testLibrary("org.springframework:spring-test:6.0.0") + testLibrary("org.springframework:spring-context:6.0.0") testLibrary("jakarta.servlet:jakarta.servlet-api:6.0.0") }