remove support for deprecated @WithSpan annotation from spring starter (#10530)
This commit is contained in:
parent
4d70bb3bbd
commit
d73304b078
|
@ -30,11 +30,4 @@ public class InstrumentationAnnotationsAutoConfiguration {
|
|||
InstrumentationWithSpanAspect otelInstrumentationWithSpanAspect(OpenTelemetry openTelemetry) {
|
||||
return new InstrumentationWithSpanAspect(openTelemetry, parameterNameDiscoverer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility
|
||||
@ConditionalOnClass(io.opentelemetry.extension.annotations.WithSpan.class)
|
||||
SdkExtensionWithSpanAspect otelSdkExtensionWithSpanAspect(OpenTelemetry openTelemetry) {
|
||||
return new SdkExtensionWithSpanAspect(openTelemetry, parameterNameDiscoverer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,25 +69,4 @@ final class JoinPointRequest {
|
|||
return new JoinPointRequest(joinPoint, method, spanName, spanKind);
|
||||
}
|
||||
}
|
||||
|
||||
static final class SdkExtensionAnnotationFactory implements Factory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility
|
||||
public JoinPointRequest create(JoinPoint joinPoint) {
|
||||
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = methodSignature.getMethod();
|
||||
|
||||
// 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
|
||||
io.opentelemetry.extension.annotations.WithSpan annotation =
|
||||
method.getDeclaredAnnotation(io.opentelemetry.extension.annotations.WithSpan.class);
|
||||
String spanName = annotation != null ? annotation.value() : "";
|
||||
SpanKind spanKind = annotation != null ? annotation.kind() : SpanKind.INTERNAL;
|
||||
|
||||
return new JoinPointRequest(joinPoint, method, spanName, spanKind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
|
||||
@Aspect
|
||||
class SdkExtensionWithSpanAspect extends WithSpanAspect {
|
||||
|
||||
SdkExtensionWithSpanAspect(
|
||||
OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
super(
|
||||
openTelemetry,
|
||||
parameterNameDiscoverer,
|
||||
new JoinPointRequest.SdkExtensionAnnotationFactory(),
|
||||
new WithSpanAspectParameterAttributeNamesExtractor
|
||||
.SdkExtensionAnnotationAttributeNameSupplier());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Around("@annotation(io.opentelemetry.extension.annotations.WithSpan)")
|
||||
public Object traceMethod(ProceedingJoinPoint pjp) throws Throwable {
|
||||
return super.traceMethod(pjp);
|
||||
}
|
||||
}
|
|
@ -19,8 +19,7 @@ import org.aspectj.lang.ProceedingJoinPoint;
|
|||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
|
||||
/**
|
||||
* Uses Spring-AOP to wrap methods marked by {@link WithSpan} (or the deprecated {@link
|
||||
* io.opentelemetry.extension.annotations.WithSpan}) in a {@link Span}.
|
||||
* Uses Spring-AOP to wrap methods marked by {@link WithSpan} in a {@link Span}.
|
||||
*
|
||||
* <p>Ensure methods annotated with {@link WithSpan} are implemented on beans managed by the Spring
|
||||
* container.
|
||||
|
|
|
@ -73,18 +73,4 @@ class WithSpanAspectParameterAttributeNamesExtractor implements ParameterAttribu
|
|||
return annotation == null ? null : annotation.value();
|
||||
}
|
||||
}
|
||||
|
||||
static final class SdkExtensionAnnotationAttributeNameSupplier
|
||||
implements SpanAttributeNameSupplier {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility
|
||||
public String spanAttributeName(Parameter parameter) {
|
||||
io.opentelemetry.extension.annotations.SpanAttribute annotation =
|
||||
parameter.getDeclaredAnnotation(
|
||||
io.opentelemetry.extension.annotations.SpanAttribute.class);
|
||||
return annotation == null ? null : annotation.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,456 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat;
|
||||
import static io.opentelemetry.semconv.SemanticAttributes.CODE_FUNCTION;
|
||||
import static io.opentelemetry.semconv.SemanticAttributes.CODE_NAMESPACE;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
|
||||
/** Spring AOP Test for {@link WithSpanAspect}. */
|
||||
abstract class AbstractWithSpanAspectTest {
|
||||
@RegisterExtension
|
||||
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
private WithSpanTester withSpanTester;
|
||||
private String unproxiedTesterSimpleClassName;
|
||||
private String unproxiedTesterClassName;
|
||||
|
||||
public interface WithSpanTester {
|
||||
String testWithSpan();
|
||||
|
||||
String testWithSpanWithValue();
|
||||
|
||||
String testWithSpanWithException() throws Exception;
|
||||
|
||||
String testWithClientSpan();
|
||||
|
||||
CompletionStage<String> testAsyncCompletionStage(CompletionStage<String> stage);
|
||||
|
||||
CompletableFuture<String> testAsyncCompletableFuture(CompletableFuture<String> stage);
|
||||
|
||||
String withSpanAttributes(
|
||||
String discoveredName,
|
||||
String implicitName,
|
||||
String parameter,
|
||||
String nullAttribute,
|
||||
String notTraced);
|
||||
}
|
||||
|
||||
abstract WithSpanTester newWithSpanTester();
|
||||
|
||||
abstract WithSpanAspect newWithSpanAspect(
|
||||
OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer);
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
WithSpanTester unproxiedTester = newWithSpanTester();
|
||||
unproxiedTesterSimpleClassName = unproxiedTester.getClass().getSimpleName();
|
||||
unproxiedTesterClassName = unproxiedTester.getClass().getName();
|
||||
|
||||
AspectJProxyFactory factory = new AspectJProxyFactory();
|
||||
factory.setTarget(unproxiedTester);
|
||||
ParameterNameDiscoverer parameterNameDiscoverer =
|
||||
new ParameterNameDiscoverer() {
|
||||
@Override
|
||||
public String[] getParameterNames(Method method) {
|
||||
return new String[] {"discoveredName", null, "parameter", "nullAttribute", "notTraced"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParameterNames(Constructor<?> constructor) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
WithSpanAspect aspect = newWithSpanAspect(testing.getOpenTelemetry(), parameterNameDiscoverer);
|
||||
factory.addAspect(aspect);
|
||||
|
||||
withSpanTester = factory.getProxy();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when method is annotated with @WithSpan should wrap method execution in a Span")
|
||||
void withSpanWithDefaults() {
|
||||
// when
|
||||
testing.runWithSpan("parent", withSpanTester::testWithSpan);
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testWithSpan")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
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() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testWithSpanWithValue());
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName("greatestSpanEver")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
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() {
|
||||
assertThatThrownBy(() -> withSpanTester.testWithSpanWithException())
|
||||
.isInstanceOf(Exception.class);
|
||||
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testWithSpanWithException")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasAttributesSatisfyingExactly(
|
||||
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() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testWithClientSpan());
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testWithClientSpan")
|
||||
.hasKind(CLIENT)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testWithClientSpan"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withSpanAttributes() {
|
||||
// when
|
||||
testing.runWithSpan(
|
||||
"parent", () -> withSpanTester.withSpanAttributes("foo", "bar", "baz", null, "fizz"));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".withSpanAttributes")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "withSpanAttributes"),
|
||||
equalTo(stringKey("discoveredName"), "foo"),
|
||||
equalTo(stringKey("implicitName"), "bar"),
|
||||
equalTo(stringKey("explicitName"), "baz"))));
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("with a method annotated with @WithSpan returns CompletionStage")
|
||||
class WithCompletionStage {
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on complete")
|
||||
void onComplete() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.complete("DONE");
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on completeException AND should record the exception")
|
||||
void onCompleteExceptionally() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on incompatible return value")
|
||||
void onIncompatibleReturnValue() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("with a method annotated with @WithSpan returns CompletableFuture")
|
||||
class WithCompletableFuture {
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on complete")
|
||||
void onComplete() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.complete("DONE");
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on completeException AND should record the exception")
|
||||
void onCompleteExceptionally() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end the Span when already complete")
|
||||
void onCompletedFuture() {
|
||||
CompletableFuture<String> future = CompletableFuture.completedFuture("Done");
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end the Span when already failed")
|
||||
void onFailedFuture() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on incompatible return value")
|
||||
void onIncompatibleReturnValue() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,30 +24,18 @@ class InstrumentationAnnotationsAutoConfigurationTest {
|
|||
void instrumentationEnabled() {
|
||||
contextRunner
|
||||
.withPropertyValues("otel.instrumentation.annotations.enabled=true")
|
||||
.run(
|
||||
context ->
|
||||
assertThat(context)
|
||||
.hasBean("otelInstrumentationWithSpanAspect")
|
||||
.hasBean("otelSdkExtensionWithSpanAspect"));
|
||||
.run(context -> assertThat(context).hasBean("otelInstrumentationWithSpanAspect"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void instrumentationDisabled() {
|
||||
contextRunner
|
||||
.withPropertyValues("otel.instrumentation.annotations.enabled=false")
|
||||
.run(
|
||||
context ->
|
||||
assertThat(context)
|
||||
.doesNotHaveBean("otelInstrumentationWithSpanAspect")
|
||||
.doesNotHaveBean("otelSdkExtensionWithSpanAspect"));
|
||||
.run(context -> assertThat(context).doesNotHaveBean("otelInstrumentationWithSpanAspect"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultConfiguration() {
|
||||
contextRunner.run(
|
||||
context ->
|
||||
assertThat(context)
|
||||
.hasBean("otelInstrumentationWithSpanAspect")
|
||||
.hasBean("otelSdkExtensionWithSpanAspect"));
|
||||
contextRunner.run(context -> assertThat(context).hasBean("otelInstrumentationWithSpanAspect"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,66 +5,220 @@
|
|||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations;
|
||||
|
||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat;
|
||||
import static io.opentelemetry.semconv.SemanticAttributes.CODE_FUNCTION;
|
||||
import static io.opentelemetry.semconv.SemanticAttributes.CODE_NAMESPACE;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.annotations.SpanAttribute;
|
||||
import io.opentelemetry.instrumentation.annotations.WithSpan;
|
||||
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
|
||||
class InstrumentationWithSpanAspectTest extends AbstractWithSpanAspectTest {
|
||||
class InstrumentationWithSpanAspectTest {
|
||||
|
||||
@Override
|
||||
WithSpanTester newWithSpanTester() {
|
||||
return new InstrumentationWithSpanTester();
|
||||
}
|
||||
@RegisterExtension
|
||||
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||
|
||||
private InstrumentationWithSpanTester withSpanTester;
|
||||
private String unproxiedTesterSimpleClassName;
|
||||
private String unproxiedTesterClassName;
|
||||
|
||||
@Override
|
||||
WithSpanAspect newWithSpanAspect(
|
||||
OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
return new InstrumentationWithSpanAspect(openTelemetry, parameterNameDiscoverer);
|
||||
}
|
||||
|
||||
static class InstrumentationWithSpanTester implements WithSpanTester {
|
||||
@Override
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
InstrumentationWithSpanTester unproxiedTester = new InstrumentationWithSpanTester();
|
||||
unproxiedTesterSimpleClassName = unproxiedTester.getClass().getSimpleName();
|
||||
unproxiedTesterClassName = unproxiedTester.getClass().getName();
|
||||
|
||||
AspectJProxyFactory factory = new AspectJProxyFactory();
|
||||
factory.setTarget(unproxiedTester);
|
||||
ParameterNameDiscoverer parameterNameDiscoverer =
|
||||
new ParameterNameDiscoverer() {
|
||||
@Override
|
||||
public String[] getParameterNames(Method method) {
|
||||
return new String[] {"discoveredName", null, "parameter", "nullAttribute", "notTraced"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParameterNames(Constructor<?> constructor) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
WithSpanAspect aspect = newWithSpanAspect(testing.getOpenTelemetry(), parameterNameDiscoverer);
|
||||
factory.addAspect(aspect);
|
||||
|
||||
withSpanTester = factory.getProxy();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("when method is annotated with @WithSpan should wrap method execution in a Span")
|
||||
void withSpanWithDefaults() {
|
||||
// when
|
||||
testing.runWithSpan("parent", withSpanTester::testWithSpan);
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testWithSpan")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
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() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testWithSpanWithValue());
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName("greatestSpanEver")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
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() {
|
||||
assertThatThrownBy(() -> withSpanTester.testWithSpanWithException())
|
||||
.isInstanceOf(Exception.class);
|
||||
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testWithSpanWithException")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasAttributesSatisfyingExactly(
|
||||
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() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testWithClientSpan());
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testWithClientSpan")
|
||||
.hasKind(CLIENT)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testWithClientSpan"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void withSpanAttributes() {
|
||||
// when
|
||||
testing.runWithSpan(
|
||||
"parent", () -> withSpanTester.withSpanAttributes("foo", "bar", "baz", null, "fizz"));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".withSpanAttributes")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "withSpanAttributes"),
|
||||
equalTo(stringKey("discoveredName"), "foo"),
|
||||
equalTo(stringKey("implicitName"), "bar"),
|
||||
equalTo(stringKey("explicitName"), "baz"))));
|
||||
}
|
||||
|
||||
static class InstrumentationWithSpanTester {
|
||||
@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,
|
||||
|
@ -76,4 +230,255 @@ class InstrumentationWithSpanAspectTest extends AbstractWithSpanAspectTest {
|
|||
return "hello!";
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("with a method annotated with @WithSpan returns CompletionStage")
|
||||
class WithCompletionStage {
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on complete")
|
||||
void onComplete() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace
|
||||
.hasSize(1)
|
||||
.hasSpansSatisfyingExactly(span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.complete("DONE");
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on completeException AND should record the exception")
|
||||
void onCompleteExceptionally() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace
|
||||
.hasSize(1)
|
||||
.hasSpansSatisfyingExactly(span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on incompatible return value")
|
||||
void onIncompatibleReturnValue() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(unproxiedTesterSimpleClassName + ".testAsyncCompletionStage")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletionStage"))));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DisplayName("with a method annotated with @WithSpan returns CompletableFuture")
|
||||
class WithCompletableFuture {
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on complete")
|
||||
void onComplete() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace
|
||||
.hasSize(1)
|
||||
.hasSpansSatisfyingExactly(span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.complete("DONE");
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on completeException AND should record the exception")
|
||||
void onCompleteExceptionally() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
assertThat(testing.waitForTraces(1))
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace
|
||||
.hasSize(1)
|
||||
.hasSpansSatisfyingExactly(span -> span.hasName("parent").hasKind(INTERNAL)));
|
||||
|
||||
// when
|
||||
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end the Span when already complete")
|
||||
void onCompletedFuture() {
|
||||
CompletableFuture<String> future = CompletableFuture.completedFuture("Done");
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end the Span when already failed")
|
||||
void onFailedFuture() {
|
||||
CompletableFuture<String> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
|
||||
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("should end Span on incompatible return value")
|
||||
void onIncompatibleReturnValue() {
|
||||
// when
|
||||
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null));
|
||||
|
||||
// then
|
||||
List<List<SpanData>> traces = testing.waitForTraces(1);
|
||||
assertThat(traces)
|
||||
.hasTracesSatisfyingExactly(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
parentSpan -> parentSpan.hasName("parent").hasKind(INTERNAL),
|
||||
span ->
|
||||
span.hasName(
|
||||
unproxiedTesterSimpleClassName + ".testAsyncCompletableFuture")
|
||||
.hasKind(INTERNAL)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(CODE_NAMESPACE, unproxiedTesterClassName),
|
||||
equalTo(CODE_FUNCTION, "testAsyncCompletableFuture"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.autoconfigure.instrumentation.annotations;
|
||||
|
||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
|
||||
@SuppressWarnings("deprecation") // instrumenting deprecated class for backwards compatibility
|
||||
class SdkExtensionWithSpanAspectTest extends AbstractWithSpanAspectTest {
|
||||
|
||||
@Override
|
||||
WithSpanTester newWithSpanTester() {
|
||||
return new SdkExtensionWithSpanTester();
|
||||
}
|
||||
|
||||
@Override
|
||||
WithSpanAspect newWithSpanAspect(
|
||||
OpenTelemetry openTelemetry, ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
return new SdkExtensionWithSpanAspect(openTelemetry, parameterNameDiscoverer);
|
||||
}
|
||||
|
||||
static class SdkExtensionWithSpanTester implements WithSpanTester {
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan
|
||||
public String testWithSpan() {
|
||||
return "Span with name testWithSpan was created";
|
||||
}
|
||||
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan("greatestSpanEver")
|
||||
public String testWithSpanWithValue() {
|
||||
return "Span with name greatestSpanEver was created";
|
||||
}
|
||||
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan
|
||||
public String testWithSpanWithException() throws Exception {
|
||||
throw new Exception("Test @WithSpan With Exception");
|
||||
}
|
||||
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan(kind = CLIENT)
|
||||
public String testWithClientSpan() {
|
||||
return "Span with name testWithClientSpan and SpanKind.CLIENT was created";
|
||||
}
|
||||
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan
|
||||
public CompletionStage<String> testAsyncCompletionStage(CompletionStage<String> stage) {
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan
|
||||
public CompletableFuture<String> testAsyncCompletableFuture(CompletableFuture<String> stage) {
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
@io.opentelemetry.extension.annotations.WithSpan
|
||||
public String withSpanAttributes(
|
||||
@io.opentelemetry.extension.annotations.SpanAttribute String discoveredName,
|
||||
@io.opentelemetry.extension.annotations.SpanAttribute String implicitName,
|
||||
@io.opentelemetry.extension.annotations.SpanAttribute("explicitName") String parameter,
|
||||
@io.opentelemetry.extension.annotations.SpanAttribute("nullAttribute") String nullAttribute,
|
||||
String notTraced) {
|
||||
|
||||
return "hello!";
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue