Skip to content

Commit

Permalink
Add auto configuration for spring scheduling instrumentation using aop
Browse files Browse the repository at this point in the history
  • Loading branch information
kyy1996 committed Oct 15, 2024
1 parent 8a10097 commit 27aba3b
Show file tree
Hide file tree
Showing 8 changed files with 425 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling;

import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.semconv.incubating.ThreadIncubatingAttributes;
import javax.annotation.Nullable;
import org.springframework.lang.NonNull;

/** Expose current thread to attributes. */
class InternalThreadAttributesExtractor<REQUEST, RESPONSE>
implements AttributesExtractor<REQUEST, RESPONSE> {
@Override
public void onStart(
@NonNull AttributesBuilder attributes,
@NonNull Context parentContext,
@NonNull REQUEST classAndMethod) {
attributes
.put(ThreadIncubatingAttributes.THREAD_ID, Thread.currentThread().getId())
.put(ThreadIncubatingAttributes.THREAD_NAME, Thread.currentThread().getName());
}

@Override
public void onEnd(
@NonNull AttributesBuilder attributes,
@NonNull Context context,
@NonNull REQUEST classAndMethod,
@Nullable RESPONSE o,
@Nullable Throwable error) {
// ignored
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeAttributesGetter;
import io.opentelemetry.instrumentation.api.incubator.semconv.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.incubator.semconv.util.ClassAndMethod;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.InstrumenterBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.AopProxyUtils;

/**
* Spring Scheduling instrumentation aop.
*
* <p>This aspect would intercept all methods annotated with {@link
* org.springframework.scheduling.annotation.Scheduled} and {@link
* org.springframework.scheduling.annotation.Schedules}.
*
* <p>Normally this would cover most of the Spring Scheduling use cases, but if you register jobs
* programmatically such as {@link
* org.springframework.scheduling.config.ScheduledTaskRegistrar#addCronTask}, this aspect would not
* cover them. You may use {@link io.opentelemetry.instrumentation.annotations.WithSpan} to trace
* these jobs manually.
*/
@Aspect
final class SpringSchedulingInstrumentationAspect {
public static final String INSTRUMENTATION_NAME = "io.opentelemetry.spring-scheduling-3.1";
private final Instrumenter<ClassAndMethod, Object> instrumenter;

public SpringSchedulingInstrumentationAspect(
OpenTelemetry openTelemetry, ConfigProperties configProperties) {
CodeAttributesGetter<ClassAndMethod> codedAttributesGetter =
ClassAndMethod.codeAttributesGetter();
InstrumenterBuilder<ClassAndMethod, Object> builder =
Instrumenter.builder(
openTelemetry,
INSTRUMENTATION_NAME,
CodeSpanNameExtractor.create(codedAttributesGetter))
.addAttributesExtractor(CodeAttributesExtractor.create(codedAttributesGetter))
.addAttributesExtractor(new InternalThreadAttributesExtractor<>());
if (configProperties.getBoolean(
"otel.instrumentation.spring-scheduling.experimental-span-attributes", false)) {
builder.addAttributesExtractor(
AttributesExtractor.constant(AttributeKey.stringKey("job.system"), "spring_scheduling"));
}
instrumenter = builder.buildInstrumenter();
}

@Pointcut(
"@annotation(org.springframework.scheduling.annotation.Scheduled)"
+ "|| @annotation(org.springframework.scheduling.annotation.Schedules)")
public void pointcut() {
// ignored
}

@Around("pointcut()")
public Object execution(ProceedingJoinPoint joinPoint) throws Throwable {
Context parent = Context.current();
ClassAndMethod request =
ClassAndMethod.create(
AopProxyUtils.ultimateTargetClass(joinPoint.getTarget()),
((MethodSignature) joinPoint.getSignature()).getMethod().getName());
if (!instrumenter.shouldStart(parent, request)) {
return joinPoint.proceed();
}
Context context = instrumenter.start(parent, request);
try (Scope ignored = context.makeCurrent()) {
Object object = joinPoint.proceed();
instrumenter.end(context, request, object, null);
return object;
} catch (Throwable t) {
instrumenter.end(context, request, null, t);
throw t;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;

/**
* Configures an aspect for tracing.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
@ConditionalOnBean(OpenTelemetry.class)
@ConditionalOnEnabledInstrumentation(module = "spring-scheduling")
@ConditionalOnClass({Scheduled.class, Aspect.class})
@Configuration
class SpringSchedulingInstrumentationAutoConfiguration {
@Bean
SpringSchedulingInstrumentationAspect springSchedulingInstrumentationAspect(
OpenTelemetry openTelemetry, ConfigProperties configProperties) {
return new SpringSchedulingInstrumentationAspect(openTelemetry, configProperties);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
.registerType(
TypeReference.of(
"io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.annotations.InstrumentationWithSpanAspect"),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
.registerType(
TypeReference.of(
"io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAspect"),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.m
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.r2dbc.R2dbcInstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.SpringWebInstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc5InstrumentationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration

org.springframework.context.ApplicationListener=\
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.logging.LogbackAppenderApplicationListener
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.w
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webflux.SpringWebfluxInstrumentationAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.web.RestClientInstrumentationAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.webmvc.SpringWebMvc6InstrumentationAutoConfiguration
io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAutoConfiguration
Loading

0 comments on commit 27aba3b

Please sign in to comment.