Skip to content

Commit 27aba3b

Browse files
committed
Add auto configuration for spring scheduling instrumentation using aop
1 parent 8a10097 commit 27aba3b

File tree

8 files changed

+425
-1
lines changed

8 files changed

+425
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling;
7+
8+
import io.opentelemetry.api.common.AttributesBuilder;
9+
import io.opentelemetry.context.Context;
10+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
11+
import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes;
12+
import javax.annotation.Nullable;
13+
import org.springframework.lang.NonNull;
14+
15+
/** Expose current thread to attributes. */
16+
class InternalThreadAttributesExtractor<REQUEST, RESPONSE>
17+
implements AttributesExtractor<REQUEST, RESPONSE> {
18+
@Override
19+
public void onStart(
20+
@NonNull AttributesBuilder attributes,
21+
@NonNull Context parentContext,
22+
@NonNull REQUEST classAndMethod) {
23+
attributes
24+
.put(ThreadIncubatingAttributes.THREAD_ID, Thread.currentThread().getId())
25+
.put(ThreadIncubatingAttributes.THREAD_NAME, Thread.currentThread().getName());
26+
}
27+
28+
@Override
29+
public void onEnd(
30+
@NonNull AttributesBuilder attributes,
31+
@NonNull Context context,
32+
@NonNull REQUEST classAndMethod,
33+
@Nullable RESPONSE o,
34+
@Nullable Throwable error) {
35+
// ignored
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.api.common.AttributeKey;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.context.Scope;
12+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
13+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
14+
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
15+
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
16+
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
17+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
18+
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
19+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
20+
import org.aspectj.lang.ProceedingJoinPoint;
21+
import org.aspectj.lang.annotation.Around;
22+
import org.aspectj.lang.annotation.Aspect;
23+
import org.aspectj.lang.annotation.Pointcut;
24+
import org.aspectj.lang.reflect.MethodSignature;
25+
import org.springframework.aop.framework.AopProxyUtils;
26+
27+
/**
28+
* Spring Scheduling instrumentation aop.
29+
*
30+
* <p>This aspect would intercept all methods annotated with {@link
31+
* org.springframework.scheduling.annotation.Scheduled} and {@link
32+
* org.springframework.scheduling.annotation.Schedules}.
33+
*
34+
* <p>Normally this would cover most of the Spring Scheduling use cases, but if you register jobs
35+
* programmatically such as {@link
36+
* org.springframework.scheduling.config.ScheduledTaskRegistrar#addCronTask}, this aspect would not
37+
* cover them. You may use {@link io.opentelemetry.instrumentation.annotations.WithSpan} to trace
38+
* these jobs manually.
39+
*/
40+
@Aspect
41+
final class SpringSchedulingInstrumentationAspect {
42+
public static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-scheduling-3.1";
43+
private final Instrumenter<ClassAndMethod, Object> instrumenter;
44+
45+
public SpringSchedulingInstrumentationAspect(
46+
OpenTelemetry openTelemetry, ConfigProperties configProperties) {
47+
CodeAttributesGetter<ClassAndMethod> codedAttributesGetter =
48+
ClassAndMethod.codeAttributesGetter();
49+
InstrumenterBuilder<ClassAndMethod, Object> builder =
50+
Instrumenter.builder(
51+
openTelemetry,
52+
INSTRUMENTATION_NAME,
53+
CodeSpanNameExtractor.create(codedAttributesGetter))
54+
.addAttributesExtractor(CodeAttributesExtractor.create(codedAttributesGetter))
55+
.addAttributesExtractor(new InternalThreadAttributesExtractor<>());
56+
if (configProperties.getBoolean(
57+
"otel.instrumentation.spring-scheduling.experimental-span-attributes", false)) {
58+
builder.addAttributesExtractor(
59+
AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "spring_scheduling"));
60+
}
61+
instrumenter = builder.buildInstrumenter();
62+
}
63+
64+
@Pointcut(
65+
"@annotation(org.springframework.scheduling.annotation.Scheduled)"
66+
+ "|| @annotation(org.springframework.scheduling.annotation.Schedules)")
67+
public void pointcut() {
68+
// ignored
69+
}
70+
71+
@Around("pointcut()")
72+
public Object execution(ProceedingJoinPoint joinPoint) throws Throwable {
73+
Context parent = Context.current();
74+
ClassAndMethod request =
75+
ClassAndMethod.create(
76+
AopProxyUtils.ultimateTargetClass(joinPoint.getTarget()),
77+
((MethodSignature) joinPoint.getSignature()).getMethod().getName());
78+
if (!instrumenter.shouldStart(parent, request)) {
79+
return joinPoint.proceed();
80+
}
81+
Context context = instrumenter.start(parent, request);
82+
try (Scope ignored = context.makeCurrent()) {
83+
Object object = joinPoint.proceed();
84+
instrumenter.end(context, request, object, null);
85+
return object;
86+
} catch (Throwable t) {
87+
instrumenter.end(context, request, null, t);
88+
throw t;
89+
}
90+
}
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
10+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
11+
import org.aspectj.lang.annotation.Aspect;
12+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
13+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
14+
import org.springframework.context.annotation.Bean;
15+
import org.springframework.context.annotation.Configuration;
16+
import org.springframework.scheduling.annotation.Scheduled;
17+
18+
/**
19+
* Configures an aspect for tracing.
20+
*
21+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
22+
* at any time.
23+
*/
24+
@ConditionalOnBean(OpenTelemetry.class)
25+
@ConditionalOnEnabledInstrumentation(module = "spring-scheduling")
26+
@ConditionalOnClass({Scheduled.class, Aspect.class})
27+
@Configuration
28+
class SpringSchedulingInstrumentationAutoConfiguration {
29+
@Bean
30+
SpringSchedulingInstrumentationAspect springSchedulingInstrumentationAspect(
31+
OpenTelemetry openTelemetry, ConfigProperties configProperties) {
32+
return new SpringSchedulingInstrumentationAspect(openTelemetry, configProperties);
33+
}
34+
}

instrumentation/spring/spring-boot-autoconfigure/src/main/javaSpring3/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/annotations/OpenTelemetryAnnotationsRuntimeHints.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
1919
.registerType(
2020
TypeReference.of(
2121
"io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.annotations.InstrumentationWithSpanAspect"),
22+
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
23+
.registerType(
24+
TypeReference.of(
25+
"io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAspect"),
2226
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
2327
}
2428
}

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.m
99
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.r2dbc.R2dbcInstrumentationAutoConfiguration,\
1010
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.SpringWebInstrumentationAutoConfiguration,\
1111
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\
12-
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration
12+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
13+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration
1314

1415
org.springframework.context.ApplicationListener=\
1516
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging.LogbackAppenderApplicationListener

instrumentation/spring/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.w
1010
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration
1111
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.RestClientInstrumentationAutoConfiguration
1212
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc6InstrumentationAutoConfiguration
13+
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration

0 commit comments

Comments
 (0)