Support new annotation in spring-boot-autoconfigure
MODIFY
instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts
instrumentation/spring/spring-boot-autoconfigure/build.gradle.kts
@@ -9,12 +9,11 @@ val versions: Map<String, String> by project
val springBootVersion = versions["org.springframework.boot"]
dependencies {
- implementation(project(":instrumentation-annotations-support"))
-
implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion")
implementation("javax.validation:validation-api:2.0.1.Final")
+ implementation(project(":instrumentation-annotations-support"))
implementation(project(":instrumentation:spring:spring-web-3.1:library"))
implementation(project(":instrumentation:spring:spring-webmvc-3.1:library"))
implementation(project(":instrumentation:spring:spring-webflux-5.0:library"))
@@ -37,6 +36,7 @@ dependencies {
compileOnly("io.opentelemetry:opentelemetry-exporter-jaeger")
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
compileOnly("io.opentelemetry:opentelemetry-exporter-zipkin")
+ compileOnly(project(":instrumentation-annotations"))
testImplementation("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-aop:$springBootVersion")
@@ -58,7 +58,7 @@ dependencies {
testImplementation("io.opentelemetry:opentelemetry-exporter-jaeger")
testImplementation("io.opentelemetry:opentelemetry-exporter-otlp")
testImplementation("io.opentelemetry:opentelemetry-exporter-zipkin")
- testImplementation(project(":instrumentation-annotations-support"))
+ testImplementation(project(":instrumentation-annotations"))
}
tasks.compileTestJava {

MODIFY
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JoinPointRequest.java
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/JoinPointRequest.java
package io.opentelemetry.instrumentation.spring.autoconfigure.aspects;
-import io.opentelemetry.extension.annotations.WithSpan;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.instrumentation.annotations.WithSpan;
+import io.opentelemetry.instrumentation.api.util.SpanNames;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
final class JoinPointRequest {
+
private final JoinPoint joinPoint;
private final Method method;
- private final WithSpan annotation;
+ private final String spanName;
+ private final SpanKind spanKind;
JoinPointRequest(JoinPoint joinPoint) {
this.joinPoint = joinPoint;
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
this.method = methodSignature.getMethod();
- this.annotation = this.method.getDeclaredAnnotation(WithSpan.class);
+
+ // in rare cases, when interface method does not have annotations but the implementation does,
+ // and the AspectJ factory is configured to proxy interfaces, this class will receive the
+ // abstract interface method (without annotations) instead of the implementation method (with
+ // annotations); these defaults prevent NPEs in this scenario
+ String spanName = "";
+ SpanKind spanKind = SpanKind.INTERNAL;
+
+ io.opentelemetry.extension.annotations.WithSpan oldAnnotation =
+ this.method.getDeclaredAnnotation(io.opentelemetry.extension.annotations.WithSpan.class);
+ if (oldAnnotation != null) {
+ spanName = oldAnnotation.value();
+ spanKind = oldAnnotation.kind();
}
- Method method() {
- return method;
+ WithSpan annotation = this.method.getDeclaredAnnotation(WithSpan.class);
+ if (annotation != null) {
+ spanName = annotation.value();
+ spanKind = annotation.kind();
+ }
+
+ if (spanName.isEmpty()) {
+ spanName = SpanNames.fromMethod(method);
}
- WithSpan annotation() {
- return annotation;
+ this.spanName = spanName;
+ this.spanKind = spanKind;
+ }
+
+ String spanName() {
+ return spanName;
+ }
+
+ SpanKind spanKind() {
+ return spanKind;
+ }
+
+ Method method() {
+ return method;
}
Object[] args() {

MODIFY
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspect.java
package io.opentelemetry.instrumentation.spring.autoconfigure.aspects;
import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
-import io.opentelemetry.extension.annotations.WithSpan;
+import io.opentelemetry.instrumentation.annotations.WithSpan;
import io.opentelemetry.instrumentation.api.annotation.support.MethodSpanAttributesExtractor;
import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor;
import io.opentelemetry.instrumentation.api.annotation.support.async.AsyncOperationEndSupport;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
-import io.opentelemetry.instrumentation.api.util.SpanNames;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.ParameterNameDiscoverer;
/**
- * Uses Spring-AOP to wrap methods marked by {@link WithSpan} in a {@link
- * io.opentelemetry.api.trace.Span}.
+ * Uses Spring-AOP to wrap methods marked by {@link WithSpan} (or the deprecated {@link
+ * io.opentelemetry.extension.annotations.WithSpan}) in a {@link Span}.
*
* <p>Ensure methods annotated with {@link WithSpan} are implemented on beans managed by the Spring
* container.
*
- * <p>Note: This Aspect uses spring-aop to proxy beans. Therefore the {@link WithSpan} annotation
+ * <p>Note: This Aspect uses spring-aop to proxy beans. Therefore, the {@link WithSpan} annotation
* can not be applied to constructors.
*/
@Aspect
@@ -44,7 +43,7 @@ public class WithSpanAspect {
new WithSpanAspectParameterAttributeNamesExtractor(parameterNameDiscoverer);
instrumenter =
- Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, WithSpanAspect::spanName)
+ Instrumenter.builder(openTelemetry, INSTRUMENTATION_NAME, JoinPointRequest::spanName)
.addAttributesExtractor(
CodeAttributesExtractor.create(JointPointCodeAttributesExtractor.INSTANCE))
.addAttributesExtractor(
@@ -52,23 +51,12 @@ public class WithSpanAspect {
JoinPointRequest::method,
parameterAttributeNamesExtractor,
JoinPointRequest::args))
- .buildInstrumenter(WithSpanAspect::spanKind);
+ .buildInstrumenter(JoinPointRequest::spanKind);
}
- private static String spanName(JoinPointRequest request) {
- WithSpan annotation = request.annotation();
- String spanName = annotation.value();
- if (spanName.isEmpty()) {
- return SpanNames.fromMethod(request.method());
- }
- return spanName;
- }
-
- private static SpanKind spanKind(JoinPointRequest request) {
- return request.annotation().kind();
- }
-
- @Around("@annotation(io.opentelemetry.extension.annotations.WithSpan)")
+ @Around(
+ "@annotation(io.opentelemetry.extension.annotations.WithSpan) || "
+ + "@annotation(io.opentelemetry.instrumentation.annotations.WithSpan)")
public Object traceMethod(ProceedingJoinPoint pjp) throws Throwable {
JoinPointRequest request = new JoinPointRequest(pjp);

MODIFY
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectParameterAttributeNamesExtractor.java
instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectParameterAttributeNamesExtractor.java
package io.opentelemetry.instrumentation.spring.autoconfigure.aspects;
-import io.opentelemetry.extension.annotations.SpanAttribute;
+import io.opentelemetry.instrumentation.annotations.SpanAttribute;
import io.opentelemetry.instrumentation.api.annotation.support.ParameterAttributeNamesExtractor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@@ -34,14 +34,24 @@ class WithSpanAspectParameterAttributeNamesExtractor implements ParameterAttribu
@Nullable
private static String attributeName(Parameter parameter, String[] parameterNames, int index) {
+ io.opentelemetry.extension.annotations.SpanAttribute oldAnnotation =
+ parameter.getDeclaredAnnotation(io.opentelemetry.extension.annotations.SpanAttribute.class);
SpanAttribute annotation = parameter.getDeclaredAnnotation(SpanAttribute.class);
- if (annotation == null) {
+ if (oldAnnotation == null && annotation == null) {
return null;
}
+ if (annotation != null) {
String value = annotation.value();
if (!value.isEmpty()) {
return value;
}
+ }
+ if (oldAnnotation != null) {
+ String value = oldAnnotation.value();
+ if (!value.isEmpty()) {
+ return value;
+ }
+ }
if (parameterNames != null && index < parameterNames.length) {
String parameterName = parameterNames[index];
if (parameterName != null && !parameterName.isEmpty()) {

RENAME
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/WithSpanAspectTest.java
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/AbstractWithSpanAspectTest.java
@@ -14,8 +14,6 @@ import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.CODE_NAMESPACE;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import io.opentelemetry.extension.annotations.SpanAttribute;
-import io.opentelemetry.extension.annotations.WithSpan;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.data.StatusData;
@@ -33,58 +31,45 @@ import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.core.ParameterNameDiscoverer;
/** Spring AOP Test for {@link WithSpanAspect}. */
-public class WithSpanAspectTest {
+abstract class AbstractWithSpanAspectTest {
@RegisterExtension
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
- static class WithSpanTester {
- @WithSpan
- public String testWithSpan() {
- return "Span with name testWithSpan was created";
- }
+ private WithSpanTester withSpanTester;
+ private String unproxiedTesterSimpleClassName;
+ private String unproxiedTesterClassName;
- @WithSpan("greatestSpanEver")
- public String testWithSpanWithValue() {
- return "Span with name greatestSpanEver was created";
- }
+ abstract WithSpanTester newWithSpanTester();
- @WithSpan
- public String testWithSpanWithException() throws Exception {
- throw new Exception("Test @WithSpan With Exception");
- }
+ public interface WithSpanTester {
+ String testWithSpan();
- @WithSpan(kind = CLIENT)
- public String testWithClientSpan() {
- return "Span with name testWithClientSpan and SpanKind.CLIENT was created";
- }
+ String testWithSpanWithValue();
- @WithSpan
- public CompletionStage<String> testAsyncCompletionStage(CompletionStage<String> stage) {
- return stage;
- }
+ String testWithSpanWithException() throws Exception;
- @WithSpan
- public CompletableFuture<String> testAsyncCompletableFuture(CompletableFuture<String> stage) {
- return stage;
- }
+ String testWithClientSpan();
- @WithSpan
- public String withSpanAttributes(
- @SpanAttribute String discoveredName,
- @SpanAttribute String implicitName,
- @SpanAttribute("explicitName") String parameter,
- @SpanAttribute("nullAttribute") String nullAttribute,
- String notTraced) {
+ CompletionStage<String> testAsyncCompletionStage(CompletionStage<String> stage);
- return "hello!";
- }
- }
+ CompletableFuture<String> testAsyncCompletableFuture(CompletableFuture<String> stage);
- private WithSpanTester withSpanTester;
+ String withSpanAttributes(
+ String discoveredName,
+ String implicitName,
+ String parameter,
+ String nullAttribute,
+ String notTraced);
+ }
@BeforeEach
void setup() {
- AspectJProxyFactory factory = new AspectJProxyFactory(new WithSpanTester());
+ WithSpanTester unproxiedTester = newWithSpanTester();
+ unproxiedTesterSimpleClassName = unproxiedTester.getClass().getSimpleName();
+ unproxiedTesterClassName = unproxiedTester.getClass().getName();
+
+ AspectJProxyFactory factory = new AspectJProxyFactory();
+ factory.setTarget(unproxiedTester);
ParameterNameDiscoverer parameterNameDiscoverer =
new ParameterNameDiscoverer() {
@Override
@@ -106,7 +91,7 @@ public class WithSpanAspectTest {
@Test
@DisplayName("when method is annotated with @WithSpan should wrap method execution in a Span")
- void withSpanWithDefaults() throws Throwable {
+ void withSpanWithDefaults() {
// when
testing.runWithSpan("parent", withSpanTester::testWithSpan);
@@ -118,18 +103,18 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testWithSpan")
+ span.hasName(unproxiedTesterSimpleClassName + ".testWithSpan")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testWithSpan"))));
}
@Test
@DisplayName(
"when @WithSpan value is set should wrap method execution in a Span with custom name")
- void withSpanName() throws Throwable {
+ void withSpanName() {
// when
testing.runWithSpan("parent", () -> withSpanTester.testWithSpanWithValue());
@@ -145,14 +130,14 @@ public class WithSpanAspectTest {
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testWithSpanWithValue"))));
}
@Test
@DisplayName(
"when method is annotated with @WithSpan AND an exception is thrown span should record the exception")
- void withSpanError() throws Throwable {
+ void withSpanError() {
assertThatThrownBy(() -> withSpanTester.testWithSpanWithException())
.isInstanceOf(Exception.class);
@@ -162,18 +147,18 @@ public class WithSpanAspectTest {
trace ->
trace.hasSpansSatisfyingExactly(
span ->
- span.hasName("WithSpanTester.testWithSpanWithException")
+ span.hasName(unproxiedTesterSimpleClassName + ".testWithSpanWithException")
.hasKind(INTERNAL)
.hasStatus(StatusData.error())
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testWithSpanWithException"))));
}
@Test
@DisplayName(
"when method is annotated with @WithSpan(kind=CLIENT) should build span with the declared SpanKind")
- void withSpanKind() throws Throwable {
+ void withSpanKind() {
// when
testing.runWithSpan("parent", () -> withSpanTester.testWithClientSpan());
@@ -185,17 +170,16 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testWithClientSpan")
+ span.hasName(unproxiedTesterSimpleClassName + ".testWithClientSpan")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testWithClientSpan"))));
}
@Test
- @DisplayName("")
- void withSpanAttributes() throws Throwable {
+ void withSpanAttributes() {
// when
testing.runWithSpan(
"parent", () -> withSpanTester.withSpanAttributes("foo", "bar", "baz", null, "fizz"));
@@ -208,11 +192,11 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.withSpanAttributes")
+ span.hasName(unproxiedTesterSimpleClassName + ".withSpanAttributes")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "withSpanAttributes"),
equalTo(stringKey("discoveredName"), "foo"),
equalTo(stringKey("implicitName"), "bar"),
@@ -225,7 +209,7 @@ public class WithSpanAspectTest {
@Test
@DisplayName("should end Span on complete")
- void onComplete() throws Throwable {
+ void onComplete() {
CompletableFuture<String> future = new CompletableFuture<>();
// when
@@ -250,17 +234,17 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletionStage")
+ span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
}
@Test
@DisplayName("should end Span on completeException AND should record the exception")
- void onCompleteExceptionally() throws Throwable {
+ void onCompleteExceptionally() {
CompletableFuture<String> future = new CompletableFuture<>();
// when
@@ -285,18 +269,18 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletionStage")
+ span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
.hasKind(INTERNAL)
.hasStatus(StatusData.error())
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
}
@Test
@DisplayName("should end Span on incompatible return value")
- void onIncompatibleReturnValue() throws Throwable {
+ void onIncompatibleReturnValue() {
// when
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null));
@@ -308,11 +292,11 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletionStage")
+ span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
}
}
@@ -323,7 +307,7 @@ public class WithSpanAspectTest {
@Test
@DisplayName("should end Span on complete")
- void onComplete() throws Throwable {
+ void onComplete() {
CompletableFuture<String> future = new CompletableFuture<>();
// when
@@ -348,17 +332,18 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletableFuture")
+ span.hasName(
+ unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
}
@Test
@DisplayName("should end Span on completeException AND should record the exception")
- void onCompleteExceptionally() throws Throwable {
+ void onCompleteExceptionally() {
CompletableFuture<String> future = new CompletableFuture<>();
// when
@@ -383,18 +368,19 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletableFuture")
+ span.hasName(
+ unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
.hasKind(INTERNAL)
.hasStatus(StatusData.error())
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
}
@Test
@DisplayName("should end the Span when already complete")
- void onCompletedFuture() throws Throwable {
+ void onCompletedFuture() {
CompletableFuture<String> future = CompletableFuture.completedFuture("Done");
// when
@@ -408,17 +394,18 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletableFuture")
+ span.hasName(
+ unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
}
@Test
@DisplayName("should end the Span when already failed")
- void onFailedFuture() throws Throwable {
+ void onFailedFuture() {
CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
@@ -433,18 +420,19 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletableFuture")
+ span.hasName(
+ unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
.hasKind(INTERNAL)
.hasStatus(StatusData.error())
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
}
@Test
@DisplayName("should end Span on incompatible return value")
- void onIncompatibleReturnValue() throws Throwable {
+ void onIncompatibleReturnValue() {
// when
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null));
@@ -456,11 +444,12 @@ public class WithSpanAspectTest {
trace.hasSpansSatisfyingExactly(
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
span ->
- span.hasName("WithSpanTester.testAsyncCompletableFuture")
+ span.hasName(
+ unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
.hasKind(INTERNAL)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
- equalTo(CODE_NAMESPACE, WithSpanTester.class.getName()),
+ equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
}
}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/InstrumentationWithSpanAspectTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.aspects;
+
+import static io.opentelemetry.api.trace.SpanKind.CLIENT;
+
+import io.opentelemetry.instrumentation.annotations.SpanAttribute;
+import io.opentelemetry.instrumentation.annotations.WithSpan;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+class InstrumentationWithSpanAspectTest extends AbstractWithSpanAspectTest {
+
+ @Override
+ WithSpanTester newWithSpanTester() {
+ return new InstrumentationWithSpanTester();
+ }
+
+ static class InstrumentationWithSpanTester implements WithSpanTester {
+ @Override
+ @WithSpan
+ public String testWithSpan() {
+ return "Span with name testWithSpan was created";
+ }
+
+ @Override
+ @WithSpan("greatestSpanEver")
+ public String testWithSpanWithValue() {
+ return "Span with name greatestSpanEver was created";
+ }
+
+ @Override
+ @WithSpan
+ public String testWithSpanWithException() throws Exception {
+ throw new Exception("Test @WithSpan With Exception");
+ }
+
+ @Override
+ @WithSpan(kind = CLIENT)
+ public String testWithClientSpan() {
+ return "Span with name testWithClientSpan and SpanKind.CLIENT was created";
+ }
+
+ @Override
+ @WithSpan
+ public CompletionStage<String> testAsyncCompletionStage(CompletionStage<String> stage) {
+ return stage;
+ }
+
+ @Override
+ @WithSpan
+ public CompletableFuture<String> testAsyncCompletableFuture(CompletableFuture<String> stage) {
+ return stage;
+ }
+
+ @Override
+ @WithSpan
+ public String withSpanAttributes(
+ @SpanAttribute String discoveredName,
+ @SpanAttribute String implicitName,
+ @SpanAttribute("explicitName") String parameter,
+ @SpanAttribute("nullAttribute") String nullAttribute,
+ String notTraced) {
+
+ return "hello!";
+ }
+ }
+}

ADD
instrumentation/spring/spring-boot-autoconfigure/src/test/java/io/opentelemetry/instrumentation/spring/autoconfigure/aspects/SdkExtensionWithSpanAspectTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.autoconfigure.aspects;
+
+import static io.opentelemetry.api.trace.SpanKind.CLIENT;
+
+import io.opentelemetry.extension.annotations.SpanAttribute;
+import io.opentelemetry.extension.annotations.WithSpan;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+class SdkExtensionWithSpanAspectTest extends AbstractWithSpanAspectTest {
+
+ @Override
+ WithSpanTester newWithSpanTester() {
+ return new SdkExtensionWithSpanTester();
+ }
+
+ static class SdkExtensionWithSpanTester implements WithSpanTester {
+ @Override
+ @WithSpan
+ public String testWithSpan() {
+ return "Span with name testWithSpan was created";
+ }
+
+ @Override
+ @WithSpan("greatestSpanEver")
+ public String testWithSpanWithValue() {
+ return "Span with name greatestSpanEver was created";
+ }
+
+ @Override
+ @WithSpan
+ public String testWithSpanWithException() throws Exception {
+ throw new Exception("Test @WithSpan With Exception");
+ }
+
+ @Override
+ @WithSpan(kind = CLIENT)
+ public String testWithClientSpan() {
+ return "Span with name testWithClientSpan and SpanKind.CLIENT was created";
+ }
+
+ @Override
+ @WithSpan
+ public CompletionStage<String> testAsyncCompletionStage(CompletionStage<String> stage) {
+ return stage;
+ }
+
+ @Override
+ @WithSpan
+ public CompletableFuture<String> testAsyncCompletableFuture(CompletableFuture<String> stage) {
+ return stage;
+ }
+
+ @Override
+ @WithSpan
+ public String withSpanAttributes(
+ @SpanAttribute String discoveredName,
+ @SpanAttribute String implicitName,
+ @SpanAttribute("explicitName") String parameter,
+ @SpanAttribute("nullAttribute") String nullAttribute,
+ String notTraced) {
+
+ return "hello!";
+ }
+ }
+}

MODIFY
instrumentation/spring/starters/spring-starter/build.gradle.kts
instrumentation/spring/starters/spring-starter/build.gradle.kts
@@ -11,9 +11,9 @@ dependencies {
api("org.springframework.boot:spring-boot-starter:${versions["org.springframework.boot"]}")
api("org.springframework.boot:spring-boot-starter-aop:${versions["org.springframework.boot"]}")
api(project(":instrumentation:spring:spring-boot-autoconfigure"))
- api("io.opentelemetry:opentelemetry-extension-annotations")
api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
api("io.opentelemetry:opentelemetry-api")
api("io.opentelemetry:opentelemetry-exporter-logging")
api("io.opentelemetry:opentelemetry-sdk")
+ api(project(":instrumentation-annotations"))
}

Support new @WithSpan annotation in spring-boot-autoconfigure (#6378)