From 6f35711198c3cd256274e5d42f0a4f9fddb2d16b Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 6 Sep 2022 17:23:23 -0400 Subject: [PATCH 01/31] GH-1444: Listener Observability Initial Commit - expand scope to include error handler: https://github.com/spring-projects/spring-amqp/pull/1287 - tracing can't be used with a batch listener (multiple messages in listener call) --- build.gradle | 2 + .../rabbit/connection/RabbitAccessor.java | 20 +- .../amqp/rabbit/core/RabbitTemplate.java | 80 ++++++- .../AbstractMessageListenerContainer.java | 50 ++++- .../micrometer/MessageReceiverContext.java | 37 ++++ .../micrometer/MessageSenderContext.java | 37 ++++ .../micrometer/RabbitListenerObservation.java | 72 +++++++ .../RabbitListenerObservationConvention.java | 73 +++++++ .../micrometer/RabbitTemplateObservation.java | 72 +++++++ .../RabbitTemplateObservationConvention.java | 73 +++++++ .../support/micrometer/package-info.java | 6 + .../support/micrometer/ObservationTests.java | 195 ++++++++++++++++++ .../src/test/resources/log4j2-test.xml | 2 +- 13 files changed, 709 insertions(+), 10 deletions(-) create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/package-info.java create mode 100644 spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java diff --git a/build.gradle b/build.gradle index 6d1d3c5466..a44fa51831 100644 --- a/build.gradle +++ b/build.gradle @@ -387,6 +387,7 @@ project('spring-rabbit') { optionalApi "ch.qos.logback:logback-classic:$logbackVersion" optionalApi 'org.apache.logging.log4j:log4j-core' optionalApi 'io.micrometer:micrometer-core' + api 'io.micrometer:micrometer-observation' optionalApi 'io.micrometer:micrometer-tracing' // Spring Data projection message binding support optionalApi ("org.springframework.data:spring-data-commons") { @@ -398,6 +399,7 @@ project('spring-rabbit') { testApi project(':spring-rabbit-junit') testImplementation("com.willowtreeapps.assertk:assertk-jvm:$assertkVersion") testImplementation "org.hibernate.validator:hibernate-validator:$hibernateValidationVersion" + testImplementation 'io.micrometer:micrometer-observation-test' testImplementation 'io.micrometer:micrometer-tracing-bridge-brave' testImplementation 'io.micrometer:micrometer-tracing-test' testImplementation 'io.micrometer:micrometer-tracing-integration-test' diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitAccessor.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitAccessor.java index afebee61bc..54c23bf687 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitAccessor.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/connection/RabbitAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,13 @@ import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.ApplicationContext; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.rabbitmq.client.Channel; +import io.micrometer.observation.ObservationRegistry; /** * @author Mark Fisher @@ -40,6 +43,8 @@ public abstract class RabbitAccessor implements InitializingBean { private volatile boolean transactional; + private ObservationRegistry observationRegistry; + public boolean isChannelTransacted() { return this.transactional; } @@ -113,4 +118,17 @@ protected RuntimeException convertRabbitAccessException(Exception ex) { return RabbitExceptionTranslator.convertRabbitAccessException(ex); } + protected void obtainObservationRegistry(@Nullable ApplicationContext appContext) { + if (this.observationRegistry == null && appContext != null) { + ObjectProvider registry = + appContext.getBeanProvider(ObservationRegistry.class); + this.observationRegistry = registry.getIfUnique(); + } + } + + @Nullable + protected ObservationRegistry getObservationRegistry() { + return this.observationRegistry; + } + } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index aecc38de30..4d9e72563b 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -74,6 +74,9 @@ import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; import org.springframework.amqp.rabbit.support.ValueExpression; +import org.springframework.amqp.rabbit.support.micrometer.MessageSenderContext; +import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservation; +import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservationConvention; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.SimpleMessageConverter; import org.springframework.amqp.support.converter.SmartMessageConverter; @@ -83,6 +86,8 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.MapAccessor; import org.springframework.core.ParameterizedTypeReference; @@ -108,6 +113,8 @@ import com.rabbitmq.client.Return; import com.rabbitmq.client.ShutdownListener; import com.rabbitmq.client.ShutdownSignalException; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; /** *

