diff --git a/sdk-platform-java/gax-java/gax-grpc/pom.xml b/sdk-platform-java/gax-java/gax-grpc/pom.xml index c669281e31f5..c41d684b7d93 100644 --- a/sdk-platform-java/gax-java/gax-grpc/pom.xml +++ b/sdk-platform-java/gax-java/gax-grpc/pom.xml @@ -100,6 +100,16 @@ + + io.opentelemetry + opentelemetry-sdk + test + + + io.opentelemetry + opentelemetry-sdk-testing + test + io.grpc grpc-s2a diff --git a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java index e797842c9eb8..06e2b9ea06be 100644 --- a/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java +++ b/sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java @@ -90,9 +90,16 @@ public static ClientCall newCall( channel = ((ChannelPool) channel).getChannel(grpcContext.getChannelAffinity()); } - if (!grpcContext.getExtraHeaders().isEmpty()) { - ClientInterceptor interceptor = - MetadataUtils.newAttachHeadersInterceptor(grpcContext.getMetadata()); + java.util.Map traceContext = new java.util.HashMap<>(); + grpcContext.getTracer().injectTraceContext(traceContext); + + if (!grpcContext.getExtraHeaders().isEmpty() || !traceContext.isEmpty()) { + Metadata metadata = grpcContext.getMetadata(); + for (java.util.Map.Entry entry : traceContext.entrySet()) { + metadata.put( + Metadata.Key.of(entry.getKey(), Metadata.ASCII_STRING_MARSHALLER), entry.getValue()); + } + ClientInterceptor interceptor = MetadataUtils.newAttachHeadersInterceptor(metadata); channel = ClientInterceptors.intercept(channel, interceptor); } diff --git a/sdk-platform-java/gax-java/gax-httpjson/pom.xml b/sdk-platform-java/gax-java/gax-httpjson/pom.xml index 259b360ba4ee..5b94d4a1f4d5 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/pom.xml +++ b/sdk-platform-java/gax-java/gax-httpjson/pom.xml @@ -86,6 +86,16 @@ + + io.opentelemetry + opentelemetry-sdk + test + + + io.opentelemetry + opentelemetry-sdk-testing + test + com.google.api gax diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java index c98d00afb961..6a2f97401101 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java @@ -80,15 +80,25 @@ public static HttpJsonClientCall newC return httpJsonContext.getChannel().newCall(methodDescriptor, httpJsonContext.getCallOptions()); } + static HttpJsonMetadata getMetadataWithTraceContext(HttpJsonCallContext context) { + java.util.Map traceHeaders = new java.util.HashMap<>(); + context.getTracer().injectTraceContext(traceHeaders); + + java.util.Map> finalHeaders = + new java.util.HashMap<>(context.getExtraHeaders()); + for (java.util.Map.Entry entry : traceHeaders.entrySet()) { + finalHeaders.put(entry.getKey(), java.util.Collections.singletonList(entry.getValue())); + } + return HttpJsonMetadata.newBuilder().build().withHeaders(finalHeaders); + } + static ApiFuture futureUnaryCall( HttpJsonClientCall clientCall, RequestT request, HttpJsonCallContext context) { // Start the call HttpJsonFuture future = new HttpJsonFuture<>(clientCall); - clientCall.start( - new FutureListener<>(future), - HttpJsonMetadata.newBuilder().build().withHeaders(context.getExtraHeaders())); + clientCall.start(new FutureListener<>(future), getMetadataWithTraceContext(context)); // Send the request try { diff --git a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java index 249cec843ad3..a67cdc855506 100644 --- a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java +++ b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java @@ -196,6 +196,9 @@ default void requestSent() {} default void batchRequestSent(long elementCount, long requestSize) {} ; + /** Extract the trace context from the tracer and add it to the given headers map. */ + default void injectTraceContext(java.util.Map carrier) {} + /** * Annotates the attempt with the full resolved HTTP URL. Only relevant for HTTP transport. * diff --git a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracer.java b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracer.java index eae14b1d9dd1..eac96d1d7529 100644 --- a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracer.java +++ b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracer.java @@ -55,6 +55,27 @@ public class SpanTracer implements ApiTracer { private final ApiTracerContext apiTracerContext; private Span attemptSpan; + @Override + public void injectTraceContext(java.util.Map carrier) { + if (attemptSpan != null) { + try { + io.opentelemetry.context.Context context = + io.opentelemetry.context.Context.current().with(attemptSpan); + io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator.getInstance() + .inject( + context, + carrier, + (c, k, v) -> { + if (c != null) { + c.put(k, v); + } + }); + } catch (NoSuchMethodError e) { + // Silently ignore if incompatible OpenTelemetry version + } + } + } + /** * Creates a new instance of {@code SpanTracer}. *