Skip to content

Commit 9eafa4f

Browse files
committed
feat: Implement trace context extraction and injection interceptors
1 parent 39133b5 commit 9eafa4f

File tree

7 files changed

+105
-7
lines changed

7 files changed

+105
-7
lines changed

sdk-platform-java/gax-java/gax-grpc/pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
</parent>
1616

1717
<dependencies>
18+
<dependency>
19+
<groupId>io.opentelemetry</groupId>
20+
<artifactId>opentelemetry-api</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>io.opentelemetry</groupId>
24+
<artifactId>opentelemetry-context</artifactId>
25+
</dependency>
1826
<dependency>
1927
<groupId>com.google.api</groupId>
2028
<artifactId>gax</artifactId>
@@ -100,6 +108,16 @@
100108
</dependency>
101109

102110
<!-- test dependencies -->
111+
<dependency>
112+
<groupId>io.opentelemetry</groupId>
113+
<artifactId>opentelemetry-sdk</artifactId>
114+
<scope>test</scope>
115+
</dependency>
116+
<dependency>
117+
<groupId>io.opentelemetry</groupId>
118+
<artifactId>opentelemetry-sdk-testing</artifactId>
119+
<scope>test</scope>
120+
</dependency>
103121
<dependency>
104122
<groupId>io.grpc</groupId>
105123
<artifactId>grpc-s2a</artifactId>

sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcClientCalls.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,16 @@ public static <RequestT, ResponseT> ClientCall<RequestT, ResponseT> newCall(
9090
channel = ((ChannelPool) channel).getChannel(grpcContext.getChannelAffinity());
9191
}
9292

93-
if (!grpcContext.getExtraHeaders().isEmpty()) {
94-
ClientInterceptor interceptor =
95-
MetadataUtils.newAttachHeadersInterceptor(grpcContext.getMetadata());
93+
java.util.Map<String, String> traceContext = new java.util.HashMap<>();
94+
grpcContext.getTracer().injectTraceContext(traceContext);
95+
96+
if (!grpcContext.getExtraHeaders().isEmpty() || !traceContext.isEmpty()) {
97+
Metadata metadata = grpcContext.getMetadata();
98+
for (java.util.Map.Entry<String, String> entry : traceContext.entrySet()) {
99+
metadata.put(
100+
Metadata.Key.of(entry.getKey(), Metadata.ASCII_STRING_MARSHALLER), entry.getValue());
101+
}
102+
ClientInterceptor interceptor = MetadataUtils.newAttachHeadersInterceptor(metadata);
96103
channel = ClientInterceptors.intercept(channel, interceptor);
97104
}
98105

sdk-platform-java/gax-java/gax-httpjson/pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
</properties>
2121

2222
<dependencies>
23+
<dependency>
24+
<groupId>io.opentelemetry</groupId>
25+
<artifactId>opentelemetry-api</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>io.opentelemetry</groupId>
29+
<artifactId>opentelemetry-context</artifactId>
30+
</dependency>
2331
<dependency>
2432
<groupId>com.google.api</groupId>
2533
<artifactId>gax</artifactId>
@@ -86,6 +94,16 @@
8694
</dependency>
8795

8896
<!-- test dependencies -->
97+
<dependency>
98+
<groupId>io.opentelemetry</groupId>
99+
<artifactId>opentelemetry-sdk</artifactId>
100+
<scope>test</scope>
101+
</dependency>
102+
<dependency>
103+
<groupId>io.opentelemetry</groupId>
104+
<artifactId>opentelemetry-sdk-testing</artifactId>
105+
<scope>test</scope>
106+
</dependency>
89107
<dependency>
90108
<groupId>com.google.api</groupId>
91109
<artifactId>gax</artifactId>

sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonClientCalls.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,25 @@ public static <RequestT, ResponseT> HttpJsonClientCall<RequestT, ResponseT> newC
8080
return httpJsonContext.getChannel().newCall(methodDescriptor, httpJsonContext.getCallOptions());
8181
}
8282

