When a {@link Tracer} is provided, scheduling a workflow emits a client span as a child of
+ * the caller's current OpenTelemetry context and propagates that trace context to the Dapr
+ * sidecar, so the workflow execution is recorded as part of the caller's trace.
+ *
+ * @param properties Properties for the GRPC Channel.
+ * @param tracer OpenTelemetry Tracer used to emit and propagate trace context when
+ * scheduling workflows. May be null, in which case tracing is disabled
+ * and this constructor behaves exactly like {@link #DaprWorkflowClient(Properties)}.
+ */
+ public DaprWorkflowClient(Properties properties, @Nullable Tracer tracer) {
+ this(NetworkUtils.buildGrpcManagedChannel(properties, new ApiTokenClientInterceptor(properties)), tracer);
}
/**
@@ -67,16 +84,17 @@ public DaprWorkflowClient(Properties properties) {
* @param additionalInterceptors extra interceptors appended after the API-token interceptor.
*/
protected DaprWorkflowClient(Properties properties, ClientInterceptor... additionalInterceptors) {
- this(buildChannelWithAdditional(properties, additionalInterceptors));
+ this(buildChannelWithAdditional(properties, additionalInterceptors), null);
}
/**
* Private Constructor that passes a created DurableTaskClient and the new GRPC channel.
*
* @param grpcChannel ManagedChannel for GRPC channel.
+ * @param tracer optional Tracer used to propagate trace context when scheduling workflows.
*/
- private DaprWorkflowClient(ManagedChannel grpcChannel) {
- this(createDurableTaskClient(grpcChannel), grpcChannel);
+ private DaprWorkflowClient(ManagedChannel grpcChannel, @Nullable Tracer tracer) {
+ this(createDurableTaskClient(grpcChannel, tracer), grpcChannel);
}
/**
@@ -444,12 +462,18 @@ private static ManagedChannel buildChannelWithAdditional(
* Static method to create the DurableTaskClient.
*
* @param grpcChannel ManagedChannel for GRPC.
+ * @param tracer optional Tracer set on the underlying client; skipped when null.
* @return a new instance of a DurableTaskClient with a GRPC channel.
*/
- private static DurableTaskClient createDurableTaskClient(ManagedChannel grpcChannel) {
- return new DurableTaskGrpcClientBuilder()
- .grpcChannel(grpcChannel)
- .build();
+ private static DurableTaskClient createDurableTaskClient(ManagedChannel grpcChannel, @Nullable Tracer tracer) {
+ DurableTaskGrpcClientBuilder builder = new DurableTaskGrpcClientBuilder()
+ .grpcChannel(grpcChannel);
+
+ if (tracer != null) {
+ builder.tracer(tracer);
+ }
+
+ return builder.build();
}
private static NewOrchestrationInstanceOptions fromNewWorkflowOptions(NewWorkflowOptions options) {
diff --git a/sdk-workflows/src/test/java/io/dapr/workflows/client/DaprWorkflowClientTest.java b/sdk-workflows/src/test/java/io/dapr/workflows/client/DaprWorkflowClientTest.java
index 2faf1e1080..eb718c5dfc 100644
--- a/sdk-workflows/src/test/java/io/dapr/workflows/client/DaprWorkflowClientTest.java
+++ b/sdk-workflows/src/test/java/io/dapr/workflows/client/DaprWorkflowClientTest.java
@@ -13,7 +13,9 @@
package io.dapr.workflows.client;
+import io.dapr.config.Properties;
import io.dapr.durabletask.DurableTaskClient;
+import io.dapr.durabletask.DurableTaskGrpcClientBuilder;
import io.dapr.durabletask.NewOrchestrationInstanceOptions;
import io.dapr.durabletask.OrchestrationMetadata;
import io.dapr.durabletask.OrchestrationRuntimeStatus;
@@ -21,10 +23,12 @@
import io.dapr.workflows.WorkflowContext;
import io.dapr.workflows.WorkflowStub;
import io.grpc.ManagedChannel;
+import io.opentelemetry.api.trace.Tracer;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.MockedConstruction;
import java.lang.reflect.Constructor;
import java.time.Duration;
@@ -38,6 +42,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -83,6 +89,37 @@ public void EmptyConstructor() {
assertDoesNotThrow(() -> new DaprWorkflowClient());
}
+ @Test
+ public void tracerConstructorPassesTracerToDurableTaskClientBuilder() throws Exception {
+ Tracer tracer = mock(Tracer.class);
+
+ try (MockedConstruction