observationRegistryProvider) {
Properties properties = createPropertiesFromConnectionDetails(daprConnectionDetails);
+ ObservationRegistry registry = observationRegistryProvider.getIfAvailable();
+ if (registry != null && !registry.isNoop()) {
+ return new ObservationDaprWorkflowClient(properties, registry);
+ }
return new DaprWorkflowClient(properties);
}
diff --git a/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/client/ObservationDaprClient.java b/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/client/ObservationDaprClient.java
new file mode 100644
index 0000000000..4fa1839f2c
--- /dev/null
+++ b/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/client/ObservationDaprClient.java
@@ -0,0 +1,754 @@
+/*
+ * Copyright 2025 The Dapr Authors
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package io.dapr.spring.boot.autoconfigure.client;
+
+import io.dapr.client.DaprClient;
+import io.dapr.client.domain.BulkPublishRequest;
+import io.dapr.client.domain.BulkPublishResponse;
+import io.dapr.client.domain.ConfigurationItem;
+import io.dapr.client.domain.DaprMetadata;
+import io.dapr.client.domain.DeleteJobRequest;
+import io.dapr.client.domain.DeleteStateRequest;
+import io.dapr.client.domain.ExecuteStateTransactionRequest;
+import io.dapr.client.domain.GetBulkSecretRequest;
+import io.dapr.client.domain.GetBulkStateRequest;
+import io.dapr.client.domain.GetConfigurationRequest;
+import io.dapr.client.domain.GetJobRequest;
+import io.dapr.client.domain.GetJobResponse;
+import io.dapr.client.domain.GetSecretRequest;
+import io.dapr.client.domain.GetStateRequest;
+import io.dapr.client.domain.HttpExtension;
+import io.dapr.client.domain.InvokeBindingRequest;
+import io.dapr.client.domain.InvokeMethodRequest;
+import io.dapr.client.domain.PublishEventRequest;
+import io.dapr.client.domain.SaveStateRequest;
+import io.dapr.client.domain.ScheduleJobRequest;
+import io.dapr.client.domain.State;
+import io.dapr.client.domain.StateOptions;
+import io.dapr.client.domain.SubscribeConfigurationRequest;
+import io.dapr.client.domain.SubscribeConfigurationResponse;
+import io.dapr.client.domain.TransactionalStateOperation;
+import io.dapr.client.domain.UnsubscribeConfigurationRequest;
+import io.dapr.client.domain.UnsubscribeConfigurationResponse;
+import io.dapr.utils.TypeRef;
+import io.grpc.Channel;
+import io.grpc.stub.AbstractStub;
+import io.micrometer.observation.Observation;
+import io.micrometer.observation.ObservationRegistry;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * A {@link DaprClient} decorator that creates Micrometer Observation spans (bridged to OpenTelemetry)
+ * for each non-deprecated method call. Consumers continue to use {@link DaprClient} as-is; no code
+ * changes are required on their side.
+ *
+ * Deprecated methods are delegated directly without any observation.
+ */
+public class ObservationDaprClient implements DaprClient {
+
+ private final DaprClient delegate;
+ private final ObservationRegistry observationRegistry;
+
+ /**
+ * Creates a new {@code ObservationDaprClient}.
+ *
+ * @param delegate the underlying {@link DaprClient} to delegate calls to
+ * @param observationRegistry the Micrometer {@link ObservationRegistry} used to create spans
+ */
+ public ObservationDaprClient(DaprClient delegate, ObservationRegistry observationRegistry) {
+ this.delegate = delegate;
+ this.observationRegistry = observationRegistry;
+ }
+
+ // -------------------------------------------------------------------------
+ // Internal helpers
+ // -------------------------------------------------------------------------
+
+ private Mono observe(Observation obs, Supplier> monoSupplier) {
+ obs.start();
+ return monoSupplier.get()
+ .doOnError(obs::error)
+ .doFinally(signal -> obs.stop());
+ }
+
+ private Flux observeFlux(Observation obs, Supplier> fluxSupplier) {
+ obs.start();
+ return fluxSupplier.get()
+ .doOnError(obs::error)
+ .doFinally(signal -> obs.stop());
+ }
+
+ private Observation observation(String name) {
+ return Observation.createNotStarted(name, observationRegistry);
+ }
+
+ // -------------------------------------------------------------------------
+ // Lifecycle
+ // -------------------------------------------------------------------------
+
+ @Override
+ public Mono waitForSidecar(int timeoutInMilliseconds) {
+ return observe(observation("dapr.client.wait_for_sidecar"),
+ () -> delegate.waitForSidecar(timeoutInMilliseconds));
+ }
+
+ @Override
+ public Mono shutdown() {
+ return observe(observation("dapr.client.shutdown"),
+ () -> delegate.shutdown());
+ }
+
+ @Override
+ public void close() throws Exception {
+ delegate.close();
+ }
+
+ // -------------------------------------------------------------------------
+ // Pub/Sub
+ // -------------------------------------------------------------------------
+
+ @Override
+ public Mono publishEvent(String pubsubName, String topicName, Object data) {
+ return observe(
+ observation("dapr.client.publish_event")
+ .highCardinalityKeyValue("dapr.pubsub.name", pubsubName)
+ .highCardinalityKeyValue("dapr.topic.name", topicName),
+ () -> delegate.publishEvent(pubsubName, topicName, data));
+ }
+
+ @Override
+ public Mono publishEvent(String pubsubName, String topicName, Object data,
+ Map metadata) {
+ return observe(
+ observation("dapr.client.publish_event")
+ .highCardinalityKeyValue("dapr.pubsub.name", pubsubName)
+ .highCardinalityKeyValue("dapr.topic.name", topicName),
+ () -> delegate.publishEvent(pubsubName, topicName, data, metadata));
+ }
+
+ @Override
+ public Mono publishEvent(PublishEventRequest request) {
+ return observe(
+ observation("dapr.client.publish_event")
+ .highCardinalityKeyValue("dapr.pubsub.name", request.getPubsubName())
+ .highCardinalityKeyValue("dapr.topic.name", request.getTopic()),
+ () -> delegate.publishEvent(request));
+ }
+
+ @Override
+ public Mono> publishEvents(BulkPublishRequest request) {
+ return observe(
+ observation("dapr.client.publish_events")
+ .highCardinalityKeyValue("dapr.pubsub.name", request.getPubsubName())
+ .highCardinalityKeyValue("dapr.topic.name", request.getTopic()),
+ () -> delegate.publishEvents(request));
+ }
+
+ @Override
+ public Mono> publishEvents(String pubsubName, String topicName,
+ String contentType, List events) {
+ return observe(
+ observation("dapr.client.publish_events")
+ .highCardinalityKeyValue("dapr.pubsub.name", pubsubName)
+ .highCardinalityKeyValue("dapr.topic.name", topicName),
+ () -> delegate.publishEvents(pubsubName, topicName, contentType, events));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Mono> publishEvents(String pubsubName, String topicName,
+ String contentType, T... events) {
+ return observe(
+ observation("dapr.client.publish_events")
+ .highCardinalityKeyValue("dapr.pubsub.name", pubsubName)
+ .highCardinalityKeyValue("dapr.topic.name", topicName),
+ () -> delegate.publishEvents(pubsubName, topicName, contentType, events));
+ }
+
+ @Override
+ public Mono> publishEvents(String pubsubName, String topicName,
+ String contentType,
+ Map requestMetadata,
+ List events) {
+ return observe(
+ observation("dapr.client.publish_events")
+ .highCardinalityKeyValue("dapr.pubsub.name", pubsubName)
+ .highCardinalityKeyValue("dapr.topic.name", topicName),
+ () -> delegate.publishEvents(pubsubName, topicName, contentType, requestMetadata, events));
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Mono> publishEvents(String pubsubName, String topicName,
+ String contentType,
+ Map requestMetadata,
+ T... events) {
+ return observe(
+ observation("dapr.client.publish_events")
+ .highCardinalityKeyValue("dapr.pubsub.name", pubsubName)
+ .highCardinalityKeyValue("dapr.topic.name", topicName),
+ () -> delegate.publishEvents(pubsubName, topicName, contentType, requestMetadata, events));
+ }
+
+ // -------------------------------------------------------------------------
+ // Service Invocation — all deprecated, delegate directly
+ // -------------------------------------------------------------------------
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, Object data,
+ HttpExtension httpExtension, Map metadata,
+ TypeRef type) {
+ return delegate.invokeMethod(appId, methodName, data, httpExtension, metadata, type);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, Object request,
+ HttpExtension httpExtension, Map metadata,
+ Class clazz) {
+ return delegate.invokeMethod(appId, methodName, request, httpExtension, metadata, clazz);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, Object request,
+ HttpExtension httpExtension, TypeRef type) {
+ return delegate.invokeMethod(appId, methodName, request, httpExtension, type);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, Object request,
+ HttpExtension httpExtension, Class clazz) {
+ return delegate.invokeMethod(appId, methodName, request, httpExtension, clazz);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, HttpExtension httpExtension,
+ Map metadata, TypeRef type) {
+ return delegate.invokeMethod(appId, methodName, httpExtension, metadata, type);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, HttpExtension httpExtension,
+ Map metadata, Class clazz) {
+ return delegate.invokeMethod(appId, methodName, httpExtension, metadata, clazz);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, Object request,
+ HttpExtension httpExtension, Map metadata) {
+ return delegate.invokeMethod(appId, methodName, request, httpExtension, metadata);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, Object request,
+ HttpExtension httpExtension) {
+ return delegate.invokeMethod(appId, methodName, request, httpExtension);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, HttpExtension httpExtension,
+ Map metadata) {
+ return delegate.invokeMethod(appId, methodName, httpExtension, metadata);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(String appId, String methodName, byte[] request,
+ HttpExtension httpExtension, Map metadata) {
+ return delegate.invokeMethod(appId, methodName, request, httpExtension, metadata);
+ }
+
+ @Override
+ @Deprecated
+ public Mono invokeMethod(InvokeMethodRequest invokeMethodRequest, TypeRef type) {
+ return delegate.invokeMethod(invokeMethodRequest, type);
+ }
+
+ // -------------------------------------------------------------------------
+ // Bindings
+ // -------------------------------------------------------------------------
+
+ @Override
+ public Mono invokeBinding(String bindingName, String operation, Object data) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", bindingName)
+ .highCardinalityKeyValue("dapr.binding.operation", operation),
+ () -> delegate.invokeBinding(bindingName, operation, data));
+ }
+
+ @Override
+ public Mono invokeBinding(String bindingName, String operation, byte[] data,
+ Map metadata) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", bindingName)
+ .highCardinalityKeyValue("dapr.binding.operation", operation),
+ () -> delegate.invokeBinding(bindingName, operation, data, metadata));
+ }
+
+ @Override
+ public Mono invokeBinding(String bindingName, String operation, Object data,
+ TypeRef type) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", bindingName)
+ .highCardinalityKeyValue("dapr.binding.operation", operation),
+ () -> delegate.invokeBinding(bindingName, operation, data, type));
+ }
+
+ @Override
+ public Mono invokeBinding(String bindingName, String operation, Object data,
+ Class clazz) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", bindingName)
+ .highCardinalityKeyValue("dapr.binding.operation", operation),
+ () -> delegate.invokeBinding(bindingName, operation, data, clazz));
+ }
+
+ @Override
+ public Mono invokeBinding(String bindingName, String operation, Object data,
+ Map metadata, TypeRef type) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", bindingName)
+ .highCardinalityKeyValue("dapr.binding.operation", operation),
+ () -> delegate.invokeBinding(bindingName, operation, data, metadata, type));
+ }
+
+ @Override
+ public Mono invokeBinding(String bindingName, String operation, Object data,
+ Map metadata, Class clazz) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", bindingName)
+ .highCardinalityKeyValue("dapr.binding.operation", operation),
+ () -> delegate.invokeBinding(bindingName, operation, data, metadata, clazz));
+ }
+
+ @Override
+ public Mono invokeBinding(InvokeBindingRequest request) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", request.getName())
+ .highCardinalityKeyValue("dapr.binding.operation", request.getOperation()),
+ () -> delegate.invokeBinding(request));
+ }
+
+ @Override
+ public Mono invokeBinding(InvokeBindingRequest request, TypeRef type) {
+ return observe(
+ observation("dapr.client.invoke_binding")
+ .highCardinalityKeyValue("dapr.binding.name", request.getName())
+ .highCardinalityKeyValue("dapr.binding.operation", request.getOperation()),
+ () -> delegate.invokeBinding(request, type));
+ }
+
+ // -------------------------------------------------------------------------
+ // State Management
+ // -------------------------------------------------------------------------
+
+ @Override
+ public Mono> getState(String storeName, State state, TypeRef type) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", state.getKey()),
+ () -> delegate.getState(storeName, state, type));
+ }
+
+ @Override
+ public Mono> getState(String storeName, State state, Class clazz) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", state.getKey()),
+ () -> delegate.getState(storeName, state, clazz));
+ }
+
+ @Override
+ public Mono> getState(String storeName, String key, TypeRef type) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.getState(storeName, key, type));
+ }
+
+ @Override
+ public Mono> getState(String storeName, String key, Class clazz) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.getState(storeName, key, clazz));
+ }
+
+ @Override
+ public Mono> getState(String storeName, String key, StateOptions options,
+ TypeRef type) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.getState(storeName, key, options, type));
+ }
+
+ @Override
+ public Mono> getState(String storeName, String key, StateOptions options,
+ Class clazz) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.getState(storeName, key, options, clazz));
+ }
+
+ @Override
+ public Mono> getState(GetStateRequest request, TypeRef type) {
+ return observe(
+ observation("dapr.client.get_state")
+ .highCardinalityKeyValue("dapr.store.name", request.getStoreName())
+ .highCardinalityKeyValue("dapr.state.key", request.getKey()),
+ () -> delegate.getState(request, type));
+ }
+
+ @Override
+ public Mono>> getBulkState(String storeName, List keys,
+ TypeRef type) {
+ return observe(
+ observation("dapr.client.get_bulk_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName),
+ () -> delegate.getBulkState(storeName, keys, type));
+ }
+
+ @Override
+ public Mono>> getBulkState(String storeName, List keys,
+ Class clazz) {
+ return observe(
+ observation("dapr.client.get_bulk_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName),
+ () -> delegate.getBulkState(storeName, keys, clazz));
+ }
+
+ @Override
+ public Mono>> getBulkState(GetBulkStateRequest request, TypeRef type) {
+ return observe(
+ observation("dapr.client.get_bulk_state")
+ .highCardinalityKeyValue("dapr.store.name", request.getStoreName()),
+ () -> delegate.getBulkState(request, type));
+ }
+
+ @Override
+ public Mono executeStateTransaction(String storeName,
+ List> operations) {
+ return observe(
+ observation("dapr.client.execute_state_transaction")
+ .highCardinalityKeyValue("dapr.store.name", storeName),
+ () -> delegate.executeStateTransaction(storeName, operations));
+ }
+
+ @Override
+ public Mono executeStateTransaction(ExecuteStateTransactionRequest request) {
+ return observe(
+ observation("dapr.client.execute_state_transaction")
+ .highCardinalityKeyValue("dapr.store.name", request.getStateStoreName()),
+ () -> delegate.executeStateTransaction(request));
+ }
+
+ @Override
+ public Mono saveBulkState(String storeName, List> states) {
+ return observe(
+ observation("dapr.client.save_bulk_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName),
+ () -> delegate.saveBulkState(storeName, states));
+ }
+
+ @Override
+ public Mono saveBulkState(SaveStateRequest request) {
+ return observe(
+ observation("dapr.client.save_bulk_state")
+ .highCardinalityKeyValue("dapr.store.name", request.getStoreName()),
+ () -> delegate.saveBulkState(request));
+ }
+
+ @Override
+ public Mono saveState(String storeName, String key, Object value) {
+ return observe(
+ observation("dapr.client.save_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.saveState(storeName, key, value));
+ }
+
+ @Override
+ public Mono saveState(String storeName, String key, String etag, Object value,
+ StateOptions options) {
+ return observe(
+ observation("dapr.client.save_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.saveState(storeName, key, etag, value, options));
+ }
+
+ @Override
+ public Mono saveState(String storeName, String key, String etag, Object value,
+ Map meta, StateOptions options) {
+ return observe(
+ observation("dapr.client.save_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.saveState(storeName, key, etag, value, meta, options));
+ }
+
+ @Override
+ public Mono deleteState(String storeName, String key) {
+ return observe(
+ observation("dapr.client.delete_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.deleteState(storeName, key));
+ }
+
+ @Override
+ public Mono deleteState(String storeName, String key, String etag,
+ StateOptions options) {
+ return observe(
+ observation("dapr.client.delete_state")
+ .highCardinalityKeyValue("dapr.store.name", storeName)
+ .highCardinalityKeyValue("dapr.state.key", key),
+ () -> delegate.deleteState(storeName, key, etag, options));
+ }
+
+ @Override
+ public Mono deleteState(DeleteStateRequest request) {
+ return observe(
+ observation("dapr.client.delete_state")
+ .highCardinalityKeyValue("dapr.store.name", request.getStateStoreName())
+ .highCardinalityKeyValue("dapr.state.key", request.getKey()),
+ () -> delegate.deleteState(request));
+ }
+
+ // -------------------------------------------------------------------------
+ // Secrets
+ // -------------------------------------------------------------------------
+
+ @Override
+ public Mono