83+
static HttpJsonMetadata getMetadataWithTraceContext(HttpJsonCallContext context) {
84+
java.util.Map<String, String> traceHeaders = new java.util.HashMap<>();
85+
context.getTracer().injectTraceContext(traceHeaders);
86+
87+
java.util.Map<String, java.util.List<String>> finalHeaders =
88+
new java.util.HashMap<>(context.getExtraHeaders());
89+
for (java.util.Map.Entry<String, String> entry : traceHeaders.entrySet()) {
90+
finalHeaders.put(entry.getKey(), java.util.Collections.singletonList(entry.getValue()));
91+
}
92+
return HttpJsonMetadata.newBuilder().build().withHeaders(finalHeaders);
93+
}
94+
8395
static <RequestT, ResponseT> ApiFuture<ResponseT> futureUnaryCall(
8496
HttpJsonClientCall<RequestT, ResponseT> clientCall,
8597
RequestT request,
8698
HttpJsonCallContext context) {
8799
// Start the call
88100
HttpJsonFuture<ResponseT> future = new HttpJsonFuture<>(clientCall);
89-
clientCall.start(
90-
new FutureListener<>(future),
91-
HttpJsonMetadata.newBuilder().build().withHeaders(context.getExtraHeaders()));
101+
clientCall.start(new FutureListener<>(future), getMetadataWithTraceContext(context));
92102

93103
// Send the request
94104
try {

sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonDirectStreamController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,12 @@ public void request(int count) {
8282
}
8383

8484
void start(RequestT request, ApiCallContext context) {
85+
HttpJsonCallContext httpJsonContext = HttpJsonCallContext.createDefault().nullToSelf(context);
8586
responseObserver.onStart(this);
8687
this.hasStarted = true;
8788
clientCall.start(
8889
new ResponseObserverAdapter(),
89-
HttpJsonMetadata.newBuilder().build().withHeaders(context.getExtraHeaders()));
90+
HttpJsonClientCalls.getMetadataWithTraceContext(httpJsonContext));
9091

9192
if (autoflowControl) {
9293
clientCall.request(1);

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ default void requestSent() {}
192192
default void batchRequestSent(long elementCount, long requestSize) {}
193193
;
194194

195+
/** Extract the trace context from the tracer and add it to the given headers map. */
196+
default void injectTraceContext(java.util.Map<String, String> carrier) {}
197+
195198
/**
196199
* A context class to be used with {@link #inScope()} and a try-with-resources block. Closing a
197200
* {@link Scope} removes any context that the underlying implementation might've set in {@link

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/tracing/SpanTracer.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,47 @@ public class SpanTracer implements ApiTracer {
5252
private final String attemptSpanName;
5353
private final ApiTracerContext apiTracerContext;
5454
private Span attemptSpan;
55+
private static volatile Boolean isOpentelemetryAvailable;
56+
57+
private static boolean isOpenTelemetryAvailable() {
58+
if (isOpentelemetryAvailable == null) {
59+
synchronized (SpanTracer.class) {
60+
if (isOpentelemetryAvailable == null) {
61+
try {
62+
Class.forName("io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator");
63+
isOpentelemetryAvailable = true;
64+
} catch (ClassNotFoundException e) {
65+
isOpentelemetryAvailable = false;
66+
}
67+
}
68+
}
69+
}
70+
return isOpentelemetryAvailable;
71+
}
72+
73+
@Override
74+
public void injectTraceContext(java.util.Map<String, String> carrier) {
75+
if (!isOpenTelemetryAvailable()) {
76+
return;
77+
}
78+
if (attemptSpan != null) {
79+
try {
80+
io.opentelemetry.context.Context context =
81+
io.opentelemetry.context.Context.current().with(attemptSpan);
82+
io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator.getInstance()
83+
.inject(
84+
context,
85+
carrier,
86+
(c, k, v) -> {
87+
if (c != null) {
88+
c.put(k, v);
89+
}
90+
});
91+
} catch (NoSuchMethodError e) {
92+
// Silently ignore if incompatible OpenTelemetry version
93+
}
94+
}
95+
}
5596

5697
/**
5798
* Creates a new instance of {@code SpanTracer}.

0 commit comments

Comments
 (0)