diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d633a29..e26a9828 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v1.5.2 +* Add distributed tracing support for Azure Functions client scenarios ([#211](https://github.com/microsoft/durabletask-java/pull/211)) + + ## v1.5.1 * Improve logging for unexpected connection failures in DurableTaskGrpcWorker ([#216](https://github.com/microsoft/durabletask-java/pull/216/files)) * Add User-Agent Header to gRPC Metadata ([#213](https://github.com/microsoft/durabletask-java/pull/213)) diff --git a/client/build.gradle b/client/build.gradle index 94547309..d9a1e345 100644 --- a/client/build.gradle +++ b/client/build.gradle @@ -15,6 +15,7 @@ archivesBaseName = 'durabletask-client' def grpcVersion = '1.59.0' def protocVersion = '3.12.0' def jacksonVersion = '2.15.3' +def openTelemetryVersion = '1.25.0' // When build on local, you need to set this value to your local jdk11 directory. // Java11 is used to compile and run all the tests. // Example for Windows: C:/Program Files/Java/openjdk-11.0.12_7/ @@ -34,6 +35,9 @@ dependencies { implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}" implementation "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}" implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${jacksonVersion}" + + implementation "io.opentelemetry:opentelemetry-api:${openTelemetryVersion}" + implementation "io.opentelemetry:opentelemetry-context:${openTelemetryVersion}" testImplementation(platform('org.junit:junit-bom:5.7.2')) testImplementation('org.junit.jupiter:junit-jupiter') diff --git a/client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java b/client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java index 11152ef6..20fc3a05 100644 --- a/client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java +++ b/client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java @@ -17,6 +17,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Logger; +import java.util.stream.Collectors; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; /** * Durable Task client implementation that uses gRPC to connect to a remote "sidecar" process. @@ -110,6 +114,37 @@ public String scheduleNewOrchestrationInstance( builder.setScheduledStartTimestamp(ts); } + Span currentSpan = Span.current(); + String traceParent = null; + String traceState = null; + + if (currentSpan != null && currentSpan.getSpanContext().isValid()) { + SpanContext spanContext = currentSpan.getSpanContext(); + + // Construct the traceparent according to the W3C Trace Context specification + // https://www.w3.org/TR/trace-context/#traceparent-header + traceParent = String.format("00-%s-%s-%02x", + spanContext.getTraceId(), // 32-character trace ID + spanContext.getSpanId(), // 16-character span ID + spanContext.getTraceFlags().asByte() // Trace flags (i.e. sampled or not) + ); + + // Get the tracestate + traceState = spanContext.getTraceState().asMap() + .entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining(",")); + } + + if (traceParent != null) { + TraceContext traceContext = TraceContext.newBuilder() + .setTraceParent(traceParent) + .setTraceState(traceState != null ? StringValue.of(traceState) : StringValue.getDefaultInstance()) + .build(); + builder.setParentTraceContext(traceContext); // Set the TraceContext in the CreateInstanceRequest + } + CreateInstanceRequest request = builder.build(); CreateInstanceResponse response = this.sidecarClient.startInstance(request); return response.getInstanceId();