Skip to content

Commit 73e07f0

Browse files
committed
Add OpenTelemetry tracing POC to regular interop client and server
1 parent a992bdf commit 73e07f0

8 files changed

Lines changed: 282 additions & 52 deletions

File tree

gcp-csm-observability/src/main/java/io/grpc/gcp/csm/observability/CsmObservability.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public void configureServerBuilder(ServerBuilder<?> serverBuilder) {
7474
}
7575

7676
@VisibleForTesting
77-
void configureChannelBuilder(ManagedChannelBuilder<?> builder) {
77+
public void configureChannelBuilder(ManagedChannelBuilder<?> builder) {
7878
delegate.configureChannelBuilder(builder);
7979
}
8080

@@ -115,6 +115,14 @@ public Builder sdk(OpenTelemetry sdk) {
115115
return this;
116116
}
117117

118+
/**
119+
* Enables or disables tracing.
120+
*/
121+
public Builder enableTracing(boolean enable) {
122+
InternalGrpcOpenTelemetry.enableTracing(delegate, enable);
123+
return this;
124+
}
125+
118126
/**
119127
* Adds optionalLabelKey to all the metrics that can provide value for the
120128
* optionalLabelKey.

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ opencensus-exporter-trace-stackdriver = { module = "io.opencensus:opencensus-exp
123123
opencensus-impl = { module = "io.opencensus:opencensus-impl", version.ref = "opencensus" }
124124
opentelemetry-api = "io.opentelemetry:opentelemetry-api:1.60.1"
125125
opentelemetry-exporter-prometheus = "io.opentelemetry:opentelemetry-exporter-prometheus:1.60.1-alpha"
126+
opentelemetry-exporter-otlp = "io.opentelemetry:opentelemetry-exporter-otlp:1.60.1"
126127
opentelemetry-gcp-resources = "io.opentelemetry.contrib:opentelemetry-gcp-resources:1.54.0-alpha"
127128
opentelemetry-sdk-extension-autoconfigure = "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.60.1"
128129
opentelemetry-sdk-testing = "io.opentelemetry:opentelemetry-sdk-testing:1.60.1"

interop-testing/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies {
4242
libraries.netty.tcnative,
4343
libraries.netty.tcnative.classes,
4444
libraries.opentelemetry.exporter.prometheus, // For xds interop client
45+
libraries.opentelemetry.exporter.otlp,
4546
project(':grpc-googleapis'),
4647
project(':grpc-grpclb'),
4748
project(':grpc-rls')

interop-testing/src/main/java/io/grpc/testing/integration/TestServiceClient.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import java.util.concurrent.TimeUnit;
8080
import java.util.concurrent.atomic.AtomicReference;
8181
import javax.annotation.Nullable;
82+
import io.grpc.gcp.csm.observability.CsmObservability;
8283

8384
/**
8485
* Application that starts a client for the {@link TestServiceGrpc.TestServiceImplBase} and runs
@@ -99,6 +100,13 @@ public class TestServiceClient {
99100
public static void main(String[] args) throws Exception {
100101
final TestServiceClient client = new TestServiceClient();
101102
client.parseArgs(args);
103+
if (client.enableOpentelemetryTracing) {
104+
io.opentelemetry.api.OpenTelemetry otel = io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
105+
io.grpc.opentelemetry.GrpcOpenTelemetry gotel = io.grpc.opentelemetry.GrpcOpenTelemetry.newBuilder()
106+
.sdk(otel)
107+
.build();
108+
gotel.registerGlobal();
109+
}
102110
customBackendMetricsLoadBalancerProvider = new CustomBackendMetricsLoadBalancerProvider();
103111
LoadBalancerRegistry.getDefaultRegistry().register(customBackendMetricsLoadBalancerProvider);
104112
client.setUp();
@@ -107,6 +115,10 @@ public static void main(String[] args) throws Exception {
107115
client.run();
108116
} finally {
109117
client.tearDown();
118+
if (client.enableOpentelemetryTracing) {
119+
System.out.println("Sleeping to flush spans...");
120+
Thread.sleep(2000);
121+
}
110122
}
111123
}
112124

@@ -136,6 +148,7 @@ public static void main(String[] args) throws Exception {
136148
private int soakResponseSize = 314159;
137149
private int numThreads = 1;
138150
private String additionalMetadata = "";
151+
private boolean enableOpentelemetryTracing = false;
139152
private static LoadBalancerProvider customBackendMetricsLoadBalancerProvider;
140153

141154
private Tester tester = new Tester();
@@ -167,6 +180,8 @@ void parseArgs(String[] args) throws Exception {
167180
serverHostOverride = value;
168181
} else if ("server_port".equals(key)) {
169182
serverPort = Integer.parseInt(value);
183+
} else if ("enable_opentelemetry_tracing".equals(key)) {
184+
enableOpentelemetryTracing = Boolean.parseBoolean(value);
170185
} else if ("test_case".equals(key)) {
171186
testCase = value;
172187
} else if ("num_times".equals(key)) {
@@ -599,6 +614,9 @@ private class Tester extends AbstractInteropTest {
599614
@Override
600615
protected ManagedChannelBuilder<?> createChannelBuilder() {
601616
boolean useGeneric = false;
617+
if (enableOpentelemetryTracing) {
618+
useGeneric = true;
619+
}
602620
ChannelCredentials channelCredentials;
603621
if (customCredentialsType != null) {
604622
useGeneric = true; // Retain old behavior; avoids erroring if incompatible

interop-testing/src/main/java/io/grpc/testing/integration/TestServiceServer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package io.grpc.testing.integration;
1818

1919
import com.google.common.annotations.VisibleForTesting;
20+
import io.grpc.gcp.csm.observability.CsmObservability;
2021
import com.google.common.util.concurrent.MoreExecutors;
2122
import io.grpc.BindableService;
2223
import io.grpc.Grpc;
@@ -46,6 +47,13 @@ public class TestServiceServer {
4647
public static void main(String[] args) throws Exception {
4748
final TestServiceServer server = new TestServiceServer();
4849
server.parseArgs(args);
50+
if (server.enableOpentelemetryTracing) {
51+
io.opentelemetry.api.OpenTelemetry otel = io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
52+
io.grpc.opentelemetry.GrpcOpenTelemetry gotel = io.grpc.opentelemetry.GrpcOpenTelemetry.newBuilder()
53+
.sdk(otel)
54+
.build();
55+
gotel.registerGlobal();
56+
}
4957
if (server.useTls) {
5058
System.out.println(
5159
"\nUsing fake CA for TLS certificate. Test clients should expect host\n"
@@ -75,6 +83,7 @@ public void run() {
7583
private int port = 8080;
7684
private boolean useTls = true;
7785
private boolean useAlts = false;
86+
private boolean enableOpentelemetryTracing = false;
7887

7988
private ScheduledExecutorService executor;
8089
private Server server;
@@ -106,6 +115,8 @@ void parseArgs(String[] args) {
106115
port = Integer.parseInt(value);
107116
} else if ("use_tls".equals(key)) {
108117
useTls = Boolean.parseBoolean(value);
118+
} else if ("enable_opentelemetry_tracing".equals(key)) {
119+
enableOpentelemetryTracing = Boolean.parseBoolean(value);
109120
} else if ("use_alts".equals(key)) {
110121
useAlts = Boolean.parseBoolean(value);
111122
} else if ("local_handshaker_port".equals(key)) {

interop-testing/src/main/java/io/grpc/testing/integration/XdsTestClient.java

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import io.grpc.InsecureChannelCredentials;
4040
import io.grpc.InsecureServerCredentials;
4141
import io.grpc.ManagedChannel;
42+
import io.grpc.ManagedChannelBuilder;
4243
import io.grpc.Metadata;
4344
import io.grpc.MethodDescriptor;
4445
import io.grpc.Server;
@@ -60,6 +61,7 @@
6061
import io.grpc.testing.integration.Messages.SimpleRequest;
6162
import io.grpc.testing.integration.Messages.SimpleResponse;
6263
import io.grpc.xds.XdsChannelCredentials;
64+
import io.opentelemetry.sdk.OpenTelemetrySdk;
6365
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
6466
import java.util.ArrayList;
6567
import java.util.Collections;
@@ -104,6 +106,7 @@ public final class XdsTestClient {
104106
private long currentRequestId;
105107
private ListeningScheduledExecutorService exec;
106108
private CsmObservability csmObservability;
109+
private OpenTelemetrySdk openTelemetrySdk;
107110

108111
/**
109112
* The main application allowing this client to be launched from the command line.
@@ -265,14 +268,23 @@ private static RpcType parseRpc(String rpc) {
265268
@IgnoreJRERequirement // OpenTelemetry uses Java 8+ APIs
266269
private void run() {
267270
if (enableCsmObservability) {
271+
Map<String, String> props = new HashMap<>();
272+
props.put("otel.logs.exporter", "none");
273+
props.put("otel.metrics.exporter", "otlp");
274+
String tracesExporter = System.getenv("OTEL_TRACES_EXPORTER");
275+
if (tracesExporter != null) {
276+
props.put("otel.traces.exporter", tracesExporter);
277+
} else {
278+
props.put("otel.traces.exporter", "none");
279+
}
280+
281+
AutoConfiguredOpenTelemetrySdk autoSdk = AutoConfiguredOpenTelemetrySdk.builder()
282+
.addPropertiesSupplier(() -> props)
283+
.build();
284+
openTelemetrySdk = autoSdk.getOpenTelemetrySdk();
268285
csmObservability = CsmObservability.newBuilder()
269-
.sdk(AutoConfiguredOpenTelemetrySdk.builder()
270-
.addPropertiesSupplier(() -> ImmutableMap.of(
271-
"otel.logs.exporter", "none",
272-
"otel.metrics.exporter", "prometheus",
273-
"otel.traces.exporter", "none"))
274-
.build()
275-
.getOpenTelemetrySdk())
286+
.sdk(openTelemetrySdk)
287+
.enableTracing(!"none".equals(props.get("otel.traces.exporter")))
276288
.build();
277289
csmObservability.registerGlobal();
278290
}
@@ -289,14 +301,16 @@ private void run() {
289301
try {
290302
statsServer.start();
291303
for (int i = 0; i < numChannels; i++) {
292-
channels.add(
293-
Grpc.newChannelBuilder(
304+
ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder(
294305
server,
295306
secureMode
296307
? XdsChannelCredentials.create(InsecureChannelCredentials.create())
297308
: InsecureChannelCredentials.create())
298-
.enableRetry()
299-
.build());
309+
.enableRetry();
310+
if (enableCsmObservability) {
311+
csmObservability.configureChannelBuilder(builder);
312+
}
313+
channels.add(builder.build());
300314
}
301315
exec = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
302316
Payload requestPayload = Payload.newBuilder()
@@ -325,6 +339,9 @@ private void stop() throws InterruptedException {
325339
if (csmObservability != null) {
326340
csmObservability.close();
327341
}
342+
if (openTelemetrySdk != null) {
343+
openTelemetrySdk.close();
344+
}
328345
}
329346

330347

@@ -373,6 +390,13 @@ public void start(Listener<RespT> responseListener, Metadata headers) {
373390
@Override
374391
public void onHeaders(Metadata headers) {
375392
hostnameRef.set(headers.get(XdsTestServer.HOSTNAME_KEY));
393+
io.opentelemetry.api.trace.Span currentSpan = io.opentelemetry.api.trace.Span.current();
394+
for (String key : config.metadata.keys()) {
395+
String value = config.metadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
396+
if (value != null) {
397+
currentSpan.setAttribute("custom.metadata." + key, value);
398+
}
399+
}
376400
super.onHeaders(headers);
377401
}
378402
},
@@ -406,44 +430,56 @@ public void onNext(EmptyProtos.Empty response) {}
406430
.setPayload(requestPayload)
407431
.setResponseSize(responseSize)
408432
.build();
409-
stub.unaryCall(
410-
request,
411-
new StreamObserver<SimpleResponse>() {
412-
@Override
413-
public void onCompleted() {
414-
handleRpcCompleted(requestId, config.rpcType, hostnameRef.get(), savedWatchers);
415-
}
416433

417-
@Override
418-
public void onError(Throwable t) {
419-
if (printResponse) {
420-
logger.log(Level.WARNING, "Rpc failed", t);
434+
io.opentelemetry.api.baggage.BaggageBuilder baggageBuilder = io.opentelemetry.api.baggage.Baggage.builder();
435+
for (String key : config.metadata.keys()) {
436+
String value = config.metadata.get(Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER));
437+
if (value != null) {
438+
baggageBuilder.put(key, value);
439+
}
440+
}
441+
io.opentelemetry.api.baggage.Baggage baggage = baggageBuilder.build();
442+
443+
try (io.opentelemetry.context.Scope scope = io.opentelemetry.context.Context.current().with(baggage).makeCurrent()) {
444+
stub.unaryCall(
445+
request,
446+
new StreamObserver<SimpleResponse>() {
447+
@Override
448+
public void onCompleted() {
449+
handleRpcCompleted(requestId, config.rpcType, hostnameRef.get(), savedWatchers);
421450
}
422-
handleRpcError(requestId, config.rpcType, Status.fromThrowable(t),
423-
savedWatchers);
424-
}
425451

426-
@Override
427-
public void onNext(SimpleResponse response) {
428-
// TODO(ericgribkoff) Currently some test environments cannot access the stats RPC
429-
// service and rely on parsing stdout.
430-
if (printResponse) {
431-
System.out.println(
432-
"Greeting: Hello world, this is "
433-
+ response.getHostname()
434-
+ ", from "
435-
+ clientCallRef
436-
.get()
437-
.getAttributes()
438-
.get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR));
452+
@Override
453+
public void onError(Throwable t) {
454+
if (printResponse) {
455+
logger.log(Level.WARNING, "Rpc failed", t);
456+
}
457+
handleRpcError(requestId, config.rpcType, Status.fromThrowable(t),
458+
savedWatchers);
439459
}
440-
// Use the hostname from the response if not present in the metadata.
441-
// TODO(ericgribkoff) Delete when server is deployed that sets metadata value.
442-
if (hostnameRef.get() == null) {
443-
hostnameRef.set(response.getHostname());
460+
461+
@Override
462+
public void onNext(SimpleResponse response) {
463+
// TODO(ericgribkoff) Currently some test environments cannot access the stats RPC
464+
// service and rely on parsing stdout.
465+
if (printResponse) {
466+
System.out.println(
467+
"Greeting: Hello world, this is "
468+
+ response.getHostname()
469+
+ ", from "
470+
+ clientCallRef
471+
.get()
472+
.getAttributes()
473+
.get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR));
474+
}
475+
// Use the hostname from the response if not present in the metadata.
476+
// TODO(ericgribkoff) Delete when server is deployed that sets metadata value.
477+
if (hostnameRef.get() == null) {
478+
hostnameRef.set(response.getHostname());
479+
}
444480
}
445-
}
446-
});
481+
});
482+
}
447483
} else {
448484
throw new AssertionError("Unknown RPC type: " + config.rpcType);
449485
}

interop-testing/src/main/java/io/grpc/testing/integration/XdsTestServer.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,16 @@
4646
import io.grpc.testing.integration.Messages.SimpleResponse;
4747
import io.grpc.xds.XdsServerBuilder;
4848
import io.grpc.xds.XdsServerCredentials;
49+
import io.opentelemetry.sdk.OpenTelemetrySdk;
4950
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
5051
import java.net.InetAddress;
5152
import java.net.InetSocketAddress;
5253
import java.net.SocketAddress;
5354
import java.net.UnknownHostException;
5455
import java.util.ArrayList;
56+
import java.util.HashMap;
5557
import java.util.List;
58+
import java.util.Map;
5659
import java.util.Locale;
5760
import java.util.concurrent.TimeUnit;
5861
import java.util.logging.Level;
@@ -92,6 +95,7 @@ public final class XdsTestServer {
9295
private String host;
9396
private Util.AddressType addressType = Util.AddressType.IPV4_IPV6;
9497
private CsmObservability csmObservability;
98+
private OpenTelemetrySdk openTelemetrySdk;
9599

96100
/**
97101
* The main application allowing this client to be launched from the command line.
@@ -197,14 +201,23 @@ void parseArgs(String[] args) {
197201
@IgnoreJRERequirement // OpenTelemetry uses Java 8+ APIs
198202
void start() throws Exception {
199203
if (enableCsmObservability) {
204+
Map<String, String> props = new HashMap<>();
205+
props.put("otel.logs.exporter", "none");
206+
props.put("otel.metrics.exporter", "otlp");
207+
String tracesExporter = System.getenv("OTEL_TRACES_EXPORTER");
208+
if (tracesExporter != null) {
209+
props.put("otel.traces.exporter", tracesExporter);
210+
} else {
211+
props.put("otel.traces.exporter", "none");
212+
}
213+
214+
AutoConfiguredOpenTelemetrySdk autoSdk = AutoConfiguredOpenTelemetrySdk.builder()
215+
.addPropertiesSupplier(() -> props)
216+
.build();
217+
openTelemetrySdk = autoSdk.getOpenTelemetrySdk();
200218
csmObservability = CsmObservability.newBuilder()
201-
.sdk(AutoConfiguredOpenTelemetrySdk.builder()
202-
.addPropertiesSupplier(() -> ImmutableMap.of(
203-
"otel.logs.exporter", "none",
204-
"otel.metrics.exporter", "prometheus",
205-
"otel.traces.exporter", "none"))
206-
.build()
207-
.getOpenTelemetrySdk())
219+
.sdk(openTelemetrySdk)
220+
.enableTracing(!"none".equals(props.get("otel.traces.exporter")))
208221
.build();
209222
csmObservability.registerGlobal();
210223
}
@@ -301,6 +314,9 @@ void stop() throws Exception {
301314
if (csmObservability != null) {
302315
csmObservability.close();
303316
}
317+
if (openTelemetrySdk != null) {
318+
openTelemetrySdk.close();
319+
}
304320
}
305321

306322
private void blockUntilShutdown() throws InterruptedException {

0 commit comments

Comments
 (0)