|
| 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 | +} |
0 commit comments