@@ -152,7 +159,7 @@ * @since 1.0 */ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count - implements BeanFactoryAware, RabbitOperations, ChannelAwareMessageListener, + implements BeanFactoryAware, RabbitOperations, ChannelAwareMessageListener, ApplicationContextAware, ListenerContainerAware, PublisherCallbackChannel.Listener, BeanNameAware, DisposableBean { private static final String UNCHECKED = "unchecked"; @@ -198,6 +205,8 @@ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count private final AtomicInteger containerInstance = new AtomicInteger(); + private ApplicationContext applicationContext; + private String exchange = DEFAULT_EXCHANGE; private String routingKey = DEFAULT_ROUTING_KEY; @@ -258,13 +267,19 @@ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count private ErrorHandler replyErrorHandler; + private boolean useChannelForCorrelation; + + private boolean observationEnabled; + + private RabbitTemplateObservationConvention observationConvention; + private volatile boolean usingFastReplyTo; private volatile boolean evaluatedFastReplyTo; private volatile boolean isListener; - private boolean useChannelForCorrelation; + private volatile boolean observationRegistryObtained; /** * Convenient constructor for use with setter injection. Don't forget to set the connection factory. @@ -297,6 +312,29 @@ public final void setConnectionFactory(ConnectionFactory connectionFactory) { } } + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + /** + * Enable observation via micrometer. + * @param observationEnabled true to enable. + * @since 3.0 + */ + public void setObservationEnabled(boolean observationEnabled) { + this.observationEnabled = observationEnabled; + } + + /** + * Set an observation convention; used to add additional key/values to observations. + * @param observationConvention the convention. + * @since 3.0 + */ + public void setObservationConvention(RabbitTemplateObservationConvention observationConvention) { + this.observationConvention = observationConvention; + } + /** * The name of the default exchange to use for send operations when none is specified. Defaults to "" * which is the default exchange in the broker (per the AMQP specification). @@ -2348,7 +2386,7 @@ private boolean isPublisherConfirmsOrReturns(ConnectionFactory connectionFactory * @throws IOException If thrown by RabbitMQ API methods. */ public void doSend(Channel channel, String exchangeArg, String routingKeyArg, Message message, - boolean mandatory, @Nullable CorrelationData correlationData) throws IOException { + boolean mandatory, @Nullable CorrelationData correlationData) { String exch = nullSafeExchange(exchangeArg); String rKey = nullSafeRoutingKey(routingKeyArg); @@ -2378,7 +2416,7 @@ public void doSend(Channel channel, String exchangeArg, String routingKeyArg, Me logger.debug("Publishing message [" + messageToUse + "] on exchange [" + exch + "], routingKey = [" + rKey + "]"); } - sendToRabbit(channel, exch, rKey, mandatory, messageToUse); + observeTheSend(channel, message, mandatory, exch, rKey, messageToUse); // Check if commit needed if (isChannelLocallyTransacted(channel)) { // Transacted channel created by this template -> commit. @@ -2386,6 +2424,30 @@ public void doSend(Channel channel, String exchangeArg, String routingKeyArg, Me } } + protected void observeTheSend(Channel channel, Message message, boolean mandatory, String exch, String rKey, + Message messageToUse) { + + if (!this.observationRegistryObtained) { + obtainObservationRegistry(this.applicationContext); + this.observationRegistryObtained = true; + } + Observation observation; + ObservationRegistry registry = getObservationRegistry(); + if (!this.observationEnabled || registry == null) { + observation = Observation.NOOP; + } + else { + observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(registry, + new MessageSenderContext(message)) + .lowCardinalityKeyValue(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + this.beanName); + if (this.observationConvention != null) { + observation.observationConvention(this.observationConvention); + } + } + observation.observe(() -> sendToRabbit(channel, exch, rKey, mandatory, messageToUse)); + } + /** * Return the exchange or the default exchange if null. * @param exchange the exchange. @@ -2407,10 +2469,16 @@ public String nullSafeRoutingKey(String rk) { } protected void sendToRabbit(Channel channel, String exchange, String routingKey, boolean mandatory, - Message message) throws IOException { + Message message) { + BasicProperties convertedMessageProperties = this.messagePropertiesConverter .fromMessageProperties(message.getMessageProperties(), this.encoding); - channel.basicPublish(exchange, routingKey, mandatory, convertedMessageProperties, message.getBody()); + try { + channel.basicPublish(exchange, routingKey, mandatory, convertedMessageProperties, message.getBody()); + } + catch (IOException ex) { + throw RabbitExceptionTranslator.convertRabbitAccessException(ex); + } } private void setupConfirm(Channel channel, Message message, @Nullable CorrelationData correlationDataArg) { diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 4e276dd05c..3beafeb136 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -63,6 +63,9 @@ import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException; import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; +import org.springframework.amqp.rabbit.support.micrometer.MessageReceiverContext; +import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservation; +import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservationConvention; import org.springframework.amqp.support.ConditionalExceptionLogger; import org.springframework.amqp.support.ConsumerTagStrategy; import org.springframework.amqp.support.postprocessor.MessagePostProcessorUtils; @@ -91,6 +94,8 @@ import com.rabbitmq.client.Channel; import com.rabbitmq.client.ShutdownSignalException; +import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationRegistry; /** * @author Mark Pollack @@ -240,6 +245,8 @@ public abstract class AbstractMessageListenerContainer extends RabbitAccessor private boolean micrometerEnabled = true; + private boolean observationEnabled = false; + private boolean isBatchListener; private long consumeDelay; @@ -254,6 +261,8 @@ public abstract class AbstractMessageListenerContainer extends RabbitAccessor private MessageAckListener messageAckListener = (success, deliveryTag, cause) -> { }; + private RabbitListenerObservationConvention observationConvention; + @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; @@ -1159,6 +1168,24 @@ public void setMicrometerEnabled(boolean micrometerEnabled) { this.micrometerEnabled = micrometerEnabled; } + /** + * Enable observation via micrometer. + * @param observationEnabled true to enable. + * @since 3.0 + */ + public void setObservationEnabled(boolean observationEnabled) { + this.observationEnabled = observationEnabled; + } + + /** + * Set an observation convention; used to add additional key/values to observations. + * @param observationConvention the convention. + * @since 3.0 + */ + public void setObservationConvention(RabbitListenerObservationConvention observationConvention) { + this.observationConvention = observationConvention; + } + /** * Get the consumeDelay - a time to wait before consuming in ms. * @return the consume delay. @@ -1230,7 +1257,7 @@ public void afterPropertiesSet() { validateConfiguration(); initialize(); try { - if (this.micrometerHolder == null && MICROMETER_PRESENT && this.micrometerEnabled + if (this.micrometerHolder == null && MICROMETER_PRESENT && this.micrometerEnabled && !this.observationEnabled && this.applicationContext != null) { String id = getListenerId(); if (id == null) { @@ -1402,6 +1429,7 @@ public void start() { } } } + obtainObservationRegistry(this.applicationContext); try { logger.debug("Starting Rabbit listener container."); configureAdminIfNeeded(); @@ -1499,8 +1527,26 @@ protected void invokeErrorHandler(Throwable ex) { * @see #invokeListener * @see #handleListenerException */ - @SuppressWarnings(UNCHECKED) protected void executeListener(Channel channel, Object data) { + Observation observation; + ObservationRegistry registry = getObservationRegistry(); + if (!this.observationEnabled || data instanceof List || registry == null) { + observation = Observation.NOOP; + } + else { + observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(registry, + new MessageReceiverContext((Message) data)) + .lowCardinalityKeyValue(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), + getListenerId()); + if (this.observationConvention != null) { + observation.observationConvention(this.observationConvention); + } + } + observation.observe(() -> executeListenerAndHandleException(channel, data)); + } + + @SuppressWarnings(UNCHECKED) + protected void executeListenerAndHandleException(Channel channel, Object data) { if (!isRunning()) { if (logger.isWarnEnabled()) { logger.warn( diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java new file mode 100644 index 0000000000..8faac4297e --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import org.springframework.amqp.core.Message; + +import io.micrometer.observation.transport.ReceiverContext; + +/** + * {@link ReceiverContext} for {@link Message}s. + * + * @author Gary Russell + * @since 2.8 + * + */ +public class MessageReceiverContext extends ReceiverContext { + + public MessageReceiverContext(Message message) { + super((carrier, key) -> carrier.getMessageProperties().getHeader(key)); + setCarrier(message); + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java new file mode 100644 index 0000000000..cc4c7dcb86 --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import org.springframework.amqp.core.Message; + +import io.micrometer.observation.transport.SenderContext; + +/** + * {@link SenderContext} for {@link Message}s. + * + * @author Gary Russell + * @since 3.0 + * + */ +public class MessageSenderContext extends SenderContext { + + public MessageSenderContext(Message message) { + super((carrier, key, value) -> message.getMessageProperties().setHeader(key, value)); + setCarrier(message); + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java new file mode 100644 index 0000000000..d561334d27 --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java @@ -0,0 +1,72 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import io.micrometer.common.docs.KeyName; +import io.micrometer.observation.docs.DocumentedObservation; + +/** + * Spring Rabbit Observation for listeners. + * + * @author Gary Russell + * @since 3.0 + * + */ +public enum RabbitListenerObservation implements DocumentedObservation { + + /** + * Observation for Rabbit listeners. + */ + LISTENER_OBSERVATION { + + @Override + public String getName() { + return "spring.rabbit.listener"; + } + + @Override + public String getContextualName() { + return "RabbitListener Observation"; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return ListenerLowCardinalityTags.values(); + } + + }; + + /** + * Low cardinality tags. + */ + public enum ListenerLowCardinalityTags implements KeyName { + + /** + * Listener id. + */ + LISTENER_ID { + + @Override + public String asString() { + return "listener.id"; + } + + } + + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java new file mode 100644 index 0000000000..281e7fa3e9 --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -0,0 +1,73 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import org.springframework.lang.Nullable; + +import io.micrometer.common.KeyValue; +import io.micrometer.common.KeyValues; +import io.micrometer.observation.Observation.Context; +import io.micrometer.observation.Observation.ObservationConvention; + +/** + * {@link ObservationConvention} for Rabbit listener key values. Allows users + * to add {@link KeyValue}s to the observations. + * + * @author Gary Russell + * @since 3.0 + * + */ +public class RabbitListenerObservationConvention implements ObservationConvention { + + @Nullable + private final KeyValues lowCardinality; + + @Nullable + private final KeyValues highCardinality; + + /** + * Create an instance with the provided {@link KeyValues}. + * @param lowCardinality the low cardinality {@link KeyValues}. + * @param highCardinality the high cardinality {@link KeyValues}. + */ + public RabbitListenerObservationConvention(@Nullable KeyValues lowCardinality, + @Nullable KeyValues highCardinality) { + + this.lowCardinality = lowCardinality; + this.highCardinality = highCardinality; + } + + @Override + public boolean supportsContext(Context context) { + return context instanceof MessageReceiverContext; + } + + @Override + public KeyValues getLowCardinalityKeyValues(MessageReceiverContext context) { + return this.lowCardinality == null + ? ObservationConvention.super.getLowCardinalityKeyValues(context) + : this.lowCardinality; + } + + @Override + public KeyValues getHighCardinalityKeyValues(MessageReceiverContext context) { + return this.highCardinality == null + ? ObservationConvention.super.getHighCardinalityKeyValues(context) + : this.highCardinality; + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java new file mode 100644 index 0000000000..8eee84396e --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java @@ -0,0 +1,72 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import io.micrometer.common.docs.KeyName; +import io.micrometer.observation.docs.DocumentedObservation; + +/** + * Spring RabbitMQ Observation for {@link org.springframework.amqp.rabbit.core.RabbitTemplate}. + * + * @author Gary Russell + * @since 3.0 + * + */ +public enum RabbitTemplateObservation implements DocumentedObservation { + + /** + * {@link org.springframework.kafka.core.KafkaTemplate} observation. + */ + TEMPLATE_OBSERVATION { + + @Override + public String getName() { + return "spring.rabbit.template"; + } + + @Override + public String getContextualName() { + return "RabbitTemplate Observation"; + } + + @Override + public KeyName[] getLowCardinalityKeyNames() { + return TemplateLowCardinalityTags.values(); + } + + }; + + /** + * Low cardinality tags. + */ + public enum TemplateLowCardinalityTags implements KeyName { + + /** + * Bean name of the template. + */ + BEAN_NAME { + + @Override + public String asString() { + return "bean.name"; + } + + } + + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java new file mode 100644 index 0000000000..f0cf52661c --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -0,0 +1,73 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import org.springframework.lang.Nullable; + +import io.micrometer.common.KeyValue; +import io.micrometer.common.KeyValues; +import io.micrometer.observation.Observation.Context; +import io.micrometer.observation.Observation.ObservationConvention; + +/** + * {@link ObservationConvention} for Rabbit template key values. Allows users + * to add {@link KeyValue}s to the observations. + * + * @author Gary Russell + * @since 3.0 + * + */ +public class RabbitTemplateObservationConvention implements ObservationConvention { + + @Nullable + private final KeyValues lowCardinality; + + @Nullable + private final KeyValues highCardinality; + + /** + * Create an instance with the provided {@link KeyValues}. + * @param lowCardinality the low cardinality {@link KeyValues}. + * @param highCardinality the high cardinality {@link KeyValues}. + */ + public RabbitTemplateObservationConvention(@Nullable KeyValues lowCardinality, + @Nullable KeyValues highCardinality) { + + this.lowCardinality = lowCardinality; + this.highCardinality = highCardinality; + } + + @Override + public boolean supportsContext(Context context) { + return context instanceof MessageReceiverContext; + } + + @Override + public KeyValues getLowCardinalityKeyValues(MessageSenderContext context) { + return this.lowCardinality == null + ? ObservationConvention.super.getLowCardinalityKeyValues(context) + : this.lowCardinality; + } + + @Override + public KeyValues getHighCardinalityKeyValues(MessageSenderContext context) { + return this.highCardinality == null + ? ObservationConvention.super.getHighCardinalityKeyValues(context) + : this.highCardinality; + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/package-info.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/package-info.java new file mode 100644 index 0000000000..8131427dac --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/package-info.java @@ -0,0 +1,6 @@ +/** + * Provides classes for Micrometer support. + */ +@org.springframework.lang.NonNullApi +@org.springframework.lang.NonNullFields +package org.springframework.amqp.rabbit.support.micrometer; diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java new file mode 100644 index 0000000000..bcca918792 --- /dev/null +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -0,0 +1,195 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.junit.RabbitAvailable; +import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import io.micrometer.observation.ObservationHandler; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.observation.tck.TestObservationRegistry; +import io.micrometer.tracing.Span; +import io.micrometer.tracing.TraceContext; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.handler.DefaultTracingObservationHandler; +import io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler; +import io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler; +import io.micrometer.tracing.propagation.Propagator; +import io.micrometer.tracing.test.simple.SimpleSpan; +import io.micrometer.tracing.test.simple.SimpleTracer; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +@SpringJUnitConfig +@RabbitAvailable(queues = { "observation.testQ1", "observation.testQ2" }) +public class ObservationTests { + + @Test + void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, + @Autowired SimpleTracer tracer) throws InterruptedException { + + template.convertAndSend("observation.testQ1", "test"); + assertThat(listener.latch.await(10, TimeUnit.SECONDS)).isTrue(); + assertThat(listener.message) + .extracting(msg -> msg.getMessageProperties().getHeaders()) + .hasFieldOrPropertyWithValue("foo", "some foo value") + .hasFieldOrPropertyWithValue("bar", "some bar value"); + Deque spans = tracer.getSpans(); + assertThat(spans).hasSize(4); + SimpleSpan span = spans.poll(); + assertThat(span.getTags()).containsEntry("bean.name", "template"); + span = spans.poll(); + assertThat(span.getTags()) + .containsAllEntriesOf(Map.of("listener.id", "obs1", "foo", "some foo value", "bar", "some bar value")); + span = spans.poll(); + assertThat(span.getTags()).containsEntry("bean.name", "template"); + span = spans.poll(); + assertThat(span.getTags()) + .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); } + + + @Configuration + @EnableRabbit + public static class Config { + + @Bean + CachingConnectionFactory ccf() { + return new CachingConnectionFactory(RabbitAvailableCondition.getBrokerRunning().getConnectionFactory()); + } + + @Bean + RabbitTemplate template(CachingConnectionFactory ccf) { + RabbitTemplate template = new RabbitTemplate(ccf); + template.setObservationEnabled(true); + return template; + } + + @Bean + SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(CachingConnectionFactory ccf) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + factory.setConnectionFactory(ccf); + factory.setContainerCustomizer(container -> container.setObservationEnabled(true)); + return factory; + } + + @Bean + SimpleTracer simpleTracer() { + return new SimpleTracer(); + } + + @Bean + ObservationRegistry observationRegistry(Tracer tracer, Propagator propagator) { + TestObservationRegistry observationRegistry = TestObservationRegistry.create(); + observationRegistry.observationConfig().observationHandler( + // Composite will pick the first matching handler + new ObservationHandler.FirstMatchingCompositeObservationHandler( + // This is responsible for creating a child span on the sender side + new PropagatingSenderTracingObservationHandler<>(tracer, propagator), + // This is responsible for creating a span on the receiver side + new PropagatingReceiverTracingObservationHandler<>(tracer, propagator), + // This is responsible for creating a default span + new DefaultTracingObservationHandler(tracer))); + return observationRegistry; + } + + @Bean + Propagator propagator(Tracer tracer) { + return new Propagator() { + + // List of headers required for tracing propagation + @Override + public List fields() { + return Arrays.asList("foo", "bar"); + } + + // This is called on the producer side when the message is being sent + // Normally we would pass information from tracing context - for tests we don't need to + @Override + public void inject(TraceContext context, @Nullable C carrier, Setter setter) { + setter.set(carrier, "foo", "some foo value"); + setter.set(carrier, "bar", "some bar value"); + } + + // This is called on the consumer side when the message is consumed + // Normally we would use tools like Extractor from tracing but for tests we are just manually creating a span + @Override + public Span.Builder extract(C carrier, Getter getter) { + String foo = getter.get(carrier, "foo"); + String bar = getter.get(carrier, "bar"); + return tracer.spanBuilder().tag("foo", foo).tag("bar", bar); + } + }; + } + + @Bean + Listener listener(RabbitTemplate template) { + return new Listener(template); + } + + } + + public static class Listener { + + private final RabbitTemplate template; + + final CountDownLatch latch = new CountDownLatch(1); + + volatile Message message; + + public Listener(RabbitTemplate template) { + this.template = template; + } + + @RabbitListener(id = "obs1", queues = "observation.testQ1") + void listen1(Message in) { + template.send("observation.testQ2", in); + } + + @RabbitListener(id = "obs2", queues = "observation.testQ2") + void listen2(Message in) { + this.message = in; + latch.countDown(); + } + + } + +} diff --git a/spring-rabbit/src/test/resources/log4j2-test.xml b/spring-rabbit/src/test/resources/log4j2-test.xml index 86ded2e34f..051983e125 100644 --- a/spring-rabbit/src/test/resources/log4j2-test.xml +++ b/spring-rabbit/src/test/resources/log4j2-test.xml @@ -6,7 +6,7 @@ - + From 3c767f1b54b5e42f51d8beb019c35eb834164806 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 14:23:30 -0400 Subject: [PATCH 02/31] Rename Sender/Receiver contexts and other PR review comments. --- build.gradle | 2 +- .../amqp/rabbit/core/RabbitTemplate.java | 11 +++++------ .../listener/AbstractMessageListenerContainer.java | 4 ++-- ...erContext.java => AmqpMessageReceiverContext.java} | 4 ++-- ...nderContext.java => AmqpMessageSenderContext.java} | 5 +++-- .../RabbitListenerObservationConvention.java | 8 ++++---- .../RabbitTemplateObservationConvention.java | 8 ++++---- spring-rabbit/src/test/resources/log4j2-test.xml | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) rename spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/{MessageReceiverContext.java => AmqpMessageReceiverContext.java} (88%) rename spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/{MessageSenderContext.java => AmqpMessageSenderContext.java} (88%) diff --git a/build.gradle b/build.gradle index a44fa51831..bb702cfe7a 100644 --- a/build.gradle +++ b/build.gradle @@ -399,7 +399,7 @@ project('spring-rabbit') { testApi project(':spring-rabbit-junit') testImplementation("com.willowtreeapps.assertk:assertk-jvm:$assertkVersion") testImplementation "org.hibernate.validator:hibernate-validator:$hibernateValidationVersion" - testImplementation 'io.micrometer:micrometer-observation-test' + testImplementation 'io.micrometer:micrometer-observation-test' testImplementation 'io.micrometer:micrometer-tracing-bridge-brave' testImplementation 'io.micrometer:micrometer-tracing-test' testImplementation 'io.micrometer:micrometer-tracing-integration-test' diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index 4d9e72563b..cadaa4b6d4 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -74,7 +74,7 @@ import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; import org.springframework.amqp.rabbit.support.ValueExpression; -import org.springframework.amqp.rabbit.support.micrometer.MessageSenderContext; +import org.springframework.amqp.rabbit.support.micrometer.AmqpMessageSenderContext; import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservation; import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservationConvention; import org.springframework.amqp.support.converter.MessageConverter; @@ -2416,7 +2416,7 @@ public void doSend(Channel channel, String exchangeArg, String routingKeyArg, Me logger.debug("Publishing message [" + messageToUse + "] on exchange [" + exch + "], routingKey = [" + rKey + "]"); } - observeTheSend(channel, message, mandatory, exch, rKey, messageToUse); + observeTheSend(channel, messageToUse, mandatory, exch, rKey); // Check if commit needed if (isChannelLocallyTransacted(channel)) { // Transacted channel created by this template -> commit. @@ -2424,8 +2424,7 @@ public void doSend(Channel channel, String exchangeArg, String routingKeyArg, Me } } - protected void observeTheSend(Channel channel, Message message, boolean mandatory, String exch, String rKey, - Message messageToUse) { + protected void observeTheSend(Channel channel, Message message, boolean mandatory, String exch, String rKey) { if (!this.observationRegistryObtained) { obtainObservationRegistry(this.applicationContext); @@ -2438,14 +2437,14 @@ protected void observeTheSend(Channel channel, Message message, boolean mandator } else { observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(registry, - new MessageSenderContext(message)) + new AmqpMessageSenderContext(message)) .lowCardinalityKeyValue(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), this.beanName); if (this.observationConvention != null) { observation.observationConvention(this.observationConvention); } } - observation.observe(() -> sendToRabbit(channel, exch, rKey, mandatory, messageToUse)); + observation.observe(() -> sendToRabbit(channel, exch, rKey, mandatory, message)); } /** diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 3beafeb136..7a5c2ee1d1 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -63,7 +63,7 @@ import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException; import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; -import org.springframework.amqp.rabbit.support.micrometer.MessageReceiverContext; +import org.springframework.amqp.rabbit.support.micrometer.AmqpMessageReceiverContext; import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservation; import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservationConvention; import org.springframework.amqp.support.ConditionalExceptionLogger; @@ -1535,7 +1535,7 @@ protected void executeListener(Channel channel, Object data) { } else { observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(registry, - new MessageReceiverContext((Message) data)) + new AmqpMessageReceiverContext((Message) data)) .lowCardinalityKeyValue(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), getListenerId()); if (this.observationConvention != null) { diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageReceiverContext.java similarity index 88% rename from spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java rename to spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageReceiverContext.java index 8faac4297e..e4c16ecde6 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageReceiverContext.java @@ -27,9 +27,9 @@ * @since 2.8 * */ -public class MessageReceiverContext extends ReceiverContext { +public class AmqpMessageReceiverContext extends ReceiverContext { - public MessageReceiverContext(Message message) { + public AmqpMessageReceiverContext(Message message) { super((carrier, key) -> carrier.getMessageProperties().getHeader(key)); setCarrier(message); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageSenderContext.java similarity index 88% rename from spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java rename to spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageSenderContext.java index cc4c7dcb86..4f15d95574 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/MessageSenderContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageSenderContext.java @@ -27,11 +27,12 @@ * @since 3.0 * */ -public class MessageSenderContext extends SenderContext { +public class AmqpMessageSenderContext extends SenderContext { - public MessageSenderContext(Message message) { + public AmqpMessageSenderContext(Message message) { super((carrier, key, value) -> message.getMessageProperties().setHeader(key, value)); setCarrier(message); + } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index 281e7fa3e9..82c70488ce 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -31,7 +31,7 @@ * @since 3.0 * */ -public class RabbitListenerObservationConvention implements ObservationConvention { +public class RabbitListenerObservationConvention implements ObservationConvention { @Nullable private final KeyValues lowCardinality; @@ -53,18 +53,18 @@ public RabbitListenerObservationConvention(@Nullable KeyValues lowCardinality, @Override public boolean supportsContext(Context context) { - return context instanceof MessageReceiverContext; + return context instanceof AmqpMessageReceiverContext; } @Override - public KeyValues getLowCardinalityKeyValues(MessageReceiverContext context) { + public KeyValues getLowCardinalityKeyValues(AmqpMessageReceiverContext context) { return this.lowCardinality == null ? ObservationConvention.super.getLowCardinalityKeyValues(context) : this.lowCardinality; } @Override - public KeyValues getHighCardinalityKeyValues(MessageReceiverContext context) { + public KeyValues getHighCardinalityKeyValues(AmqpMessageReceiverContext context) { return this.highCardinality == null ? ObservationConvention.super.getHighCardinalityKeyValues(context) : this.highCardinality; diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index f0cf52661c..336575531e 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -31,7 +31,7 @@ * @since 3.0 * */ -public class RabbitTemplateObservationConvention implements ObservationConvention { +public class RabbitTemplateObservationConvention implements ObservationConvention { @Nullable private final KeyValues lowCardinality; @@ -53,18 +53,18 @@ public RabbitTemplateObservationConvention(@Nullable KeyValues lowCardinality, @Override public boolean supportsContext(Context context) { - return context instanceof MessageReceiverContext; + return context instanceof AmqpMessageSenderContext; } @Override - public KeyValues getLowCardinalityKeyValues(MessageSenderContext context) { + public KeyValues getLowCardinalityKeyValues(AmqpMessageSenderContext context) { return this.lowCardinality == null ? ObservationConvention.super.getLowCardinalityKeyValues(context) : this.lowCardinality; } @Override - public KeyValues getHighCardinalityKeyValues(MessageSenderContext context) { + public KeyValues getHighCardinalityKeyValues(AmqpMessageSenderContext context) { return this.highCardinality == null ? ObservationConvention.super.getHighCardinalityKeyValues(context) : this.highCardinality; diff --git a/spring-rabbit/src/test/resources/log4j2-test.xml b/spring-rabbit/src/test/resources/log4j2-test.xml index 051983e125..86ded2e34f 100644 --- a/spring-rabbit/src/test/resources/log4j2-test.xml +++ b/spring-rabbit/src/test/resources/log4j2-test.xml @@ -6,7 +6,7 @@ - + From 08867838f85cef1ed53f6822d73cbf19b3edf2da Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 15:04:32 -0400 Subject: [PATCH 03/31] Rename contexts to Rabbit...; supply default KeyValues via the conventions. --- .../amqp/rabbit/core/RabbitTemplate.java | 14 +++++----- .../AbstractMessageListenerContainer.java | 14 +++++----- .../RabbitListenerObservationConvention.java | 26 +++++++++---------- ...java => RabbitMessageReceiverContext.java} | 11 ++++++-- ...t.java => RabbitMessageSenderContext.java} | 10 +++++-- .../RabbitTemplateObservationConvention.java | 24 ++++++++--------- 6 files changed, 53 insertions(+), 46 deletions(-) rename spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/{AmqpMessageReceiverContext.java => RabbitMessageReceiverContext.java} (77%) rename spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/{AmqpMessageSenderContext.java => RabbitMessageSenderContext.java} (79%) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index cadaa4b6d4..380c4c0066 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -74,7 +74,7 @@ import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; import org.springframework.amqp.rabbit.support.ValueExpression; -import org.springframework.amqp.rabbit.support.micrometer.AmqpMessageSenderContext; +import org.springframework.amqp.rabbit.support.micrometer.RabbitMessageSenderContext; import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservation; import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservationConvention; import org.springframework.amqp.support.converter.MessageConverter; @@ -271,7 +271,8 @@ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count private boolean observationEnabled; - private RabbitTemplateObservationConvention observationConvention; + private RabbitTemplateObservationConvention observationConvention = + new RabbitTemplateObservationConvention(null, null); private volatile boolean usingFastReplyTo; @@ -332,6 +333,7 @@ public void setObservationEnabled(boolean observationEnabled) { * @since 3.0 */ public void setObservationConvention(RabbitTemplateObservationConvention observationConvention) { + Assert.notNull(observationConvention, "'observationConvention' cannot be null"); this.observationConvention = observationConvention; } @@ -2437,12 +2439,8 @@ protected void observeTheSend(Channel channel, Message message, boolean mandator } else { observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(registry, - new AmqpMessageSenderContext(message)) - .lowCardinalityKeyValue(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), - this.beanName); - if (this.observationConvention != null) { - observation.observationConvention(this.observationConvention); - } + new RabbitMessageSenderContext(message, this.beanName)) + .observationConvention(this.observationConvention); } observation.observe(() -> sendToRabbit(channel, exch, rKey, mandatory, message)); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 7a5c2ee1d1..0db816aaaf 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -63,9 +63,9 @@ import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException; import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; -import org.springframework.amqp.rabbit.support.micrometer.AmqpMessageReceiverContext; import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservation; import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservationConvention; +import org.springframework.amqp.rabbit.support.micrometer.RabbitMessageReceiverContext; import org.springframework.amqp.support.ConditionalExceptionLogger; import org.springframework.amqp.support.ConsumerTagStrategy; import org.springframework.amqp.support.postprocessor.MessagePostProcessorUtils; @@ -261,7 +261,8 @@ public abstract class AbstractMessageListenerContainer extends RabbitAccessor private MessageAckListener messageAckListener = (success, deliveryTag, cause) -> { }; - private RabbitListenerObservationConvention observationConvention; + private RabbitListenerObservationConvention observationConvention = + new RabbitListenerObservationConvention(null, null); @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { @@ -1183,6 +1184,7 @@ public void setObservationEnabled(boolean observationEnabled) { * @since 3.0 */ public void setObservationConvention(RabbitListenerObservationConvention observationConvention) { + Assert.notNull(observationConvention, "'observationConvention' cannot be null"); this.observationConvention = observationConvention; } @@ -1535,12 +1537,8 @@ protected void executeListener(Channel channel, Object data) { } else { observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(registry, - new AmqpMessageReceiverContext((Message) data)) - .lowCardinalityKeyValue(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), - getListenerId()); - if (this.observationConvention != null) { - observation.observationConvention(this.observationConvention); - } + new RabbitMessageReceiverContext((Message) data, getListenerId())) + .observationConvention(this.observationConvention); } observation.observe(() -> executeListenerAndHandleException(channel, data)); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index 82c70488ce..f1f18dd19e 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -31,12 +31,10 @@ * @since 3.0 * */ -public class RabbitListenerObservationConvention implements ObservationConvention { +public class RabbitListenerObservationConvention implements ObservationConvention { - @Nullable private final KeyValues lowCardinality; - @Nullable private final KeyValues highCardinality; /** @@ -47,27 +45,27 @@ public class RabbitListenerObservationConvention implements ObservationConventio public RabbitListenerObservationConvention(@Nullable KeyValues lowCardinality, @Nullable KeyValues highCardinality) { - this.lowCardinality = lowCardinality; - this.highCardinality = highCardinality; + this.lowCardinality = lowCardinality != null ? KeyValues.of(lowCardinality) : KeyValues.empty(); + this.highCardinality = highCardinality != null ? KeyValues.of(highCardinality) : KeyValues.empty(); } @Override public boolean supportsContext(Context context) { - return context instanceof AmqpMessageReceiverContext; + return context instanceof RabbitMessageReceiverContext; } @Override - public KeyValues getLowCardinalityKeyValues(AmqpMessageReceiverContext context) { - return this.lowCardinality == null - ? ObservationConvention.super.getLowCardinalityKeyValues(context) - : this.lowCardinality; + public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) { + return this.lowCardinality + .and(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), + context.getListenerId()); } @Override - public KeyValues getHighCardinalityKeyValues(AmqpMessageReceiverContext context) { - return this.highCardinality == null - ? ObservationConvention.super.getHighCardinalityKeyValues(context) - : this.highCardinality; + public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext context) { + return this.highCardinality + .and(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), + context.getListenerId()); } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java similarity index 77% rename from spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageReceiverContext.java rename to spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index e4c16ecde6..e3dba4da29 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -27,11 +27,18 @@ * @since 2.8 * */ -public class AmqpMessageReceiverContext extends ReceiverContext { +public class RabbitMessageReceiverContext extends ReceiverContext { - public AmqpMessageReceiverContext(Message message) { + private final String listenerId; + + public RabbitMessageReceiverContext(Message message, String listenerId) { super((carrier, key) -> carrier.getMessageProperties().getHeader(key)); setCarrier(message); + this.listenerId = listenerId; + } + + public String getListenerId() { + return this.listenerId; } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java similarity index 79% rename from spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageSenderContext.java rename to spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java index 4f15d95574..016ab77640 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/AmqpMessageSenderContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java @@ -27,12 +27,18 @@ * @since 3.0 * */ -public class AmqpMessageSenderContext extends SenderContext { +public class RabbitMessageSenderContext extends SenderContext { - public AmqpMessageSenderContext(Message message) { + private final String beanName; + + public RabbitMessageSenderContext(Message message, String beanName) { super((carrier, key, value) -> message.getMessageProperties().setHeader(key, value)); setCarrier(message); + this.beanName = beanName; + } + public String getBeanName() { + return this.beanName; } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index 336575531e..1352a03c21 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -31,7 +31,7 @@ * @since 3.0 * */ -public class RabbitTemplateObservationConvention implements ObservationConvention { +public class RabbitTemplateObservationConvention implements ObservationConvention { @Nullable private final KeyValues lowCardinality; @@ -47,27 +47,27 @@ public class RabbitTemplateObservationConvention implements ObservationConventio public RabbitTemplateObservationConvention(@Nullable KeyValues lowCardinality, @Nullable KeyValues highCardinality) { - this.lowCardinality = lowCardinality; - this.highCardinality = highCardinality; + this.lowCardinality = lowCardinality != null ? KeyValues.of(lowCardinality) : KeyValues.empty(); + this.highCardinality = highCardinality != null ? KeyValues.of(highCardinality) : KeyValues.empty(); } @Override public boolean supportsContext(Context context) { - return context instanceof AmqpMessageSenderContext; + return context instanceof RabbitMessageSenderContext; } @Override - public KeyValues getLowCardinalityKeyValues(AmqpMessageSenderContext context) { - return this.lowCardinality == null - ? ObservationConvention.super.getLowCardinalityKeyValues(context) - : this.lowCardinality; + public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { + return this.lowCardinality + .and(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + context.getBeanName()); } @Override - public KeyValues getHighCardinalityKeyValues(AmqpMessageSenderContext context) { - return this.highCardinality == null - ? ObservationConvention.super.getHighCardinalityKeyValues(context) - : this.highCardinality; + public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) { + return this.highCardinality + .and(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + context.getBeanName()); } } From 355f5db810fbf68fd13f623896f2f2e071f89577 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 15:08:32 -0400 Subject: [PATCH 04/31] Javadoc polishing. --- .../rabbit/listener/AbstractMessageListenerContainer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 0db816aaaf..55010cc995 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -1161,18 +1161,22 @@ public void setMicrometerTags(Map tags) { } /** - * Set to false to disable micrometer listener timers. + * Set to false to disable micrometer listener timers. When true, ignored + * if {@link #setObservationEnabled(boolean)} is set to true. * @param micrometerEnabled false to disable. * @since 2.2 + * @see #setObservationEnabled(boolean) */ public void setMicrometerEnabled(boolean micrometerEnabled) { this.micrometerEnabled = micrometerEnabled; } /** - * Enable observation via micrometer. + * Enable observation via micrometer; disables basic Micrometer timers enabled + * by {@link #setMicrometerEnabled(boolean)}. * @param observationEnabled true to enable. * @since 3.0 + * @see #setMicrometerEnabled(boolean) */ public void setObservationEnabled(boolean observationEnabled) { this.observationEnabled = observationEnabled; From b39a5f967d0ff91651741620857b3e18f1fd692e Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 15:42:24 -0400 Subject: [PATCH 05/31] Don't add default KV to high-card KVs. --- .../micrometer/RabbitListenerObservationConvention.java | 4 +--- .../micrometer/RabbitTemplateObservationConvention.java | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index f1f18dd19e..5e8ed7d194 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -63,9 +63,7 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context @Override public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext context) { - return this.highCardinality - .and(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), - context.getListenerId()); + return KeyValues.of(this.highCardinality); } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index 1352a03c21..cbfdb8044b 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -58,9 +58,7 @@ public boolean supportsContext(Context context) { @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { - return this.lowCardinality - .and(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), - context.getBeanName()); + return KeyValues.of(this.lowCardinality); } @Override From 393ed51887bbada4b12f48b790b7e7a61f66c797 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 15:53:40 -0400 Subject: [PATCH 06/31] Fix previous commit. --- .../micrometer/RabbitTemplateObservationConvention.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index cbfdb8044b..cf3b88f6bc 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -58,14 +58,14 @@ public boolean supportsContext(Context context) { @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { - return KeyValues.of(this.lowCardinality); + return this.lowCardinality + .and(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + context.getBeanName()); } @Override public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) { - return this.highCardinality - .and(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), - context.getBeanName()); + return KeyValues.of(this.highCardinality); } } From c282d46e775d27d1f4522325c5175f08319b47bc Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 16:42:04 -0400 Subject: [PATCH 07/31] Fix contextual name (receiver side). --- .../listener/AbstractMessageListenerContainer.java | 4 +++- .../micrometer/RabbitMessageReceiverContext.java | 10 +++++++++- .../rabbit/support/micrometer/ObservationTests.java | 6 ++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 55010cc995..2c6ee6e0cc 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -1540,8 +1540,10 @@ protected void executeListener(Channel channel, Object data) { observation = Observation.NOOP; } else { + Message message = (Message) data; observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(registry, - new RabbitMessageReceiverContext((Message) data, getListenerId())) + new RabbitMessageReceiverContext(message, getListenerId(), + message.getMessageProperties().getConsumerQueue())) .observationConvention(this.observationConvention); } observation.observe(() -> executeListenerAndHandleException(channel, data)); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index e3dba4da29..12b453e169 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -31,14 +31,22 @@ public class RabbitMessageReceiverContext extends ReceiverContext { private final String listenerId; - public RabbitMessageReceiverContext(Message message, String listenerId) { + private final String queue; + + public RabbitMessageReceiverContext(Message message, String listenerId, String queue) { super((carrier, key) -> carrier.getMessageProperties().getHeader(key)); setCarrier(message); this.listenerId = listenerId; + this.queue = queue; } public String getListenerId() { return this.listenerId; } + @Override + public String getContextualName() { + return queue + " receive"; + } + } diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index bcca918792..57fcadba74 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -80,12 +80,14 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf(Map.of("listener.id", "obs1", "foo", "some foo value", "bar", "some bar value")); + assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); span = spans.poll(); assertThat(span.getTags()).containsEntry("bean.name", "template"); span = spans.poll(); assertThat(span.getTags()) - .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); } - + .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); + assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); + } @Configuration @EnableRabbit From c5b29750e8630d460e272f7086b7346c39c81a7e Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 16:49:17 -0400 Subject: [PATCH 08/31] Fix checkstyle. --- .../rabbit/support/micrometer/RabbitMessageReceiverContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 12b453e169..1a02f764c1 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -46,7 +46,7 @@ public String getListenerId() { @Override public String getContextualName() { - return queue + " receive"; + return this.queue + " receive"; } } From 9de2da2feff3865fb5567f59dbb4c71d22d4b86f Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 16:56:45 -0400 Subject: [PATCH 09/31] Polish previous commit. --- .../rabbit/listener/AbstractMessageListenerContainer.java | 3 +-- .../support/micrometer/RabbitMessageReceiverContext.java | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 2c6ee6e0cc..b595f7f0d2 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -1542,8 +1542,7 @@ protected void executeListener(Channel channel, Object data) { else { Message message = (Message) data; observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(registry, - new RabbitMessageReceiverContext(message, getListenerId(), - message.getMessageProperties().getConsumerQueue())) + new RabbitMessageReceiverContext(message, getListenerId())) .observationConvention(this.observationConvention); } observation.observe(() -> executeListenerAndHandleException(channel, data)); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 1a02f764c1..8f796c719a 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -31,13 +31,13 @@ public class RabbitMessageReceiverContext extends ReceiverContext { private final String listenerId; - private final String queue; + private final Message message; - public RabbitMessageReceiverContext(Message message, String listenerId, String queue) { + public RabbitMessageReceiverContext(Message message, String listenerId) { super((carrier, key) -> carrier.getMessageProperties().getHeader(key)); setCarrier(message); + this.message = message; this.listenerId = listenerId; - this.queue = queue; } public String getListenerId() { @@ -46,7 +46,7 @@ public String getListenerId() { @Override public String getContextualName() { - return this.queue + " receive"; + return message.getMessageProperties().getConsumerQueue() + " receive"; } } From 55f04376ff6645f84bde7bf4b1136a7d32b2aed2 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 17:03:55 -0400 Subject: [PATCH 10/31] Fix contextual name (sender side) --- .../amqp/rabbit/core/RabbitTemplate.java | 2 +- .../support/micrometer/RabbitMessageSenderContext.java | 10 +++++++++- .../rabbit/support/micrometer/ObservationTests.java | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index 380c4c0066..1b4d347100 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -2439,7 +2439,7 @@ protected void observeTheSend(Channel channel, Message message, boolean mandator } else { observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(registry, - new RabbitMessageSenderContext(message, this.beanName)) + new RabbitMessageSenderContext(message, this.beanName, exch + "/" + rKey)) .observationConvention(this.observationConvention); } observation.observe(() -> sendToRabbit(channel, exch, rKey, mandatory, message)); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java index 016ab77640..78ac037b43 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java @@ -31,14 +31,22 @@ public class RabbitMessageSenderContext extends SenderContext { private final String beanName; - public RabbitMessageSenderContext(Message message, String beanName) { + private final String destination; + + public RabbitMessageSenderContext(Message message, String beanName, String destination) { super((carrier, key, value) -> message.getMessageProperties().setHeader(key, value)); setCarrier(message); this.beanName = beanName; + this.destination = destination; } public String getBeanName() { return this.beanName; } + @Override + public String getContextualName() { + return this.destination + " send"; + } + } diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 57fcadba74..bca5d20ec2 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -77,12 +77,14 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, assertThat(spans).hasSize(4); SimpleSpan span = spans.poll(); assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf(Map.of("listener.id", "obs1", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); span = spans.poll(); assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); From 307926859803aba15e5ad659dc2f53bc789d19d5 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 17:10:04 -0400 Subject: [PATCH 11/31] Remove contextual names from observations. --- .../rabbit/support/micrometer/RabbitListenerObservation.java | 5 ----- .../rabbit/support/micrometer/RabbitTemplateObservation.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java index d561334d27..4a09549cdf 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java @@ -38,11 +38,6 @@ public String getName() { return "spring.rabbit.listener"; } - @Override - public String getContextualName() { - return "RabbitListener Observation"; - } - @Override public KeyName[] getLowCardinalityKeyNames() { return ListenerLowCardinalityTags.values(); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java index 8eee84396e..da3db55cdf 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java @@ -38,11 +38,6 @@ public String getName() { return "spring.rabbit.template"; } - @Override - public String getContextualName() { - return "RabbitTemplate Observation"; - } - @Override public KeyName[] getLowCardinalityKeyNames() { return TemplateLowCardinalityTags.values(); From 0cf3e552fd64c4bc49254274808402adf51f3a6f Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 17:20:00 -0400 Subject: [PATCH 12/31] Fix checkstyle. --- .../rabbit/support/micrometer/RabbitMessageReceiverContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 8f796c719a..0a4a73b138 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -46,7 +46,7 @@ public String getListenerId() { @Override public String getContextualName() { - return message.getMessageProperties().getConsumerQueue() + " receive"; + return this.message.getMessageProperties().getConsumerQueue() + " receive"; } } From d929b0fb821c267306e7be2357fb1c8c0b078ca7 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 7 Sep 2022 17:29:01 -0400 Subject: [PATCH 13/31] Remove customization of KeyValues from conventions. --- .../amqp/rabbit/core/RabbitTemplate.java | 3 +- .../AbstractMessageListenerContainer.java | 3 +- .../RabbitListenerObservationConvention.java | 27 ++--------------- .../RabbitTemplateObservationConvention.java | 29 ++----------------- 4 files changed, 8 insertions(+), 54 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index 1b4d347100..895058aae3 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -271,8 +271,7 @@ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count private boolean observationEnabled; - private RabbitTemplateObservationConvention observationConvention = - new RabbitTemplateObservationConvention(null, null); + private RabbitTemplateObservationConvention observationConvention = new RabbitTemplateObservationConvention(); private volatile boolean usingFastReplyTo; diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index b595f7f0d2..d13c737a64 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -261,8 +261,7 @@ public abstract class AbstractMessageListenerContainer extends RabbitAccessor private MessageAckListener messageAckListener = (success, deliveryTag, cause) -> { }; - private RabbitListenerObservationConvention observationConvention = - new RabbitListenerObservationConvention(null, null); + private RabbitListenerObservationConvention observationConvention = new RabbitListenerObservationConvention(); @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index 5e8ed7d194..4049875d48 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -16,16 +16,12 @@ package org.springframework.amqp.rabbit.support.micrometer; -import org.springframework.lang.Nullable; - -import io.micrometer.common.KeyValue; import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation.Context; import io.micrometer.observation.Observation.ObservationConvention; /** - * {@link ObservationConvention} for Rabbit listener key values. Allows users - * to add {@link KeyValue}s to the observations. + * {@link ObservationConvention} for Rabbit listener key values. * * @author Gary Russell * @since 3.0 @@ -33,22 +29,6 @@ */ public class RabbitListenerObservationConvention implements ObservationConvention { - private final KeyValues lowCardinality; - - private final KeyValues highCardinality; - - /** - * Create an instance with the provided {@link KeyValues}. - * @param lowCardinality the low cardinality {@link KeyValues}. - * @param highCardinality the high cardinality {@link KeyValues}. - */ - public RabbitListenerObservationConvention(@Nullable KeyValues lowCardinality, - @Nullable KeyValues highCardinality) { - - this.lowCardinality = lowCardinality != null ? KeyValues.of(lowCardinality) : KeyValues.empty(); - this.highCardinality = highCardinality != null ? KeyValues.of(highCardinality) : KeyValues.empty(); - } - @Override public boolean supportsContext(Context context) { return context instanceof RabbitMessageReceiverContext; @@ -56,14 +36,13 @@ public boolean supportsContext(Context context) { @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) { - return this.lowCardinality - .and(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), + return KeyValues.of(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), context.getListenerId()); } @Override public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext context) { - return KeyValues.of(this.highCardinality); + return KeyValues.empty(); } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index cf3b88f6bc..a33ef27f21 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -16,16 +16,12 @@ package org.springframework.amqp.rabbit.support.micrometer; -import org.springframework.lang.Nullable; - -import io.micrometer.common.KeyValue; import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation.Context; import io.micrometer.observation.Observation.ObservationConvention; /** - * {@link ObservationConvention} for Rabbit template key values. Allows users - * to add {@link KeyValue}s to the observations. + * {@link ObservationConvention} for Rabbit template key values. * * @author Gary Russell * @since 3.0 @@ -33,24 +29,6 @@ */ public class RabbitTemplateObservationConvention implements ObservationConvention { - @Nullable - private final KeyValues lowCardinality; - - @Nullable - private final KeyValues highCardinality; - - /** - * Create an instance with the provided {@link KeyValues}. - * @param lowCardinality the low cardinality {@link KeyValues}. - * @param highCardinality the high cardinality {@link KeyValues}. - */ - public RabbitTemplateObservationConvention(@Nullable KeyValues lowCardinality, - @Nullable KeyValues highCardinality) { - - this.lowCardinality = lowCardinality != null ? KeyValues.of(lowCardinality) : KeyValues.empty(); - this.highCardinality = highCardinality != null ? KeyValues.of(highCardinality) : KeyValues.empty(); - } - @Override public boolean supportsContext(Context context) { return context instanceof RabbitMessageSenderContext; @@ -58,14 +36,13 @@ public boolean supportsContext(Context context) { @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { - return this.lowCardinality - .and(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + return KeyValues.of(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), context.getBeanName()); } @Override public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) { - return KeyValues.of(this.highCardinality); + return KeyValues.empty(); } } From b1bd02358837f3407cfbdfcebb6880506935bd82 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 13 Sep 2022 11:50:44 -0400 Subject: [PATCH 14/31] Add `getDefaultConvention()` to observations. --- .../support/micrometer/RabbitListenerObservation.java | 10 +++++++++- .../RabbitListenerObservationConvention.java | 2 +- .../support/micrometer/RabbitTemplateObservation.java | 9 ++++++++- .../RabbitTemplateObservationConvention.java | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java index 4a09549cdf..ec7a0d8379 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java @@ -17,6 +17,8 @@ package org.springframework.amqp.rabbit.support.micrometer; import io.micrometer.common.docs.KeyName; +import io.micrometer.observation.Observation.Context; +import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.docs.DocumentedObservation; /** @@ -33,8 +35,14 @@ public enum RabbitListenerObservation implements DocumentedObservation { */ LISTENER_OBSERVATION { + + @Override + public Class> getDefaultConvention() { + return RabbitListenerObservationConvention.class; + } + @Override - public String getName() { + public String getPrefix() { return "spring.rabbit.listener"; } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index 4049875d48..c67b6cb598 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -18,7 +18,7 @@ import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation.Context; -import io.micrometer.observation.Observation.ObservationConvention; +import io.micrometer.observation.ObservationConvention; /** * {@link ObservationConvention} for Rabbit listener key values. diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java index da3db55cdf..dc344f0bf6 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java @@ -17,6 +17,8 @@ package org.springframework.amqp.rabbit.support.micrometer; import io.micrometer.common.docs.KeyName; +import io.micrometer.observation.Observation.Context; +import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.docs.DocumentedObservation; /** @@ -34,7 +36,12 @@ public enum RabbitTemplateObservation implements DocumentedObservation { TEMPLATE_OBSERVATION { @Override - public String getName() { + public Class> getDefaultConvention() { + return RabbitTemplateObservationConvention.class; + } + + @Override + public String getPrefix() { return "spring.rabbit.template"; } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index a33ef27f21..eea344792d 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -18,7 +18,7 @@ import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation.Context; -import io.micrometer.observation.Observation.ObservationConvention; +import io.micrometer.observation.ObservationConvention; /** * {@link ObservationConvention} for Rabbit template key values. From be4be28afa97999993c6606557576484168d732f Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 13 Sep 2022 13:21:52 -0400 Subject: [PATCH 15/31] Fix since 3.0. --- .../rabbit/support/micrometer/RabbitMessageReceiverContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 0a4a73b138..47f9f63f1f 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -24,7 +24,7 @@ * {@link ReceiverContext} for {@link Message}s. * * @author Gary Russell - * @since 2.8 + * @since 3.0 * */ public class RabbitMessageReceiverContext extends ReceiverContext { From d422412a54039ff1149e80d954b9fefd49741609 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 09:30:48 -0400 Subject: [PATCH 16/31] Support wider convention customization. --- .../amqp/rabbit/core/RabbitTemplate.java | 14 +++-- .../AbstractMessageListenerContainer.java | 13 ++-- .../RabbitListenerObservationConvention.java | 5 ++ .../RabbitTemplateObservationConvention.java | 5 ++ .../support/micrometer/ObservationTests.java | 61 +++++++++++++++++-- 5 files changed, 81 insertions(+), 17 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index 895058aae3..c7de54877d 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -114,6 +114,7 @@ import com.rabbitmq.client.ShutdownListener; import com.rabbitmq.client.ShutdownSignalException; import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.ObservationRegistry; /** @@ -271,7 +272,8 @@ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count private boolean observationEnabled; - private RabbitTemplateObservationConvention observationConvention = new RabbitTemplateObservationConvention(); + @Nullable + private ObservationConvention observationConvention; private volatile boolean usingFastReplyTo; @@ -331,8 +333,7 @@ public void setObservationEnabled(boolean observationEnabled) { * @param observationConvention the convention. * @since 3.0 */ - public void setObservationConvention(RabbitTemplateObservationConvention observationConvention) { - Assert.notNull(observationConvention, "'observationConvention' cannot be null"); + public void setObservationConvention(ObservationConvention observationConvention) { this.observationConvention = observationConvention; } @@ -2437,9 +2438,10 @@ protected void observeTheSend(Channel channel, Message message, boolean mandator observation = Observation.NOOP; } else { - observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(registry, - new RabbitMessageSenderContext(message, this.beanName, exch + "/" + rKey)) - .observationConvention(this.observationConvention); + observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(this.observationConvention, + RabbitTemplateObservationConvention.INSTANCE, + new RabbitMessageSenderContext(message, this.beanName, exch + "/" + rKey), registry); + } observation.observe(() -> sendToRabbit(channel, exch, rKey, mandatory, message)); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index d13c737a64..8fc4ebe7c0 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -95,6 +95,7 @@ import com.rabbitmq.client.Channel; import com.rabbitmq.client.ShutdownSignalException; import io.micrometer.observation.Observation; +import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.ObservationRegistry; /** @@ -261,7 +262,8 @@ public abstract class AbstractMessageListenerContainer extends RabbitAccessor private MessageAckListener messageAckListener = (success, deliveryTag, cause) -> { }; - private RabbitListenerObservationConvention observationConvention = new RabbitListenerObservationConvention(); + @Nullable + private ObservationConvention observationConvention; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { @@ -1186,8 +1188,7 @@ public void setObservationEnabled(boolean observationEnabled) { * @param observationConvention the convention. * @since 3.0 */ - public void setObservationConvention(RabbitListenerObservationConvention observationConvention) { - Assert.notNull(observationConvention, "'observationConvention' cannot be null"); + public void setObservationConvention(ObservationConvention observationConvention) { this.observationConvention = observationConvention; } @@ -1540,9 +1541,9 @@ protected void executeListener(Channel channel, Object data) { } else { Message message = (Message) data; - observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(registry, - new RabbitMessageReceiverContext(message, getListenerId())) - .observationConvention(this.observationConvention); + observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(this.observationConvention, + RabbitListenerObservationConvention.INSTANCE, + new RabbitMessageReceiverContext(message, getListenerId()), registry); } observation.observe(() -> executeListenerAndHandleException(channel, data)); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index c67b6cb598..a20aac90a6 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -29,6 +29,11 @@ */ public class RabbitListenerObservationConvention implements ObservationConvention { + /** + * A singleton instance of the convention. + */ + public static RabbitListenerObservationConvention INSTANCE = new RabbitListenerObservationConvention(); + @Override public boolean supportsContext(Context context) { return context instanceof RabbitMessageReceiverContext; diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index eea344792d..60261a94a3 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -29,6 +29,11 @@ */ public class RabbitTemplateObservationConvention implements ObservationConvention { + /** + * A singleton instance of the convention. + */ + public static RabbitTemplateObservationConvention INSTANCE = new RabbitTemplateObservationConvention(); + @Override public boolean supportsContext(Context context) { return context instanceof RabbitMessageSenderContext; diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index bca5d20ec2..94aca5b907 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -35,12 +35,15 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.junit.RabbitAvailable; import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition; +import org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer; +import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import io.micrometer.common.KeyValues; import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; @@ -65,10 +68,11 @@ public class ObservationTests { @Test void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, - @Autowired SimpleTracer tracer) throws InterruptedException { + @Autowired SimpleTracer tracer, @Autowired RabbitListenerEndpointRegistry rler) + throws InterruptedException { template.convertAndSend("observation.testQ1", "test"); - assertThat(listener.latch.await(10, TimeUnit.SECONDS)).isTrue(); + assertThat(listener.latch1.await(10, TimeUnit.SECONDS)).isTrue(); assertThat(listener.message) .extracting(msg -> msg.getMessageProperties().getHeaders()) .hasFieldOrPropertyWithValue("foo", "some foo value") @@ -89,6 +93,50 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, assertThat(span.getTags()) .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); + template.setObservationConvention(new RabbitTemplateObservationConvention() { + + @Override + public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { + return super.getLowCardinalityKeyValues(context).and("foo", "bar"); + } + + }); + ((AbstractMessageListenerContainer) rler.getListenerContainer("obs1")).setObservationConvention( + new RabbitListenerObservationConvention() { + + @Override + public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) { + return super.getLowCardinalityKeyValues(context).and("baz", "qux"); + } + + }); + rler.getListenerContainer("obs1").stop(); + rler.getListenerContainer("obs1").start(); + template.convertAndSend("observation.testQ1", "test"); + assertThat(listener.latch1.await(10, TimeUnit.SECONDS)).isTrue(); + assertThat(listener.message) + .extracting(msg -> msg.getMessageProperties().getHeaders()) + .hasFieldOrPropertyWithValue("foo", "some foo value") + .hasFieldOrPropertyWithValue("bar", "some bar value"); + assertThat(spans).hasSize(8); + span = spans.poll(); + assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getTags()).containsEntry("foo", "bar"); + assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); + span = spans.poll(); + assertThat(span.getTags()) + .containsAllEntriesOf(Map.of("listener.id", "obs1", "foo", "some foo value", "bar", "some bar value", + "baz", "qux")); + assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); + span = spans.poll(); + assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getTags()).containsEntry("foo", "bar"); + assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); + span = spans.poll(); + assertThat(span.getTags()) + .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); + assertThat(span.getTags()).doesNotContainEntry("baz", "qux"); + assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); } @Configuration @@ -175,7 +223,9 @@ public static class Listener { private final RabbitTemplate template; - final CountDownLatch latch = new CountDownLatch(1); + final CountDownLatch latch1 = new CountDownLatch(1); + + final CountDownLatch latch2 = new CountDownLatch(2); volatile Message message; @@ -185,13 +235,14 @@ public Listener(RabbitTemplate template) { @RabbitListener(id = "obs1", queues = "observation.testQ1") void listen1(Message in) { - template.send("observation.testQ2", in); + this.template.send("observation.testQ2", in); } @RabbitListener(id = "obs2", queues = "observation.testQ2") void listen2(Message in) { this.message = in; - latch.countDown(); + this.latch1.countDown(); + this.latch2.countDown(); } } From d762752b19415c89309f975eb1ee055499b9974e Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 10:41:48 -0400 Subject: [PATCH 17/31] Convention type safety. --- .../amqp/rabbit/core/RabbitTemplate.java | 8 ++-- .../AbstractMessageListenerContainer.java | 8 ++-- ...ltRabbitListenerObservationConvention.java | 47 +++++++++++++++++++ ...ltRabbitTemplateObservationConvention.java | 46 ++++++++++++++++++ .../micrometer/RabbitListenerObservation.java | 2 +- .../RabbitListenerObservationConvention.java | 21 +-------- .../micrometer/RabbitTemplateObservation.java | 2 +- .../RabbitTemplateObservationConvention.java | 21 +-------- .../support/micrometer/ObservationTests.java | 4 +- 9 files changed, 109 insertions(+), 50 deletions(-) create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java index c7de54877d..2bbd7b98c5 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitTemplate.java @@ -74,6 +74,7 @@ import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; import org.springframework.amqp.rabbit.support.ValueExpression; +import org.springframework.amqp.rabbit.support.micrometer.DefaultRabbitTemplateObservationConvention; import org.springframework.amqp.rabbit.support.micrometer.RabbitMessageSenderContext; import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservation; import org.springframework.amqp.rabbit.support.micrometer.RabbitTemplateObservationConvention; @@ -114,7 +115,6 @@ import com.rabbitmq.client.ShutdownListener; import com.rabbitmq.client.ShutdownSignalException; import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.ObservationRegistry; /** @@ -273,7 +273,7 @@ public class RabbitTemplate extends RabbitAccessor // NOSONAR type line count private boolean observationEnabled; @Nullable - private ObservationConvention observationConvention; + private RabbitTemplateObservationConvention observationConvention; private volatile boolean usingFastReplyTo; @@ -333,7 +333,7 @@ public void setObservationEnabled(boolean observationEnabled) { * @param observationConvention the convention. * @since 3.0 */ - public void setObservationConvention(ObservationConvention observationConvention) { + public void setObservationConvention(RabbitTemplateObservationConvention observationConvention) { this.observationConvention = observationConvention; } @@ -2439,7 +2439,7 @@ protected void observeTheSend(Channel channel, Message message, boolean mandator } else { observation = RabbitTemplateObservation.TEMPLATE_OBSERVATION.observation(this.observationConvention, - RabbitTemplateObservationConvention.INSTANCE, + DefaultRabbitTemplateObservationConvention.INSTANCE, new RabbitMessageSenderContext(message, this.beanName, exch + "/" + rKey), registry); } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index 8fc4ebe7c0..e96633e56c 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -63,6 +63,7 @@ import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter; import org.springframework.amqp.rabbit.support.ListenerExecutionFailedException; import org.springframework.amqp.rabbit.support.MessagePropertiesConverter; +import org.springframework.amqp.rabbit.support.micrometer.DefaultRabbitListenerObservationConvention; import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservation; import org.springframework.amqp.rabbit.support.micrometer.RabbitListenerObservationConvention; import org.springframework.amqp.rabbit.support.micrometer.RabbitMessageReceiverContext; @@ -95,7 +96,6 @@ import com.rabbitmq.client.Channel; import com.rabbitmq.client.ShutdownSignalException; import io.micrometer.observation.Observation; -import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.ObservationRegistry; /** @@ -263,7 +263,7 @@ public abstract class AbstractMessageListenerContainer extends RabbitAccessor private MessageAckListener messageAckListener = (success, deliveryTag, cause) -> { }; @Nullable - private ObservationConvention observationConvention; + private RabbitListenerObservationConvention observationConvention; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { @@ -1188,7 +1188,7 @@ public void setObservationEnabled(boolean observationEnabled) { * @param observationConvention the convention. * @since 3.0 */ - public void setObservationConvention(ObservationConvention observationConvention) { + public void setObservationConvention(RabbitListenerObservationConvention observationConvention) { this.observationConvention = observationConvention; } @@ -1542,7 +1542,7 @@ protected void executeListener(Channel channel, Object data) { else { Message message = (Message) data; observation = RabbitListenerObservation.LISTENER_OBSERVATION.observation(this.observationConvention, - RabbitListenerObservationConvention.INSTANCE, + DefaultRabbitListenerObservationConvention.INSTANCE, new RabbitMessageReceiverContext(message, getListenerId()), registry); } observation.observe(() -> executeListenerAndHandleException(channel, data)); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java new file mode 100644 index 0000000000..614c49550c --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java @@ -0,0 +1,47 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import io.micrometer.common.KeyValues; + +/** + * Default {@link RabbitListenerObservationConvention} for Rabbit listener key values. + * + * @author Gary Russell + * @since 3.0 + * + */ +public class DefaultRabbitListenerObservationConvention implements RabbitListenerObservationConvention { + + /** + * A singleton instance of the convention. + */ + public static final DefaultRabbitListenerObservationConvention INSTANCE = + new DefaultRabbitListenerObservationConvention(); + + @Override + public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) { + return KeyValues.of(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), + context.getListenerId()); + } + + @Override + public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext context) { + return KeyValues.empty(); + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java new file mode 100644 index 0000000000..8f686d33e3 --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import io.micrometer.common.KeyValues; + +/** + * Default {@link RabbitTemplateObservationConvention} for Rabbit template key values. + * + * @author Gary Russell + * @since 3.0 + * + */ +public class DefaultRabbitTemplateObservationConvention implements RabbitTemplateObservationConvention { + + /** + * A singleton instance of the convention. + */ + public static DefaultRabbitTemplateObservationConvention INSTANCE = new DefaultRabbitTemplateObservationConvention(); + + @Override + public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { + return KeyValues.of(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), + context.getBeanName()); + } + + @Override + public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) { + return KeyValues.empty(); + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java index ec7a0d8379..271995e9fe 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java @@ -38,7 +38,7 @@ public enum RabbitListenerObservation implements DocumentedObservation { @Override public Class> getDefaultConvention() { - return RabbitListenerObservationConvention.class; + return DefaultRabbitListenerObservationConvention.class; } @Override diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index a20aac90a6..71e0d6d836 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -16,7 +16,6 @@ package org.springframework.amqp.rabbit.support.micrometer; -import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation.Context; import io.micrometer.observation.ObservationConvention; @@ -27,27 +26,11 @@ * @since 3.0 * */ -public class RabbitListenerObservationConvention implements ObservationConvention { - - /** - * A singleton instance of the convention. - */ - public static RabbitListenerObservationConvention INSTANCE = new RabbitListenerObservationConvention(); +public interface RabbitListenerObservationConvention extends ObservationConvention { @Override - public boolean supportsContext(Context context) { + default boolean supportsContext(Context context) { return context instanceof RabbitMessageReceiverContext; } - @Override - public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) { - return KeyValues.of(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), - context.getListenerId()); - } - - @Override - public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext context) { - return KeyValues.empty(); - } - } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java index dc344f0bf6..2a016692bf 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java @@ -37,7 +37,7 @@ public enum RabbitTemplateObservation implements DocumentedObservation { @Override public Class> getDefaultConvention() { - return RabbitTemplateObservationConvention.class; + return DefaultRabbitTemplateObservationConvention.class; } @Override diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index 60261a94a3..8d405b5182 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -16,7 +16,6 @@ package org.springframework.amqp.rabbit.support.micrometer; -import io.micrometer.common.KeyValues; import io.micrometer.observation.Observation.Context; import io.micrometer.observation.ObservationConvention; @@ -27,27 +26,11 @@ * @since 3.0 * */ -public class RabbitTemplateObservationConvention implements ObservationConvention { - - /** - * A singleton instance of the convention. - */ - public static RabbitTemplateObservationConvention INSTANCE = new RabbitTemplateObservationConvention(); +public interface RabbitTemplateObservationConvention extends ObservationConvention { @Override - public boolean supportsContext(Context context) { + default boolean supportsContext(Context context) { return context instanceof RabbitMessageSenderContext; } - @Override - public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { - return KeyValues.of(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(), - context.getBeanName()); - } - - @Override - public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) { - return KeyValues.empty(); - } - } diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 94aca5b907..a30b413d36 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -93,7 +93,7 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, assertThat(span.getTags()) .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); - template.setObservationConvention(new RabbitTemplateObservationConvention() { + template.setObservationConvention(new DefaultRabbitTemplateObservationConvention() { @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { @@ -102,7 +102,7 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) }); ((AbstractMessageListenerContainer) rler.getListenerContainer("obs1")).setObservationConvention( - new RabbitListenerObservationConvention() { + new DefaultRabbitListenerObservationConvention() { @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) { From f1ac117f3873f087f04e302038dbdbbaa057d72c Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 11:12:12 -0400 Subject: [PATCH 18/31] Fix Test - not sure why PR build succeeded. --- .../amqp/rabbit/support/micrometer/ObservationTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index a30b413d36..609c2487dd 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -113,12 +113,12 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context rler.getListenerContainer("obs1").stop(); rler.getListenerContainer("obs1").start(); template.convertAndSend("observation.testQ1", "test"); - assertThat(listener.latch1.await(10, TimeUnit.SECONDS)).isTrue(); + assertThat(listener.latch2.await(10, TimeUnit.SECONDS)).isTrue(); assertThat(listener.message) .extracting(msg -> msg.getMessageProperties().getHeaders()) .hasFieldOrPropertyWithValue("foo", "some foo value") .hasFieldOrPropertyWithValue("bar", "some bar value"); - assertThat(spans).hasSize(8); + assertThat(spans).hasSize(4); span = spans.poll(); assertThat(span.getTags()).containsEntry("bean.name", "template"); assertThat(span.getTags()).containsEntry("foo", "bar"); From 03ccb331f60a2b49bdc08dfb559373057c302e45 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 11:41:08 -0400 Subject: [PATCH 19/31] Add Meters to ObservationTests. --- .../RabbitMessageReceiverContext.java | 5 ++++ .../RabbitMessageSenderContext.java | 5 ++++ .../support/micrometer/ObservationTests.java | 25 ++++++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 47f9f63f1f..0f5fd2fc7a 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -44,6 +44,11 @@ public String getListenerId() { return this.listenerId; } + @Override + public String getName() { + return "spring.rabbit.listener"; + } + @Override public String getContextualName() { return this.message.getMessageProperties().getConsumerQueue() + " receive"; diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java index 78ac037b43..92c9bc8ce7 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java @@ -44,6 +44,11 @@ public String getBeanName() { return this.beanName; } + @Override + public String getName() { + return "spring.rabbit.template"; + } + @Override public String getContextualName() { return this.destination + " send"; diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 609c2487dd..09dd5ae958 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -44,6 +44,10 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import io.micrometer.common.KeyValues; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import io.micrometer.core.tck.MeterRegistryAssert; import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationRegistry; import io.micrometer.observation.tck.TestObservationRegistry; @@ -68,7 +72,8 @@ public class ObservationTests { @Test void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, - @Autowired SimpleTracer tracer, @Autowired RabbitListenerEndpointRegistry rler) + @Autowired SimpleTracer tracer, @Autowired RabbitListenerEndpointRegistry rler, + @Autowired MeterRegistry meterRegistry) throws InterruptedException { template.convertAndSend("observation.testQ1", "test"); @@ -137,6 +142,14 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getTags()).doesNotContainEntry("baz", "qux"); assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); + MeterRegistryAssert.assertThat(meterRegistry) + .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) + .hasTimerWithNameAndTags("spring.rabbit.template", + KeyValues.of("bean.name", "template", "foo", "bar")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) + .hasTimerWithNameAndTags("spring.rabbit.listener", + KeyValues.of("listener.id", "obs1", "baz","qux")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs2")); } @Configuration @@ -169,7 +182,12 @@ SimpleTracer simpleTracer() { } @Bean - ObservationRegistry observationRegistry(Tracer tracer, Propagator propagator) { + MeterRegistry meterRegistry() { + return new SimpleMeterRegistry(); + } + + @Bean + ObservationRegistry observationRegistry(Tracer tracer, Propagator propagator, MeterRegistry meterRegistry) { TestObservationRegistry observationRegistry = TestObservationRegistry.create(); observationRegistry.observationConfig().observationHandler( // Composite will pick the first matching handler @@ -179,7 +197,8 @@ ObservationRegistry observationRegistry(Tracer tracer, Propagator propagator) { // This is responsible for creating a span on the receiver side new PropagatingReceiverTracingObservationHandler<>(tracer, propagator), // This is responsible for creating a default span - new DefaultTracingObservationHandler(tracer))); + new DefaultTracingObservationHandler(tracer))) + .observationHandler(new DefaultMeterObservationHandler(meterRegistry)); return observationRegistry; } From 2557794f33d23e74110ccf8bdc5a65c8e18de6df Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 12:01:49 -0400 Subject: [PATCH 20/31] Fix checkstyle. --- .../amqp/rabbit/support/micrometer/ObservationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 09dd5ae958..432c1b2cfa 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -148,7 +148,7 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context KeyValues.of("bean.name", "template", "foo", "bar")) .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) .hasTimerWithNameAndTags("spring.rabbit.listener", - KeyValues.of("listener.id", "obs1", "baz","qux")) + KeyValues.of("listener.id", "obs1", "baz", "qux")) .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs2")); } From 970ba6cf5fd142b2c0760d67000b2302d31f7762 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 12:41:59 -0400 Subject: [PATCH 21/31] Make INSTANCE final. --- .../micrometer/DefaultRabbitTemplateObservationConvention.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java index 8f686d33e3..99d5b459dc 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java @@ -30,7 +30,8 @@ public class DefaultRabbitTemplateObservationConvention implements RabbitTemplat /** * A singleton instance of the convention. */ - public static DefaultRabbitTemplateObservationConvention INSTANCE = new DefaultRabbitTemplateObservationConvention(); + public static final DefaultRabbitTemplateObservationConvention INSTANCE = + new DefaultRabbitTemplateObservationConvention(); @Override public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) { From a528d09c6b27a335bb84038c485b1a02ce4dbe4c Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 14 Sep 2022 17:16:51 -0400 Subject: [PATCH 22/31] Add integration test. --- .../ObservationIntegrationTests.java | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java new file mode 100644 index 0000000000..cc4250cba3 --- /dev/null +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java @@ -0,0 +1,159 @@ +/* + * Copyright 2022 the original author or 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 + * + * https://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 org.springframework.amqp.rabbit.support.micrometer; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.junit.RabbitAvailable; +import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition; +import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; + +import io.micrometer.common.KeyValues; +import io.micrometer.core.tck.MeterRegistryAssert; +import io.micrometer.observation.ObservationRegistry; +import io.micrometer.tracing.Span.Kind; +import io.micrometer.tracing.exporter.FinishedSpan; +import io.micrometer.tracing.test.SampleTestRunner; +import io.micrometer.tracing.test.simple.SpanAssert; +import io.micrometer.tracing.test.simple.SpansAssert; + +/** + * @author Artem Bilan + * @author Gary Russell + * + * @since 3.0 + */ +@RabbitAvailable(queues = { "int.observation.testQ1", "int.observation.testQ2" }) +public class ObservationIntegrationTests extends SampleTestRunner { + + @Override + public TracingSetup[] getTracingSetup() { + return new TracingSetup[]{ TracingSetup.IN_MEMORY_BRAVE }; + } + + @Override + public SampleTestRunnerConsumer yourCode() { + // template -> listener -> template -> listener + return (bb, meterRegistry) -> { + ObservationRegistry observationRegistry = getObservationRegistry(); + try (AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext()) { + applicationContext.registerBean(ObservationRegistry.class, () -> observationRegistry); + applicationContext.register(Config.class); + applicationContext.refresh(); + RabbitListenerEndpointRegistry bean = applicationContext.getBean(RabbitListenerEndpointRegistry.class); + applicationContext.getBean(RabbitTemplate.class).convertAndSend("int.observation.testQ1", "test"); + assertThat(applicationContext.getBean(Listener.class).latch1.await(10, TimeUnit.SECONDS)).isTrue(); + } + + List finishedSpans = bb.getFinishedSpans(); + SpansAssert.assertThat(finishedSpans) + .haveSameTraceId() + .hasSize(4); + SpanAssert.assertThat(finishedSpans.get(0)) + .hasKindEqualTo(Kind.PRODUCER) + .hasTag("bean.name", "template"); + SpanAssert.assertThat(finishedSpans.get(1)) + .hasKindEqualTo(Kind.PRODUCER) + .hasTag("bean.name", "template"); + SpanAssert.assertThat(finishedSpans.get(2)) + .hasKindEqualTo(Kind.CONSUMER) + .hasTag("listener.id", "obs1"); + SpanAssert.assertThat(finishedSpans.get(3)) + .hasKindEqualTo(Kind.CONSUMER) + .hasTag("listener.id", "obs2"); + + MeterRegistryAssert.assertThat(getMeterRegistry()) + .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) + .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs2")); + }; + } + + + @Configuration + @EnableRabbit + public static class Config { + + @Bean + CachingConnectionFactory ccf() { + return new CachingConnectionFactory(RabbitAvailableCondition.getBrokerRunning().getConnectionFactory()); + } + + @Bean + RabbitTemplate template(CachingConnectionFactory ccf) { + RabbitTemplate template = new RabbitTemplate(ccf); + template.setObservationEnabled(true); + return template; + } + + @Bean + SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(CachingConnectionFactory ccf) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + factory.setConnectionFactory(ccf); + factory.setContainerCustomizer(container -> container.setObservationEnabled(true)); + return factory; + } + + @Bean + Listener listener(RabbitTemplate template) { + return new Listener(template); + } + + } + + public static class Listener { + + private final RabbitTemplate template; + + final CountDownLatch latch1 = new CountDownLatch(1); + + volatile Message message; + + public Listener(RabbitTemplate template) { + this.template = template; + } + + @RabbitListener(id = "obs1", queues = "int.observation.testQ1") + void listen1(Message in) { + this.template.convertAndSend("int.observation.testQ2", in); + } + + @RabbitListener(id = "obs2", queues = "int.observation.testQ2") + void listen2(Message in) { + this.message = in; + this.latch1.countDown(); + } + + } + + +} From 1927f3bfe47321d607ae66c8c9c2e6a63120110e Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Sep 2022 08:59:23 -0400 Subject: [PATCH 23/31] Test all available integrations. --- .../support/micrometer/ObservationIntegrationTests.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java index cc4250cba3..f980dbb940 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java @@ -53,11 +53,6 @@ @RabbitAvailable(queues = { "int.observation.testQ1", "int.observation.testQ2" }) public class ObservationIntegrationTests extends SampleTestRunner { - @Override - public TracingSetup[] getTracingSetup() { - return new TracingSetup[]{ TracingSetup.IN_MEMORY_BRAVE }; - } - @Override public SampleTestRunnerConsumer yourCode() { // template -> listener -> template -> listener From cb6ba4c9e1135811d37e5b32ecfced4ac4f1b3f4 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Sep 2022 09:44:17 -0400 Subject: [PATCH 24/31] Remove redundant test code. --- .../rabbit/support/micrometer/ObservationIntegrationTests.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java index f980dbb940..95ebc35135 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java @@ -29,7 +29,6 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.junit.RabbitAvailable; import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition; -import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -62,7 +61,6 @@ public SampleTestRunnerConsumer yourCode() { applicationContext.registerBean(ObservationRegistry.class, () -> observationRegistry); applicationContext.register(Config.class); applicationContext.refresh(); - RabbitListenerEndpointRegistry bean = applicationContext.getBean(RabbitListenerEndpointRegistry.class); applicationContext.getBean(RabbitTemplate.class).convertAndSend("int.observation.testQ1", "test"); assertThat(applicationContext.getBean(Listener.class).latch1.await(10, TimeUnit.SECONDS)).isTrue(); } @@ -88,7 +86,6 @@ public SampleTestRunnerConsumer yourCode() { .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) - .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs2")); }; } From d0b758e319f6fca3e5c9317bfebf87a53115f311 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Sep 2022 10:36:26 -0400 Subject: [PATCH 25/31] Move getContextualName to conventions. --- .../DefaultRabbitListenerObservationConvention.java | 5 +++++ .../DefaultRabbitTemplateObservationConvention.java | 5 +++++ .../support/micrometer/RabbitMessageReceiverContext.java | 9 ++++++--- .../support/micrometer/RabbitMessageSenderContext.java | 9 ++++++--- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java index 614c49550c..82b84ba8cf 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java @@ -44,4 +44,9 @@ public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext contex return KeyValues.empty(); } + @Override + public String getContextualName(RabbitMessageReceiverContext context) { + return context.getSource() + " receive"; + } + } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java index 99d5b459dc..38394a0927 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java @@ -44,4 +44,9 @@ public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) return KeyValues.empty(); } + @Override + public String getContextualName(RabbitMessageSenderContext context) { + return context.getDestination() + " send"; + } + } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 0f5fd2fc7a..7bfa91f58a 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -49,9 +49,12 @@ public String getName() { return "spring.rabbit.listener"; } - @Override - public String getContextualName() { - return this.message.getMessageProperties().getConsumerQueue() + " receive"; + /** + * Return the source (queue) for this message. + * @return the source. + */ + public String getSource() { + return this.message.getMessageProperties().getConsumerQueue(); } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java index 92c9bc8ce7..cde103cb4b 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java @@ -49,9 +49,12 @@ public String getName() { return "spring.rabbit.template"; } - @Override - public String getContextualName() { - return this.destination + " send"; + /** + * Return the destination - {@code exchange/routingKey}. + * @return the destination. + */ + public String getDestination() { + return this.destination; } } From 00432802c48fc1526cb4754432468db21d47991b Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Sep 2022 13:21:19 -0400 Subject: [PATCH 26/31] Add docs. --- src/reference/asciidoc/amqp.adoc | 17 +++++++++++++++++ src/reference/asciidoc/whats-new.adoc | 5 +++++ 2 files changed, 22 insertions(+) diff --git a/src/reference/asciidoc/amqp.adoc b/src/reference/asciidoc/amqp.adoc index dc011b1666..0b2f684f72 100644 --- a/src/reference/asciidoc/amqp.adoc +++ b/src/reference/asciidoc/amqp.adoc @@ -3764,6 +3764,23 @@ The timers are named `spring.rabbitmq.listener` and have the following tags: You can add additional tags using the `micrometerTags` container property. +Also see <>. + +[[observation]] +===== Micrometer Observation + +Using Micrometer for observation is now supported, since version 3.0, for the `RabbitTemplate` and listener containers. + +Set `observationEnabled` on each component to enable observation; this will disable <> because the timers will now be managed with each observation. + +Refer to https://micrometer.io/docs/tracing#_running_integration_tests[Micrometer Tracing] for more information. + +To add tags to timers/traces, configure a custom `RabbitTemplateObservationConvention` or `RabbitListenerObservationConvention` to the template or listener container, respectively. + +The default implementations add the `bean.name` tag for template observations and `listener.id` tag for containers. + +You can either subclass `DefaultRabbitTemplateObservationConvention` or `DefaultRabbitListenerObservationConvention` or provide completely new implementations. + [[containers-and-broker-named-queues]] ==== Containers and Broker-Named queues diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 2b43baca63..225befa469 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -11,6 +11,11 @@ This version requires Spring Framework 6.0 and Java 17 The remoting feature (using RMI) is no longer supported. +==== Observation + +Enabling observation for timers and tracing using Micrometer is now supported. +See <> for more information. + ==== AsyncRabbitTemplate The `AsyncRabbitTemplate2`, which was added in 2.4.7 to aid migration to this release, is deprecated in favor of `AsyncRabbitTemplate`. From 599d5b7158846bae3e948887709ddac765c6881b Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Sep 2022 14:30:58 -0400 Subject: [PATCH 27/31] Fix doc link. Co-authored-by: Artem Bilan --- src/reference/asciidoc/amqp.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference/asciidoc/amqp.adoc b/src/reference/asciidoc/amqp.adoc index 0b2f684f72..b817785496 100644 --- a/src/reference/asciidoc/amqp.adoc +++ b/src/reference/asciidoc/amqp.adoc @@ -3773,7 +3773,7 @@ Using Micrometer for observation is now supported, since version 3.0, for the `R Set `observationEnabled` on each component to enable observation; this will disable <> because the timers will now be managed with each observation. -Refer to https://micrometer.io/docs/tracing#_running_integration_tests[Micrometer Tracing] for more information. +Refer to https://micrometer.io/docs/tracing[Micrometer Tracing] for more information. To add tags to timers/traces, configure a custom `RabbitTemplateObservationConvention` or `RabbitListenerObservationConvention` to the template or listener container, respectively. From 38589a03486a91fa08c3b02d3050c46ec4ba299b Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 15 Sep 2022 15:05:42 -0400 Subject: [PATCH 28/31] Remove unnecessary method overrides; make tag names more meaningful. --- ...ltRabbitListenerObservationConvention.java | 5 --- ...ltRabbitTemplateObservationConvention.java | 5 --- .../micrometer/RabbitListenerObservation.java | 2 +- .../micrometer/RabbitTemplateObservation.java | 2 +- .../ObservationIntegrationTests.java | 20 +++++++----- .../support/micrometer/ObservationTests.java | 32 +++++++++++-------- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java index 82b84ba8cf..ae6f4488b9 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitListenerObservationConvention.java @@ -39,11 +39,6 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context context.getListenerId()); } - @Override - public KeyValues getHighCardinalityKeyValues(RabbitMessageReceiverContext context) { - return KeyValues.empty(); - } - @Override public String getContextualName(RabbitMessageReceiverContext context) { return context.getSource() + " receive"; diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java index 38394a0927..285d52b835 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/DefaultRabbitTemplateObservationConvention.java @@ -39,11 +39,6 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) context.getBeanName()); } - @Override - public KeyValues getHighCardinalityKeyValues(RabbitMessageSenderContext context) { - return KeyValues.empty(); - } - @Override public String getContextualName(RabbitMessageSenderContext context) { return context.getDestination() + " send"; diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java index 271995e9fe..8454a987a3 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservation.java @@ -65,7 +65,7 @@ public enum ListenerLowCardinalityTags implements KeyName { @Override public String asString() { - return "listener.id"; + return "spring.rabbit.listener.id"; } } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java index 2a016692bf..01fb63f6c2 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservation.java @@ -64,7 +64,7 @@ public enum TemplateLowCardinalityTags implements KeyName { @Override public String asString() { - return "bean.name"; + return "spring.rabbit.template.name"; } } diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java index 95ebc35135..2611785c8f 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationIntegrationTests.java @@ -71,22 +71,26 @@ public SampleTestRunnerConsumer yourCode() { .hasSize(4); SpanAssert.assertThat(finishedSpans.get(0)) .hasKindEqualTo(Kind.PRODUCER) - .hasTag("bean.name", "template"); + .hasTag("spring.rabbit.template.name", "template"); SpanAssert.assertThat(finishedSpans.get(1)) .hasKindEqualTo(Kind.PRODUCER) - .hasTag("bean.name", "template"); + .hasTag("spring.rabbit.template.name", "template"); SpanAssert.assertThat(finishedSpans.get(2)) .hasKindEqualTo(Kind.CONSUMER) - .hasTag("listener.id", "obs1"); + .hasTag("spring.rabbit.listener.id", "obs1"); SpanAssert.assertThat(finishedSpans.get(3)) .hasKindEqualTo(Kind.CONSUMER) - .hasTag("listener.id", "obs2"); + .hasTag("spring.rabbit.listener.id", "obs2"); MeterRegistryAssert.assertThat(getMeterRegistry()) - .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) - .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) - .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) - .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs2")); + .hasTimerWithNameAndTags("spring.rabbit.template", + KeyValues.of("spring.rabbit.template.name", "template")) + .hasTimerWithNameAndTags("spring.rabbit.template", + KeyValues.of("spring.rabbit.template.name", "template")) + .hasTimerWithNameAndTags("spring.rabbit.listener", + KeyValues.of("spring.rabbit.listener.id", "obs1")) + .hasTimerWithNameAndTags("spring.rabbit.listener", + KeyValues.of("spring.rabbit.listener.id", "obs2")); }; } diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 432c1b2cfa..96b31d68a5 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -85,18 +85,20 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, Deque spans = tracer.getSpans(); assertThat(spans).hasSize(4); SimpleSpan span = spans.poll(); - assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); span = spans.poll(); assertThat(span.getTags()) - .containsAllEntriesOf(Map.of("listener.id", "obs1", "foo", "some foo value", "bar", "some bar value")); + .containsAllEntriesOf( + Map.of("spring.rabbit.listener.id", "obs1", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); span = spans.poll(); - assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); span = spans.poll(); assertThat(span.getTags()) - .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); + .containsAllEntriesOf( + Map.of("spring.rabbit.listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); template.setObservationConvention(new DefaultRabbitTemplateObservationConvention() { @@ -125,31 +127,33 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context .hasFieldOrPropertyWithValue("bar", "some bar value"); assertThat(spans).hasSize(4); span = spans.poll(); - assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getTags()).containsEntry("foo", "bar"); assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); span = spans.poll(); assertThat(span.getTags()) - .containsAllEntriesOf(Map.of("listener.id", "obs1", "foo", "some foo value", "bar", "some bar value", - "baz", "qux")); + .containsAllEntriesOf(Map.of("spring.rabbit.listener.id", "obs1", "foo", "some foo value", "bar", + "some bar value", "baz", "qux")); assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); span = spans.poll(); - assertThat(span.getTags()).containsEntry("bean.name", "template"); + assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getTags()).containsEntry("foo", "bar"); assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); span = spans.poll(); assertThat(span.getTags()) - .containsAllEntriesOf(Map.of("listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); + .containsAllEntriesOf( + Map.of("spring.rabbit.listener.id", "obs2", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getTags()).doesNotContainEntry("baz", "qux"); assertThat(span.getName()).isEqualTo("observation.testQ2 receive"); MeterRegistryAssert.assertThat(meterRegistry) - .hasTimerWithNameAndTags("spring.rabbit.template", KeyValues.of("bean.name", "template")) .hasTimerWithNameAndTags("spring.rabbit.template", - KeyValues.of("bean.name", "template", "foo", "bar")) - .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs1")) + KeyValues.of("spring.rabbit.template.name", "template")) + .hasTimerWithNameAndTags("spring.rabbit.template", + KeyValues.of("spring.rabbit.template.name", "template", "foo", "bar")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("spring.rabbit.listener.id", "obs1")) .hasTimerWithNameAndTags("spring.rabbit.listener", - KeyValues.of("listener.id", "obs1", "baz", "qux")) - .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("listener.id", "obs2")); + KeyValues.of("spring.rabbit.listener.id", "obs1", "baz", "qux")) + .hasTimerWithNameAndTags("spring.rabbit.listener", KeyValues.of("spring.rabbit.listener.id", "obs2")); } @Configuration From d5464ab7682bacf4915fdb391d01a55a9d67c365 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Sat, 17 Sep 2022 16:18:30 -0400 Subject: [PATCH 29/31] Move getName() from contexts to conventions. --- .../micrometer/RabbitListenerObservationConvention.java | 5 +++++ .../support/micrometer/RabbitMessageReceiverContext.java | 5 ----- .../support/micrometer/RabbitMessageSenderContext.java | 5 ----- .../micrometer/RabbitTemplateObservationConvention.java | 5 +++++ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java index 71e0d6d836..bbf1f27df5 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitListenerObservationConvention.java @@ -33,4 +33,9 @@ default boolean supportsContext(Context context) { return context instanceof RabbitMessageReceiverContext; } + @Override + default String getName() { + return "spring.rabbit.listener"; + } + } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java index 7bfa91f58a..07ffebd732 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageReceiverContext.java @@ -44,11 +44,6 @@ public String getListenerId() { return this.listenerId; } - @Override - public String getName() { - return "spring.rabbit.listener"; - } - /** * Return the source (queue) for this message. * @return the source. diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java index cde103cb4b..b1b25755d4 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitMessageSenderContext.java @@ -44,11 +44,6 @@ public String getBeanName() { return this.beanName; } - @Override - public String getName() { - return "spring.rabbit.template"; - } - /** * Return the destination - {@code exchange/routingKey}. * @return the destination. diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java index 8d405b5182..2128d3dd9b 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/support/micrometer/RabbitTemplateObservationConvention.java @@ -33,4 +33,9 @@ default boolean supportsContext(Context context) { return context instanceof RabbitMessageSenderContext; } + @Override + default String getName() { + return "spring.rabbit.template"; + } + } From 4ec81a13bfddb0502b32cf7e4ebb3655339655f9 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Mon, 19 Sep 2022 12:08:54 -0400 Subject: [PATCH 30/31] Fix Race in Test --- .../amqp/rabbit/support/micrometer/ObservationTests.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 96b31d68a5..7bc469ea90 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -17,6 +17,7 @@ package org.springframework.amqp.rabbit.support.micrometer; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import java.util.Arrays; import java.util.Deque; @@ -87,6 +88,8 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, SimpleSpan span = spans.poll(); assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); + SimpleSpan last1 = spans.peekFirst(); + await().until(() -> last1.getTags().size() == 3); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf( @@ -139,6 +142,8 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getTags()).containsEntry("foo", "bar"); assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); + SimpleSpan last2 = spans.peekFirst(); + await().until(() -> last2.getTags().size() == 3); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf( From 2651f1c50ad2b4cae3023b35ebdaaaa26b8c8bc6 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Mon, 19 Sep 2022 12:41:24 -0400 Subject: [PATCH 31/31] Fix Race in Test. --- .../rabbit/support/micrometer/ObservationTests.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java index 7bc469ea90..58e3d911b3 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/support/micrometer/ObservationTests.java @@ -88,16 +88,17 @@ void endToEnd(@Autowired Listener listener, @Autowired RabbitTemplate template, SimpleSpan span = spans.poll(); assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); - SimpleSpan last1 = spans.peekFirst(); - await().until(() -> last1.getTags().size() == 3); + await().until(() -> spans.peekFirst().getTags().size() == 3); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf( Map.of("spring.rabbit.listener.id", "obs1", "foo", "some foo value", "bar", "some bar value")); assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); + await().until(() -> spans.peekFirst().getTags().size() == 1); span = spans.poll(); assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); + await().until(() -> spans.peekFirst().getTags().size() == 3); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf( @@ -133,17 +134,18 @@ public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getTags()).containsEntry("foo", "bar"); assertThat(span.getName()).isEqualTo("/observation.testQ1 send"); + await().until(() -> spans.peekFirst().getTags().size() == 4); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf(Map.of("spring.rabbit.listener.id", "obs1", "foo", "some foo value", "bar", "some bar value", "baz", "qux")); assertThat(span.getName()).isEqualTo("observation.testQ1 receive"); + await().until(() -> spans.peekFirst().getTags().size() == 2); span = spans.poll(); assertThat(span.getTags()).containsEntry("spring.rabbit.template.name", "template"); assertThat(span.getTags()).containsEntry("foo", "bar"); assertThat(span.getName()).isEqualTo("/observation.testQ2 send"); - SimpleSpan last2 = spans.peekFirst(); - await().until(() -> last2.getTags().size() == 3); + await().until(() -> spans.peekFirst().getTags().size() == 3); span = spans.poll(); assertThat(span.getTags()) .containsAllEntriesOf(