From 0b7a051b79c7a394e9bd4f57bd40778fb5f29897 Mon Sep 17 00:00:00 2001 From: Artur Souza Date: Wed, 23 Oct 2024 09:59:01 -0700 Subject: [PATCH 1/3] Fix script tp update release version. (#1153) --- .github/scripts/update_docs.sh | 14 ++++++-- .github/scripts/update_sdk_version.sh | 4 +++ .github/workflows/validate-docs.yml | 34 +++++++++++++++++++ .../data/PostgreSQLDaprKeyValueAdapter.java | 3 +- .../query/DaprPredicateBuilder.java | 25 +++++++++----- dapr-spring/pom.xml | 8 ++--- 6 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/validate-docs.yml diff --git a/.github/scripts/update_docs.sh b/.github/scripts/update_docs.sh index 829b4b486..aacc4ccb7 100755 --- a/.github/scripts/update_docs.sh +++ b/.github/scripts/update_docs.sh @@ -4,20 +4,28 @@ set -uex DAPR_JAVA_SDK_VERSION=$1 +# Alpha artifacts of the sdk tracks the regular SDK minor and patch versions, just not the major. +# Replaces the SDK major version to 0 for alpha artifacts. +DAPR_JAVA_SDK_ALPHA_VERSION=`echo $DAPR_JAVA_SDK_VERSION | sed 's/^[0-9]*\./0./'` + if [[ "$OSTYPE" == "darwin"* ]]; then sed -i bak "s/.*<\/version>\$/${DAPR_JAVA_SDK_VERSION}<\/version>/g" README.md sed -i bak "s/compile('io.dapr:\(.*\):.*')/compile('io.dapr:\\1:${DAPR_JAVA_SDK_VERSION}')/g" README.md sed -i bak "s/.*<\/version>\$/${DAPR_JAVA_SDK_VERSION}<\/version>/g" daprdocs/content/en/java-sdk-docs/_index.md sed -i bak "s/compile('io.dapr:\(.*\):.*')/compile('io.dapr:\\1:${DAPR_JAVA_SDK_VERSION}')/g" daprdocs/content/en/java-sdk-docs/_index.md + sed -i bak "s/.*<\/version>\$/${DAPR_JAVA_SDK_ALPHA_VERSION}<\/version>/g" daprdocs/content/en/java-sdk-docs/spring-boot/_index.md rm README.mdbak else sed -i "s/.*<\/version>\$/${DAPR_JAVA_SDK_VERSION}<\/version>/g" README.md sed -i "s/compile('io.dapr:\(.*\):.*')/compile('io.dapr:\\1:${DAPR_JAVA_SDK_VERSION}')/g" README.md sed -i "s/.*<\/version>\$/${DAPR_JAVA_SDK_VERSION}<\/version>/g" daprdocs/content/en/java-sdk-docs/_index.md sed -i "s/compile('io.dapr:\(.*\):.*')/compile('io.dapr:\\1:${DAPR_JAVA_SDK_VERSION}')/g" daprdocs/content/en/java-sdk-docs/_index.md + sed -i "s/.*<\/version>\$/${DAPR_JAVA_SDK_ALPHA_VERSION}<\/version>/g" daprdocs/content/en/java-sdk-docs/spring-boot/_index.md fi -rm -rf docs -mvn -Dmaven.test.skip=false -Djacoco.skip=true clean install -mvn site-deploy rm -f daprdocs/content/en/java-sdk-docs/_index.mdbak || echo +rm -f daprdocs/content/en/java-sdk-docs/spring-boot/_index.md/_index.mdbak || echo + +rm -rf docs +./mvnw -Dmaven.test.skip=false -Djacoco.skip=true clean install +./mvnw site-deploy diff --git a/.github/scripts/update_sdk_version.sh b/.github/scripts/update_sdk_version.sh index 307a150c0..fba6ff9af 100755 --- a/.github/scripts/update_sdk_version.sh +++ b/.github/scripts/update_sdk_version.sh @@ -10,6 +10,7 @@ DAPR_JAVA_SDK_ALPHA_VERSION=`echo $DAPR_JAVA_SDK_VERSION | sed 's/^[0-9]*\./0./' mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_VERSION mvn versions:set-property -Dproperty=dapr.sdk.alpha.version -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION +mvn versions:set-property -Dproperty=dapr.sdk.version -DnewVersion=$DAPR_JAVA_SDK_VERSION mvn versions:set-property -Dproperty=dapr.sdk.version -DnewVersion=$DAPR_JAVA_SDK_VERSION -f sdk-tests/pom.xml mvn versions:set-property -Dproperty=dapr.sdk.alpha.version -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f sdk-tests/pom.xml @@ -23,4 +24,7 @@ mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f sdk-workflows/pom. # testcontainers-dapr mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f testcontainers-dapr/pom.xml +# dapr-spring +mvn versions:set -DnewVersion=$DAPR_JAVA_SDK_ALPHA_VERSION -f dapr-spring/pom.xml + git clean -f diff --git a/.github/workflows/validate-docs.yml b/.github/workflows/validate-docs.yml new file mode 100644 index 000000000..fa9662612 --- /dev/null +++ b/.github/workflows/validate-docs.yml @@ -0,0 +1,34 @@ +name: Validate Javadocs Generation + +on: + workflow_dispatch: + push: + branches: + - master + - release-* + tags: + - v* + + pull_request: + branches: + - master + - release-* + +jobs: + build: + name: "Validate Javadocs generation" + runs-on: linux-arm64-latest-4-cores + timeout-minutes: 30 + env: + JDK_VER: 17 + steps: + - uses: actions/checkout@v4 + - name: Set up OpenJDK ${{ env.JDK_VER }} + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ env.JDK_VER }} + - name: Install jars + run: ./mvnw install -q -B -DskipTests + - name: Validate Java docs generation + run: ./mvnw site-deploy diff --git a/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/PostgreSQLDaprKeyValueAdapter.java b/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/PostgreSQLDaprKeyValueAdapter.java index e5cbf2b48..c66ea50d8 100644 --- a/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/PostgreSQLDaprKeyValueAdapter.java +++ b/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/PostgreSQLDaprKeyValueAdapter.java @@ -152,7 +152,8 @@ private String createSql(String sqlPattern, String keyspace) { private String createSql(String sqlPattern, String keyspace, Object criteria) { String keyspaceFilter = getKeyspaceFilter(keyspace); - if (criteria instanceof DaprPredicate daprPredicate) { + if (criteria instanceof DaprPredicate) { + var daprPredicate = (DaprPredicate) criteria; String path = daprPredicate.getPath().toString(); String pathWithOutType = String.format("'%s'", path.substring(path.indexOf(".") + 1)); String value = String.format("'%s'", daprPredicate.getValue().toString()); diff --git a/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/repository/query/DaprPredicateBuilder.java b/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/repository/query/DaprPredicateBuilder.java index 1eff32b98..c57bd5650 100644 --- a/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/repository/query/DaprPredicateBuilder.java +++ b/dapr-spring/dapr-spring-data/src/main/java/io/dapr/spring/data/repository/query/DaprPredicateBuilder.java @@ -46,7 +46,9 @@ public Predicate isFalse() { public Predicate isEqualTo(Object value) { return new DaprPredicate(part.getProperty(), value, o -> { if (!ObjectUtils.nullSafeEquals(Part.IgnoreCaseType.NEVER, part.shouldIgnoreCase())) { - if (o instanceof String s1 && value instanceof String s2) { + if ((o instanceof String) && (value instanceof String)) { + var s1 = (String)o; + var s2 = (String)value; return s1.equalsIgnoreCase(s2); } } @@ -85,7 +87,8 @@ public Predicate matches(Object value) { return ObjectUtils.nullSafeEquals(o, value); } - if (value instanceof Pattern pattern) { + if (value instanceof Pattern) { + var pattern = (Pattern)value; return pattern.matcher(o.toString()).find(); } @@ -95,8 +98,10 @@ public Predicate matches(Object value) { public Predicate in(Object value) { return new DaprPredicate(part.getProperty(), value, o -> { - if (value instanceof Collection collection) { - if (o instanceof Collection subSet) { + if (value instanceof Collection) { + var collection = (Collection)value; + if (o instanceof Collection) { + var subSet = (Collection)o; return collection.containsAll(subSet); } @@ -117,7 +122,8 @@ public Predicate contains(Object value) { return false; } - if (o instanceof Collection collection) { + if (o instanceof Collection) { + var collection = (Collection)o; return collection.contains(value); } @@ -125,7 +131,8 @@ public Predicate contains(Object value) { return ObjectUtils.containsElement(ObjectUtils.toObjectArray(o), value); } - if (o instanceof Map map) { + if (o instanceof Map) { + var map = (Map)o; return map.containsValue(value); } @@ -145,9 +152,10 @@ public Predicate contains(Object value) { public Predicate startsWith(Object value) { return new DaprPredicate(part.getProperty(), value, o -> { - if (!(o instanceof String s)) { + if (!(o instanceof String)) { return false; } + var s = (String)o; if (ObjectUtils.nullSafeEquals(Part.IgnoreCaseType.NEVER, part.shouldIgnoreCase())) { return s.startsWith(value.toString()); @@ -159,10 +167,11 @@ public Predicate startsWith(Object value) { public Predicate endsWith(Object value) { return new DaprPredicate(part.getProperty(), value, o -> { - if (!(o instanceof String s)) { + if (!(o instanceof String)) { return false; } + var s = (String)o; if (ObjectUtils.nullSafeEquals(Part.IgnoreCaseType.NEVER, part.shouldIgnoreCase())) { return s.endsWith(value.toString()); } diff --git a/dapr-spring/pom.xml b/dapr-spring/pom.xml index b6cb7b4a2..a7c9474f3 100644 --- a/dapr-spring/pom.xml +++ b/dapr-spring/pom.xml @@ -28,9 +28,9 @@ 3.2.6 - 17 - 17 - 17 + 11 + 11 + 11 @@ -108,7 +108,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.7.0 + 3.2.0 attach-javadocs From be05a47fd88098fd6d2467d136a1ff8304416415 Mon Sep 17 00:00:00 2001 From: artur-ciocanu Date: Tue, 5 Nov 2024 04:47:34 +0200 Subject: [PATCH 2/3] Add Micrometer Observation support to Spring Dapr Messaging (#1150) * Add Micrometer Observation support to Spring Dapr Messaging Signed-off-by: Artur Ciocanu * Remove direct Micrometer deps it is part of Spring Boot Signed-off-by: Artur Ciocanu * Remove another explicit dependency Signed-off-by: Artur Ciocanu * Hide default observation convention implementation Signed-off-by: Artur Ciocanu * Fix typo in default message builder Signed-off-by: Artur Ciocanu * Ensure trace is properly sent using OTEL Signed-off-by: Artur Ciocanu --------- Signed-off-by: Artur Ciocanu Co-authored-by: Artur Ciocanu --- .../dapr-spring-boot-autoconfigure/pom.xml | 4 +- .../pubsub/DaprPubSubProperties.java | 8 + dapr-spring/dapr-spring-messaging/pom.xml | 2 +- .../messaging/DaprMessagingTemplate.java | 160 ++++++++++++++++-- .../DaprMessagingObservationConvention.java | 39 +++++ ...DaprMessagingObservationDocumentation.java | 64 +++++++ .../DaprMessagingSenderContext.java | 98 +++++++++++ ...ultDaprMessagingObservationConvention.java | 47 +++++ dapr-spring/pom.xml | 10 ++ .../it/spring/messaging/TestApplication.java | 4 +- .../io/dapr/testcontainers/DaprContainer.java | 28 ++- 11 files changed, 448 insertions(+), 16 deletions(-) create mode 100644 dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationConvention.java create mode 100644 dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationDocumentation.java create mode 100644 dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingSenderContext.java create mode 100644 dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DefaultDaprMessagingObservationConvention.java diff --git a/dapr-spring/dapr-spring-boot-autoconfigure/pom.xml b/dapr-spring/dapr-spring-boot-autoconfigure/pom.xml index 5cbcba6ae..3bc4bc18f 100644 --- a/dapr-spring/dapr-spring-boot-autoconfigure/pom.xml +++ b/dapr-spring/dapr-spring-boot-autoconfigure/pom.xml @@ -28,8 +28,8 @@ true - org.springframework.boot - spring-boot-starter + org.springframework.boot + spring-boot-starter org.springframework.boot diff --git a/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/pubsub/DaprPubSubProperties.java b/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/pubsub/DaprPubSubProperties.java index 9cd038538..d598b9c99 100644 --- a/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/pubsub/DaprPubSubProperties.java +++ b/dapr-spring/dapr-spring-boot-autoconfigure/src/main/java/io/dapr/spring/boot/autoconfigure/pubsub/DaprPubSubProperties.java @@ -25,6 +25,7 @@ public class DaprPubSubProperties { * Name of the PubSub Dapr component. */ private String name; + private boolean observationEnabled; public String getName() { return name; @@ -34,4 +35,11 @@ public void setName(String name) { this.name = name; } + public boolean isObservationEnabled() { + return observationEnabled; + } + + public void setObservationEnabled(boolean observationEnabled) { + this.observationEnabled = observationEnabled; + } } diff --git a/dapr-spring/dapr-spring-messaging/pom.xml b/dapr-spring/dapr-spring-messaging/pom.xml index 135e904db..c9b280a47 100644 --- a/dapr-spring/dapr-spring-messaging/pom.xml +++ b/dapr-spring/dapr-spring-messaging/pom.xml @@ -12,6 +12,6 @@ dapr-spring-messaging dapr-spring-messaging Dapr Spring Messaging - jar + jar diff --git a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java index 584d91fa5..6e4140936 100644 --- a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java +++ b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java @@ -15,20 +15,104 @@ import io.dapr.client.DaprClient; import io.dapr.client.domain.Metadata; +import io.dapr.spring.messaging.observation.DaprMessagingObservationConvention; +import io.dapr.spring.messaging.observation.DaprMessagingObservationDocumentation; +import io.dapr.spring.messaging.observation.DaprMessagingSenderContext; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.propagation.TextMapSetter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import reactor.core.publisher.Mono; +import reactor.util.context.Context; +import javax.annotation.Nullable; + +import java.util.HashMap; import java.util.Map; -public class DaprMessagingTemplate implements DaprMessagingOperations { +/** + * Create a new DaprMessagingTemplate. + * @param templated message type + */ +public class DaprMessagingTemplate implements DaprMessagingOperations, ApplicationContextAware, BeanNameAware, + SmartInitializingSingleton { + private static final Logger LOGGER = LoggerFactory.getLogger(DaprMessagingTemplate.class); private static final String MESSAGE_TTL_IN_SECONDS = "10"; + private static final DaprMessagingObservationConvention DEFAULT_OBSERVATION_CONVENTION = + DaprMessagingObservationConvention.getDefault(); private final DaprClient daprClient; private final String pubsubName; + private final Map metadata; + private final boolean observationEnabled; + + @Nullable + private ApplicationContext applicationContext; + + @Nullable + private String beanName; + + @Nullable + private OpenTelemetry openTelemetry; + + @Nullable + private ObservationRegistry observationRegistry; - public DaprMessagingTemplate(DaprClient daprClient, String pubsubName) { + @Nullable + private DaprMessagingObservationConvention observationConvention; + + /** + * Constructs a new DaprMessagingTemplate. + * @param daprClient Dapr client + * @param pubsubName pubsub name + * @param observationEnabled whether to enable observations + */ + public DaprMessagingTemplate(DaprClient daprClient, String pubsubName, boolean observationEnabled) { this.daprClient = daprClient; this.pubsubName = pubsubName; + this.metadata = Map.of(Metadata.TTL_IN_SECONDS, MESSAGE_TTL_IN_SECONDS); + this.observationEnabled = observationEnabled; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + /** + * If observations are enabled, attempt to obtain the Observation registry and + * convention. + */ + @Override + public void afterSingletonsInstantiated() { + if (!observationEnabled) { + LOGGER.debug("Observations are not enabled - not recording"); + return; + } + + if (applicationContext == null) { + LOGGER.warn("Observations enabled but application context null - not recording"); + return; + } + + observationRegistry = applicationContext.getBeanProvider(ObservationRegistry.class) + .getIfUnique(() -> observationRegistry); + this.openTelemetry = this.applicationContext.getBeanProvider(OpenTelemetry.class) + .getIfUnique(() -> this.openTelemetry); + observationConvention = applicationContext.getBeanProvider(DaprMessagingObservationConvention.class) + .getIfUnique(() -> observationConvention); } @Override @@ -38,7 +122,7 @@ public void send(String topic, T message) { @Override public SendMessageBuilder newMessage(T message) { - return new SendMessageBuilderImpl<>(this, message); + return new DefaultSendMessageBuilder<>(this, message); } private void doSend(String topic, T message) { @@ -46,13 +130,67 @@ private void doSend(String topic, T message) { } private Mono doSendAsync(String topic, T message) { - return daprClient.publishEvent(pubsubName, - topic, - message, - Map.of(Metadata.TTL_IN_SECONDS, MESSAGE_TTL_IN_SECONDS)); + LOGGER.trace("Sending message to '{}' topic", topic); + + if (canUseObservation()) { + return publishEventWithObservation(pubsubName, topic, message); + } + + return publishEvent(pubsubName, topic, message); + } + + private boolean canUseObservation() { + return observationEnabled + && observationRegistry != null + && openTelemetry != null + && beanName != null; + } + + private Mono publishEvent(String pubsubName, String topic, T message) { + return daprClient.publishEvent(pubsubName, topic, message, metadata); + } + + private Mono publishEventWithObservation(String pubsubName, String topic, T message) { + DaprMessagingSenderContext senderContext = DaprMessagingSenderContext.newContext(topic, this.beanName); + Observation observation = createObservation(senderContext); + + return observation.observe(() -> + publishEvent(pubsubName, topic, message) + .contextWrite(getReactorContext()) + .doOnError(err -> { + LOGGER.error("Failed to send msg to '{}' topic", topic, err); + + observation.error(err); + observation.stop(); + }) + .doOnSuccess(ignore -> { + LOGGER.trace("Sent msg to '{}' topic", topic); + + observation.stop(); + }) + ); + } + + private Context getReactorContext() { + Map map = new HashMap<>(); + TextMapSetter> setter = (carrier, key, value) -> map.put(key, value); + io.opentelemetry.context.Context otelContext = io.opentelemetry.context.Context.current(); + + openTelemetry.getPropagators().getTextMapPropagator().inject(otelContext, map, setter); + + return Context.of(map); + } + + private Observation createObservation(DaprMessagingSenderContext senderContext) { + return DaprMessagingObservationDocumentation.TEMPLATE_OBSERVATION.observation( + observationConvention, + DEFAULT_OBSERVATION_CONVENTION, + () -> senderContext, + observationRegistry + ); } - private static class SendMessageBuilderImpl implements SendMessageBuilder { + private static class DefaultSendMessageBuilder implements SendMessageBuilder { private final DaprMessagingTemplate template; @@ -60,7 +198,7 @@ private static class SendMessageBuilderImpl implements SendMessageBuilder private String topic; - SendMessageBuilderImpl(DaprMessagingTemplate template, T message) { + DefaultSendMessageBuilder(DaprMessagingTemplate template, T message) { this.template = template; this.message = message; } @@ -74,12 +212,12 @@ public SendMessageBuilder withTopic(String topic) { @Override public void send() { - this.template.doSend(this.topic, this.message); + template.doSend(topic, message); } @Override public Mono sendAsync() { - return this.template.doSendAsync(this.topic, this.message); + return template.doSendAsync(topic, message); } } diff --git a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationConvention.java b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationConvention.java new file mode 100644 index 000000000..67d87850d --- /dev/null +++ b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationConvention.java @@ -0,0 +1,39 @@ +/* + * Copyright 2024 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.messaging.observation; + +import io.micrometer.observation.Observation.Context; +import io.micrometer.observation.ObservationConvention; + +/** + * {@link ObservationConvention} for Dapr Messaging. + * + */ +public interface DaprMessagingObservationConvention extends ObservationConvention { + + @Override + default boolean supportsContext(Context context) { + return context instanceof DaprMessagingSenderContext; + } + + @Override + default String getName() { + return "spring.dapr.messaging.template"; + } + + static DaprMessagingObservationConvention getDefault() { + return DefaultDaprMessagingObservationConvention.INSTANCE; + } + +} diff --git a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationDocumentation.java b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationDocumentation.java new file mode 100644 index 000000000..b532faddb --- /dev/null +++ b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingObservationDocumentation.java @@ -0,0 +1,64 @@ +/* + * Copyright 2024 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.messaging.observation; + +import io.micrometer.common.docs.KeyName; +import io.micrometer.observation.Observation; +import io.micrometer.observation.Observation.Context; +import io.micrometer.observation.ObservationConvention; +import io.micrometer.observation.docs.ObservationDocumentation; + +/** + * An {@link Observation} for {@link io.dapr.spring.messaging.DaprMessagingTemplate}. + * + */ +public enum DaprMessagingObservationDocumentation implements ObservationDocumentation { + + /** + * Observation created when a Dapr template sends a message. + */ + TEMPLATE_OBSERVATION { + + @Override + public Class> getDefaultConvention() { + return DefaultDaprMessagingObservationConvention.class; + } + + @Override + public String getPrefix() { + return "spring.dapr.messaging.template"; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return TemplateLowCardinalityTags.values(); + } + }; + + /** + * Low cardinality tags. + */ + public enum TemplateLowCardinalityTags implements KeyName { + /** + * Bean name of the template that sent the message. + */ + BEAN_NAME { + + @Override + public String asString() { + return "spring.dapr.messaging.template.name"; + } + } + } +} diff --git a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingSenderContext.java b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingSenderContext.java new file mode 100644 index 000000000..5397b7a64 --- /dev/null +++ b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DaprMessagingSenderContext.java @@ -0,0 +1,98 @@ +/* + * Copyright 2024 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.messaging.observation; + +import io.micrometer.observation.transport.SenderContext; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link SenderContext} for Dapr Messaging. + * + */ +public final class DaprMessagingSenderContext extends SenderContext { + private final String beanName; + + private final String destination; + + private DaprMessagingSenderContext(Carrier dataHolder, String topic, String beanName) { + super((carrier, key, value) -> dataHolder.property(key, value)); + setCarrier(dataHolder); + this.beanName = beanName; + this.destination = topic; + } + + /** + * Create a new context. + * @param topic topic to be used + * @param beanName name of the bean used usually (typically a {@code DaprMessagingTemplate}) + * @return DaprMessageSenderContext + */ + public static DaprMessagingSenderContext newContext(String topic, String beanName) { + Carrier carrier = new Carrier(); + return new DaprMessagingSenderContext(carrier, topic, beanName); + } + + /** + * The properties of the message. + * @return the properties of the message + */ + public Map properties() { + Carrier carrier = getCarrier(); + + if (carrier == null) { + return Map.of(); + } + + return carrier.properties(); + } + + + /** + * The name of the bean sending the message (typically a {@code DaprMessagingTemplate}). + * @return the name of the bean sending the message + */ + public String getBeanName() { + return this.beanName; + } + + /** + * The destination topic for the message. + * @return the topic the message is being sent to + */ + public String getDestination() { + return this.destination; + } + + + /** + * Acts as a carrier for a Dapr message and records the propagated properties for + * later access by the Dapr. + */ + public static final class Carrier { + + private final Map properties = new HashMap<>(); + + private Carrier() { + } + + public void property(String key, String value) { + this.properties.put(key, value); + } + + public Map properties() { + return Map.copyOf(this.properties); + } + } +} diff --git a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DefaultDaprMessagingObservationConvention.java b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DefaultDaprMessagingObservationConvention.java new file mode 100644 index 000000000..60516edfd --- /dev/null +++ b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/observation/DefaultDaprMessagingObservationConvention.java @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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.messaging.observation; + +import io.micrometer.common.KeyValues; + +/** + * Default {@link DefaultDaprMessagingObservationConvention} for Dapr template key values. + * + */ +class DefaultDaprMessagingObservationConvention implements DaprMessagingObservationConvention { + /** + * A singleton instance of the convention. + */ + public static final DefaultDaprMessagingObservationConvention INSTANCE = + new DefaultDaprMessagingObservationConvention(); + + @Override + public KeyValues getLowCardinalityKeyValues(DaprMessagingSenderContext context) { + return KeyValues.of(DaprMessagingObservationDocumentation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + context.getBeanName()); + } + + // Remove once addressed: + // https://github.com/micrometer-metrics/micrometer-docs-generator/issues/30 + @Override + public String getName() { + return "spring.dapr.messaging.template"; + } + + @Override + public String getContextualName(DaprMessagingSenderContext context) { + return context.getDestination() + " send"; + } + +} diff --git a/dapr-spring/pom.xml b/dapr-spring/pom.xml index a7c9474f3..9a67c459c 100644 --- a/dapr-spring/pom.xml +++ b/dapr-spring/pom.xml @@ -75,6 +75,16 @@ true + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-context + + org.springframework.boot diff --git a/sdk-tests/src/test/java/io/dapr/it/spring/messaging/TestApplication.java b/sdk-tests/src/test/java/io/dapr/it/spring/messaging/TestApplication.java index b9c34c136..44c832dc4 100644 --- a/sdk-tests/src/test/java/io/dapr/it/spring/messaging/TestApplication.java +++ b/sdk-tests/src/test/java/io/dapr/it/spring/messaging/TestApplication.java @@ -35,7 +35,9 @@ static class DaprSpringMessagingConfiguration { @Bean public DaprMessagingTemplate messagingTemplate(DaprClient daprClient, DaprPubSubProperties daprPubSubProperties) { - return new DaprMessagingTemplate<>(daprClient, daprPubSubProperties.getName()); + String pubsubName = daprPubSubProperties.getName(); + boolean observationEnabled = daprPubSubProperties.isObservationEnabled(); + return new DaprMessagingTemplate<>(daprClient, pubsubName, observationEnabled); } } diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java index d96cc2552..9fce30934 100644 --- a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java @@ -18,6 +18,8 @@ import io.dapr.testcontainers.converter.SubscriptionYamlConverter; import io.dapr.testcontainers.converter.YamlConverter; import io.dapr.testcontainers.converter.YamlMapperFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; @@ -30,6 +32,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -37,6 +40,7 @@ import java.util.Set; public class DaprContainer extends GenericContainer { + private static final Logger LOGGER = LoggerFactory.getLogger(DaprContainer.class); private static final int DAPRD_DEFAULT_HTTP_PORT = 3500; private static final int DAPRD_DEFAULT_GRPC_PORT = 50001; private static final DaprProtocol DAPR_PROTOCOL = DaprProtocol.HTTP; @@ -235,14 +239,28 @@ protected void configure() { cmds.add(Integer.toString(appPort)); } + if (configuration != null) { + cmds.add("--config"); + cmds.add("/dapr-resources/" + configuration.getName() + ".yaml"); + } + cmds.add("--log-level"); cmds.add(daprLogLevel.toString()); cmds.add("--resources-path"); cmds.add("/dapr-resources"); - withCommand(cmds.toArray(new String[]{})); + + String[] cmdArray = cmds.toArray(new String[]{}); + LOGGER.info("> `daprd` Command: \n"); + LOGGER.info("\t" + Arrays.toString(cmdArray) + "\n"); + + withCommand(cmdArray); if (configuration != null) { String configurationYaml = CONFIGURATION_CONVERTER.convert(configuration); + + LOGGER.info("> Configuration YAML: \n"); + LOGGER.info("\t\n" + configurationYaml + "\n"); + withCopyToContainer(Transferable.of(configurationYaml), "/dapr-resources/" + configuration.getName() + ".yaml"); } @@ -257,11 +275,19 @@ protected void configure() { for (Component component : components) { String componentYaml = COMPONENT_CONVERTER.convert(component); + + LOGGER.info("> Component YAML: \n"); + LOGGER.info("\t\n" + componentYaml + "\n"); + withCopyToContainer(Transferable.of(componentYaml), "/dapr-resources/" + component.getName() + ".yaml"); } for (Subscription subscription : subscriptions) { String subscriptionYaml = SUBSCRIPTION_CONVERTER.convert(subscription); + + LOGGER.info("> Subscription YAML: \n"); + LOGGER.info("\t\n" + subscriptionYaml + "\n"); + withCopyToContainer(Transferable.of(subscriptionYaml), "/dapr-resources/" + subscription.getName() + ".yaml"); } From 3dc96d7d730f22681631ef5190bb8e748eca5b51 Mon Sep 17 00:00:00 2001 From: artur-ciocanu Date: Tue, 5 Nov 2024 19:49:04 +0200 Subject: [PATCH 3/3] Simplify OTEL integration to reduce deps (#1160) --- .../messaging/DaprMessagingTemplate.java | 49 ++++++------------- dapr-spring/pom.xml | 10 ---- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java index 6e4140936..d8929704e 100644 --- a/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java +++ b/dapr-spring/dapr-spring-messaging/src/main/java/io/dapr/spring/messaging/DaprMessagingTemplate.java @@ -20,8 +20,6 @@ import io.dapr.spring.messaging.observation.DaprMessagingSenderContext; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.context.propagation.TextMapSetter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanNameAware; @@ -33,7 +31,6 @@ import javax.annotation.Nullable; -import java.util.HashMap; import java.util.Map; /** @@ -59,9 +56,6 @@ public class DaprMessagingTemplate implements DaprMessagingOperations, App @Nullable private String beanName; - @Nullable - private OpenTelemetry openTelemetry; - @Nullable private ObservationRegistry observationRegistry; @@ -109,8 +103,6 @@ public void afterSingletonsInstantiated() { observationRegistry = applicationContext.getBeanProvider(ObservationRegistry.class) .getIfUnique(() -> observationRegistry); - this.openTelemetry = this.applicationContext.getBeanProvider(OpenTelemetry.class) - .getIfUnique(() -> this.openTelemetry); observationConvention = applicationContext.getBeanProvider(DaprMessagingObservationConvention.class) .getIfUnique(() -> observationConvention); } @@ -140,10 +132,7 @@ private Mono doSendAsync(String topic, T message) { } private boolean canUseObservation() { - return observationEnabled - && observationRegistry != null - && openTelemetry != null - && beanName != null; + return observationEnabled && observationRegistry != null && beanName != null; } private Mono publishEvent(String pubsubName, String topic, T message) { @@ -154,31 +143,25 @@ private Mono publishEventWithObservation(String pubsubName, String topic, DaprMessagingSenderContext senderContext = DaprMessagingSenderContext.newContext(topic, this.beanName); Observation observation = createObservation(senderContext); - return observation.observe(() -> - publishEvent(pubsubName, topic, message) - .contextWrite(getReactorContext()) - .doOnError(err -> { - LOGGER.error("Failed to send msg to '{}' topic", topic, err); - - observation.error(err); - observation.stop(); - }) - .doOnSuccess(ignore -> { - LOGGER.trace("Sent msg to '{}' topic", topic); + observation.start(); - observation.stop(); - }) - ); - } + return publishEvent(pubsubName, topic, message) + .contextWrite(getReactorContext(senderContext)) + .doOnError(err -> { + LOGGER.error("Failed to send msg to '{}' topic", topic, err); - private Context getReactorContext() { - Map map = new HashMap<>(); - TextMapSetter> setter = (carrier, key, value) -> map.put(key, value); - io.opentelemetry.context.Context otelContext = io.opentelemetry.context.Context.current(); + observation.error(err); + observation.stop(); + }) + .doOnSuccess(ignore -> { + LOGGER.trace("Sent msg to '{}' topic", topic); - openTelemetry.getPropagators().getTextMapPropagator().inject(otelContext, map, setter); + observation.stop(); + }); + } - return Context.of(map); + private Context getReactorContext(DaprMessagingSenderContext senderContext) { + return Context.of(senderContext.properties()); } private Observation createObservation(DaprMessagingSenderContext senderContext) { diff --git a/dapr-spring/pom.xml b/dapr-spring/pom.xml index 9a67c459c..a7c9474f3 100644 --- a/dapr-spring/pom.xml +++ b/dapr-spring/pom.xml @@ -75,16 +75,6 @@ true - - - io.opentelemetry - opentelemetry-api - - - io.opentelemetry - opentelemetry-context - - org.springframework.boot