Merge Java TraceUtils into extension and switch to Instrumenter API. (#3532)

* Merge Java TraceUtils into extension and switch to Instrumenter API.

* Woah
This commit is contained in:
Anuraag Agrawal 2021-07-10 13:21:12 +09:00 committed by GitHub
parent 9be9e40556
commit f601863d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 155 additions and 163 deletions

View File

@ -96,11 +96,11 @@ afterEvaluate {
// If agent depends on some shared instrumentation module that is not a testing module, it will
// be packaged into the testing jar so we need to make sure to exclude from the test classpath.
val libPath = it.absolutePath
val instrumentationPath = file("${rootDir}/instrumentation/").absolutePath
if (libPath.startsWith(instrumentationPath) &&
libPath.endsWith(".jar") &&
!libPath.substring(instrumentationPath.length).contains("testing")) {
val lib = it.absoluteFile
val instrumentationDir = file("${rootDir}/instrumentation/").absoluteFile
if (lib.startsWith(instrumentationDir) &&
lib.extension == "jar" &&
!lib.absolutePath.substring(instrumentationDir.absolutePath.length).contains("testing")) {
return@filter false
}
return@filter true

View File

@ -3,7 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withClientSpan;
import static org.assertj.core.api.Assertions.assertThat;
import com.amazonaws.DefaultRequest;
@ -12,12 +11,14 @@ import com.amazonaws.Response;
import com.amazonaws.http.HttpResponse;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.amazonaws.services.sqs.model.SendMessageResult;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.TracingRequestHandler;
import java.net.URI;
import org.apache.http.client.methods.HttpGet;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
public class TracingRequestHandlerTest {
class TracingRequestHandlerTest {
private static Response<SendMessageResult> response(Request request) {
return new Response<>(new SendMessageResult(), new HttpResponse(request, new HttpGet()));
@ -29,13 +30,16 @@ public class TracingRequestHandlerTest {
return request;
}
@RegisterExtension
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Test
public void shouldNotSetScopeAndNotFailIfClientSpanAlreadyPresent() {
void shouldNotSetScopeAndNotFailIfClientSpanAlreadyPresent() {
// given
TracingRequestHandler underTest = new TracingRequestHandler();
Request<SendMessageRequest> request = request();
withClientSpan(
testing.runWithClientSpan(
"test",
() -> {
// when
@ -47,7 +51,7 @@ public class TracingRequestHandlerTest {
}
@Test
public void shouldSetScopeIfClientSpanNotPresent() {
void shouldSetScopeIfClientSpanNotPresent() {
// given
TracingRequestHandler underTest = new TracingRequestHandler();
Request<SendMessageRequest> request = request();

View File

@ -8,9 +8,6 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.aspects;
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
import static io.opentelemetry.api.trace.SpanKind.SERVER;
import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withClientSpan;
import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withServerSpan;
import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withSpan;
import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ -34,8 +31,7 @@ import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
/** Spring AOP Test for {@link WithSpanAspect}. */
public class WithSpanAspectTest {
@RegisterExtension
static final LibraryInstrumentationExtension instrumentation =
LibraryInstrumentationExtension.create();
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
static class WithSpanTester {
@WithSpan
@ -79,7 +75,7 @@ public class WithSpanAspectTest {
@BeforeEach
void setup() {
AspectJProxyFactory factory = new AspectJProxyFactory(new WithSpanTester());
WithSpanAspect aspect = new WithSpanAspect(instrumentation.getOpenTelemetry());
WithSpanAspect aspect = new WithSpanAspect(testing.getOpenTelemetry());
factory.addAspect(aspect);
withSpanTester = factory.getProxy();
@ -94,10 +90,10 @@ public class WithSpanAspectTest {
@DisplayName("when method is annotated with @WithSpan should wrap method execution in a Span")
void withSpanWithDefaults() throws Throwable {
// when
withSpan("parent", () -> withSpanTester.testWithSpan());
testing.runWithSpan("parent", withSpanTester::testWithSpan);
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -116,10 +112,10 @@ public class WithSpanAspectTest {
"when @WithSpan value is set should wrap method execution in a Span with custom name")
void withSpanName() throws Throwable {
// when
withSpan("parent", () -> withSpanTester.testWithSpanWithValue());
testing.runWithSpan("parent", () -> withSpanTester.testWithSpanWithValue());
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -138,7 +134,7 @@ public class WithSpanAspectTest {
assertThatThrownBy(() -> withSpanTester.testWithSpanWithException())
.isInstanceOf(Exception.class);
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -154,10 +150,10 @@ public class WithSpanAspectTest {
"when method is annotated with @WithSpan(kind=CLIENT) should build span with the declared SpanKind")
void withSpanKind() throws Throwable {
// when
withSpan("parent", () -> withSpanTester.testWithClientSpan());
testing.runWithSpan("parent", () -> withSpanTester.testWithClientSpan());
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -174,10 +170,10 @@ public class WithSpanAspectTest {
"when method is annotated with @WithSpan(kind=CLIENT) and context already contains a CLIENT span should suppress span")
void suppressClientSpan() throws Throwable {
// when
withClientSpan("parent", () -> withSpanTester.testWithClientSpan());
testing.runWithClientSpan("parent", withSpanTester::testWithClientSpan);
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -190,10 +186,10 @@ public class WithSpanAspectTest {
"when method is annotated with @WithSpan(kind=SERVER) and context already contains a SERVER span should suppress span")
void suppressServerSpan() throws Throwable {
// when
withServerSpan("parent", () -> withSpanTester.testWithServerSpan());
testing.runWithServerSpan("parent", withSpanTester::testWithServerSpan);
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -211,10 +207,10 @@ public class WithSpanAspectTest {
CompletableFuture<String> future = new CompletableFuture<>();
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
// then
assertThat(instrumentation.waitForTraces(1))
assertThat(testing.waitForTraces(1))
.hasTracesSatisfyingExactly(
trace ->
trace
@ -225,7 +221,7 @@ public class WithSpanAspectTest {
future.complete("DONE");
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -243,10 +239,10 @@ public class WithSpanAspectTest {
CompletableFuture<String> future = new CompletableFuture<>();
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(future));
// then
assertThat(instrumentation.waitForTraces(1))
assertThat(testing.waitForTraces(1))
.hasTracesSatisfyingExactly(
trace ->
trace
@ -257,7 +253,7 @@ public class WithSpanAspectTest {
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -274,10 +270,10 @@ public class WithSpanAspectTest {
@DisplayName("should end Span on incompatible return value")
void onIncompatibleReturnValue() throws Throwable {
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletionStage(null));
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -300,10 +296,10 @@ public class WithSpanAspectTest {
CompletableFuture<String> future = new CompletableFuture<>();
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
// then
assertThat(instrumentation.waitForTraces(1))
assertThat(testing.waitForTraces(1))
.hasTracesSatisfyingExactly(
trace ->
trace
@ -314,7 +310,7 @@ public class WithSpanAspectTest {
future.complete("DONE");
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -332,10 +328,10 @@ public class WithSpanAspectTest {
CompletableFuture<String> future = new CompletableFuture<>();
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
// then
assertThat(instrumentation.waitForTraces(1))
assertThat(testing.waitForTraces(1))
.hasTracesSatisfyingExactly(
trace ->
trace
@ -346,7 +342,7 @@ public class WithSpanAspectTest {
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -365,10 +361,10 @@ public class WithSpanAspectTest {
CompletableFuture<String> future = CompletableFuture.completedFuture("Done");
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -387,10 +383,10 @@ public class WithSpanAspectTest {
future.completeExceptionally(new Exception("Test @WithSpan With completeExceptionally"));
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(future));
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->
@ -407,10 +403,10 @@ public class WithSpanAspectTest {
@DisplayName("should end Span on incompatible return value")
void onIncompatibleReturnValue() throws Throwable {
// when
withSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null));
testing.runWithSpan("parent", () -> withSpanTester.testAsyncCompletableFuture(null));
// then
List<List<SpanData>> traces = instrumentation.waitForTraces(1);
List<List<SpanData>> traces = testing.waitForTraces(1);
assertThat(traces)
.hasTracesSatisfyingExactly(
trace ->

View File

@ -5,7 +5,6 @@
package io.opentelemetry.instrumentation.spring.httpclients;
import static io.opentelemetry.instrumentation.testing.util.TraceUtils.withClientSpan;
import static io.opentelemetry.sdk.testing.assertj.TracesAssert.assertThat;
import static org.mockito.BDDMockito.then;
@ -22,8 +21,7 @@ import org.springframework.http.client.ClientHttpRequestExecution;
@ExtendWith(MockitoExtension.class)
class RestTemplateInterceptorTest {
@RegisterExtension
static final LibraryInstrumentationExtension instrumentation =
LibraryInstrumentationExtension.create();
static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
@Mock HttpRequest httpRequestMock;
@Mock ClientHttpRequestExecution requestExecutionMock;
@ -32,11 +30,10 @@ class RestTemplateInterceptorTest {
@Test
void shouldSkipWhenContextHasClientSpan() throws Exception {
// given
RestTemplateInterceptor interceptor =
new RestTemplateInterceptor(instrumentation.getOpenTelemetry());
RestTemplateInterceptor interceptor = new RestTemplateInterceptor(testing.getOpenTelemetry());
// when
withClientSpan(
testing.runWithClientSpan(
"parent",
() -> {
interceptor.intercept(httpRequestMock, requestBody, requestExecutionMock);
@ -45,7 +42,7 @@ class RestTemplateInterceptorTest {
// then
then(requestExecutionMock).should().execute(httpRequestMock, requestBody);
assertThat(instrumentation.waitForTraces(1))
assertThat(testing.waitForTraces(1))
.hasTracesSatisfyingExactly(
trace ->
trace.hasSpansSatisfyingExactly(

View File

@ -6,9 +6,15 @@
package io.opentelemetry.instrumentation.testing.junit;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner;
import io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil;
import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable;
import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.trace.data.SpanData;
import java.util.List;
@ -26,8 +32,21 @@ public abstract class InstrumentationExtension
private final InstrumentationTestRunner testRunner;
private final Instrumenter<String, Void> testInstrumenter;
private final Instrumenter<String, Void> testClientInstrumenter;
private final Instrumenter<String, Void> testServerInstrumenter;
protected InstrumentationExtension(InstrumentationTestRunner testRunner) {
this.testRunner = testRunner;
testInstrumenter =
Instrumenter.<String, Void>newBuilder(testRunner.getOpenTelemetry(), "test", name -> name)
.newInstrumenter(SpanKindExtractor.alwaysInternal());
testClientInstrumenter =
Instrumenter.<String, Void>newBuilder(testRunner.getOpenTelemetry(), "test", name -> name)
.newInstrumenter(SpanKindExtractor.alwaysClient());
testServerInstrumenter =
Instrumenter.<String, Void>newBuilder(testRunner.getOpenTelemetry(), "test", name -> name)
.newInstrumenter(SpanKindExtractor.alwaysServer());
}
@Override
@ -106,4 +125,88 @@ public abstract class InstrumentationExtension
throws TimeoutException, InterruptedException {
return TelemetryDataUtil.waitForTraces(this::spans, numberOfTraces, timeout, unit);
}
/**
* Runs the provided {@code callback} inside the scope of an INTERNAL span with name {@code
* spanName}.
*/
public <E extends Exception> void runWithSpan(String spanName, ThrowingRunnable<E> callback)
throws E {
runWithSpan(
spanName,
() -> {
callback.run();
return null;
});
}
/**
* Runs the provided {@code callback} inside the scope of an INTERNAL span with name {@code
* spanName}.
*/
public <T, E extends Throwable> T runWithSpan(String spanName, ThrowingSupplier<T, E> callback)
throws E {
return runWithInstrumenter(spanName, testInstrumenter, callback);
}
/**
* Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code
* spanName}.
*/
public <E extends Throwable> void runWithClientSpan(String spanName, ThrowingRunnable<E> callback)
throws E {
runWithClientSpan(
spanName,
() -> {
callback.run();
return null;
});
}
/**
* Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code
* spanName}.
*/
public <T, E extends Throwable> T runWithClientSpan(
String spanName, ThrowingSupplier<T, E> callback) throws E {
return runWithInstrumenter(spanName, testClientInstrumenter, callback);
}
/**
* Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code
* spanName}.
*/
public <E extends Throwable> void runWithServerSpan(String spanName, ThrowingRunnable<E> callback)
throws E {
runWithServerSpan(
spanName,
() -> {
callback.run();
return null;
});
}
/**
* Runs the provided {@code callback} inside the scope of an CLIENT span with name {@code
* spanName}.
*/
public <T, E extends Throwable> T runWithServerSpan(
String spanName, ThrowingSupplier<T, E> callback) throws E {
return runWithInstrumenter(spanName, testServerInstrumenter, callback);
}
private static <T, E extends Throwable> T runWithInstrumenter(
String spanName, Instrumenter<String, Void> instrumenter, ThrowingSupplier<T, E> callback)
throws E {
Context context = instrumenter.start(Context.current(), spanName);
Throwable err = null;
try (Scope ignored = context.makeCurrent()) {
return callback.get();
} catch (Throwable t) {
err = t;
throw t;
} finally {
instrumenter.end(context, spanName, null, err);
}
}
}

View File

@ -1,108 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.testing.util;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.tracer.BaseTracer;
/** Utility class for creating spans in tests. */
public final class TraceUtils {
private static final TestTracer TRACER = new TestTracer();
public static <E extends Exception> void withSpan(String spanName, ThrowingRunnable<E> callback)
throws E {
withSpan(
spanName,
() -> {
callback.run();
return null;
});
}
public static <T, E extends Throwable> T withSpan(
String spanName, ThrowingSupplier<T, E> callback) throws E {
Context context = TRACER.startSpan(spanName);
try (Scope ignored = context.makeCurrent()) {
T result = callback.get();
TRACER.end(context);
return result;
} catch (Throwable t) {
TRACER.endExceptionally(context, t);
throw t;
}
}
public static <E extends Throwable> void withClientSpan(
String spanName, ThrowingRunnable<E> callback) throws E {
withClientSpan(
spanName,
() -> {
callback.run();
return null;
});
}
public static <T, E extends Throwable> T withClientSpan(
String spanName, ThrowingSupplier<T, E> callback) throws E {
Context context = TRACER.startClientSpan(spanName);
try (Scope ignored = context.makeCurrent()) {
T result = callback.get();
TRACER.end(context);
return result;
} catch (Throwable t) {
TRACER.endExceptionally(context, t);
throw t;
}
}
public static <E extends Throwable> void withServerSpan(
String spanName, ThrowingRunnable<E> callback) throws E {
withServerSpan(
spanName,
() -> {
callback.run();
return null;
});
}
public static <T, E extends Throwable> T withServerSpan(
String spanName, ThrowingSupplier<T, E> callback) throws E {
Context context = TRACER.startServerSpan(spanName);
try (Scope ignored = context.makeCurrent()) {
T result = callback.get();
TRACER.end(context);
return result;
} catch (Throwable t) {
TRACER.endExceptionally(context, t);
throw t;
}
}
private static final class TestTracer extends BaseTracer {
@Override
protected String getInstrumentationName() {
return "test";
}
Context startClientSpan(String name) {
Context parentContext = Context.current();
Span span = spanBuilder(parentContext, name, SpanKind.CLIENT).startSpan();
return withClientSpan(parentContext, span);
}
Context startServerSpan(String name) {
Context parentContext = Context.current();
Span span = spanBuilder(parentContext, name, SpanKind.SERVER).startSpan();
return withServerSpan(parentContext, span);
}
}
private TraceUtils() {}
}