243 lines
8.7 KiB
Java
243 lines
8.7 KiB
Java
/*
|
|
* Copyright The OpenTelemetry Authors
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package io.opentelemetry;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
|
|
import com.linecorp.armeria.client.ClientRequestContext;
|
|
import com.linecorp.armeria.client.WebClient;
|
|
import com.linecorp.armeria.common.HttpRequest;
|
|
import com.linecorp.armeria.common.HttpResponse;
|
|
import com.linecorp.armeria.server.HttpService;
|
|
import com.linecorp.armeria.server.ServerBuilder;
|
|
import com.linecorp.armeria.server.ServiceRequestContext;
|
|
import com.linecorp.armeria.server.logging.LoggingService;
|
|
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
|
|
import io.netty.util.AsciiString;
|
|
import io.opentelemetry.api.OpenTelemetry;
|
|
import io.opentelemetry.api.trace.Span;
|
|
import io.opentelemetry.context.Context;
|
|
import io.opentelemetry.context.Scope;
|
|
import io.opentelemetry.context.propagation.ContextPropagators;
|
|
import io.opentelemetry.context.propagation.TextMapGetter;
|
|
import io.opentelemetry.context.propagation.TextMapPropagator;
|
|
import io.opentelemetry.extension.trace.propagation.B3Propagator;
|
|
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
|
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
|
|
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
|
import io.opentelemetry.sdk.trace.data.SpanData;
|
|
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.util.List;
|
|
import java.util.function.Supplier;
|
|
import java.util.stream.Stream;
|
|
import javax.annotation.Nullable;
|
|
import org.junit.jupiter.api.AfterEach;
|
|
import org.junit.jupiter.api.BeforeAll;
|
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
|
import org.junit.jupiter.params.ParameterizedTest;
|
|
import org.junit.jupiter.params.provider.Arguments;
|
|
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
|
|
|
/** Integration tests for the B3 propagators, in various configurations. */
|
|
class B3PropagationIntegrationTest {
|
|
|
|
private static final InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
|
|
|
|
static WebClient b3MultiClient;
|
|
static WebClient b3SingleClient;
|
|
|
|
private static class FrontendService implements HttpService {
|
|
private final OpenTelemetry openTelemetry;
|
|
private final Supplier<WebClient> client;
|
|
|
|
FrontendService(OpenTelemetry openTelemetry, Supplier<WebClient> client) {
|
|
this.openTelemetry = openTelemetry;
|
|
this.client = client;
|
|
}
|
|
|
|
@Override
|
|
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) {
|
|
Context incomingContext = extract(req, openTelemetry);
|
|
Span span =
|
|
openTelemetry
|
|
.getTracer("server1Tracer")
|
|
.spanBuilder("server1Span")
|
|
.setParent(incomingContext)
|
|
.startSpan();
|
|
try (Scope ignored = span.makeCurrent()) {
|
|
return HttpResponse.of(
|
|
client
|
|
.get()
|
|
.get("/backend")
|
|
.aggregate()
|
|
.thenApply(
|
|
unused -> {
|
|
span.end();
|
|
return HttpResponse.of("OK");
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class BackendService implements HttpService {
|
|
private final OpenTelemetry openTelemetry;
|
|
|
|
BackendService(OpenTelemetry openTelemetry) {
|
|
this.openTelemetry = openTelemetry;
|
|
}
|
|
|
|
@Override
|
|
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
|
|
Context incomingContext = extract(req, openTelemetry);
|
|
|
|
Span span =
|
|
openTelemetry
|
|
.getTracer("server2Tracer")
|
|
.spanBuilder("server2Span")
|
|
.setParent(incomingContext)
|
|
.startSpan();
|
|
try (Scope ignored = span.makeCurrent()) {
|
|
return HttpResponse.of("OK");
|
|
} finally {
|
|
span.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
@RegisterExtension
|
|
static final ServerExtension server =
|
|
new ServerExtension() {
|
|
@Override
|
|
protected void configure(ServerBuilder sb) {
|
|
SdkTracerProvider tracerProvider =
|
|
SdkTracerProvider.builder()
|
|
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
|
|
.build();
|
|
OpenTelemetry b3MultiOpenTelemetry =
|
|
OpenTelemetrySdk.builder()
|
|
.setTracerProvider(tracerProvider)
|
|
.setPropagators(ContextPropagators.create(B3Propagator.injectingMultiHeaders()))
|
|
.build();
|
|
OpenTelemetry b3SingleOpenTelemetry =
|
|
OpenTelemetrySdk.builder()
|
|
.setTracerProvider(tracerProvider)
|
|
.setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader()))
|
|
.build();
|
|
sb.service(
|
|
"/multi/frontend", new FrontendService(b3MultiOpenTelemetry, () -> b3MultiClient));
|
|
sb.service("/multi/backend", new BackendService(b3MultiOpenTelemetry));
|
|
sb.service(
|
|
"/single/frontend", new FrontendService(b3SingleOpenTelemetry, () -> b3SingleClient));
|
|
sb.service("/single/backend", new BackendService(b3SingleOpenTelemetry));
|
|
|
|
sb.decorator(LoggingService.newDecorator());
|
|
}
|
|
};
|
|
|
|
@BeforeAll
|
|
static void setup() {
|
|
b3MultiClient =
|
|
createPropagatingClient(
|
|
server.httpUri().resolve("/multi"), B3Propagator.injectingMultiHeaders());
|
|
b3SingleClient =
|
|
createPropagatingClient(
|
|
server.httpUri().resolve("/single"), B3Propagator.injectingSingleHeader());
|
|
}
|
|
|
|
@AfterEach
|
|
void shutdown() {
|
|
spanExporter.reset();
|
|
}
|
|
|
|
@ParameterizedTest
|
|
@ArgumentsSource(WebClientArgumentSupplier.class)
|
|
void propagation(String testType, WebClient client) throws IOException {
|
|
OpenTelemetrySdk clientSdk = setupClient();
|
|
|
|
Span span = clientSdk.getTracer("testTracer").spanBuilder("clientSpan").startSpan();
|
|
try (Scope ignored = span.makeCurrent()) {
|
|
assertThat(client.get("/frontend").aggregate().join().contentUtf8()).isEqualTo("OK");
|
|
} finally {
|
|
span.end();
|
|
}
|
|
|
|
List<SpanData> finishedSpanItems = spanExporter.getFinishedSpanItems();
|
|
// 3 spans, one from the client, and one from each of the servers.
|
|
assertThat(finishedSpanItems).hasSize(3);
|
|
String traceId = finishedSpanItems.get(0).getTraceId();
|
|
|
|
assertThat(finishedSpanItems)
|
|
.allSatisfy(spanData -> assertThat(spanData.getTraceId()).isEqualTo(traceId));
|
|
}
|
|
|
|
@ParameterizedTest
|
|
@ArgumentsSource(WebClientArgumentSupplier.class)
|
|
void noClientTracing(String testType, WebClient client) throws IOException {
|
|
assertThat(client.get("/frontend").aggregate().join().contentUtf8()).isEqualTo("OK");
|
|
|
|
List<SpanData> finishedSpanItems = spanExporter.getFinishedSpanItems();
|
|
// 2 spans, one from each of the servers
|
|
assertThat(finishedSpanItems).hasSize(2);
|
|
String traceId = finishedSpanItems.get(0).getTraceId();
|
|
|
|
assertThat(finishedSpanItems)
|
|
.allSatisfy(spanData -> assertThat(spanData.getTraceId()).isEqualTo(traceId));
|
|
}
|
|
|
|
private static WebClient createPropagatingClient(URI uri, TextMapPropagator propagator) {
|
|
return WebClient.builder(uri)
|
|
.decorator(
|
|
(delegate, ctx, req) -> {
|
|
propagator.inject(
|
|
Context.current(), ctx, ClientRequestContext::addAdditionalRequestHeader);
|
|
return delegate.execute(ctx, req);
|
|
})
|
|
.build();
|
|
}
|
|
|
|
private static OpenTelemetrySdk setupClient() {
|
|
return OpenTelemetrySdk.builder()
|
|
.setTracerProvider(
|
|
SdkTracerProvider.builder()
|
|
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
|
|
.build())
|
|
.build();
|
|
}
|
|
|
|
private static Context extract(HttpRequest request, OpenTelemetry sdk) {
|
|
return sdk.getPropagators()
|
|
.getTextMapPropagator()
|
|
.extract(
|
|
Context.root(),
|
|
request,
|
|
new TextMapGetter<HttpRequest>() {
|
|
@Override
|
|
public Iterable<String> keys(HttpRequest carrier) {
|
|
return () ->
|
|
carrier.headers().names().stream().map(AsciiString::toString).iterator();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public String get(@Nullable HttpRequest carrier, String key) {
|
|
return carrier.headers().get(key);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static class WebClientArgumentSupplier implements ArgumentsProvider {
|
|
@Override
|
|
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
|
return Stream.of(
|
|
Arguments.of("b3multi", b3MultiClient), Arguments.of("b3single", b3SingleClient));
|
|
}
|
|
}
|
|
}
|