GH-2814: OTel tags for RabbitTemplate and RabbitListener

Fixes: #2814
Issue link: https://github.com/spring-projects/spring-amqp/issues/2814

* Add Opentelemetry tags `RabbitTemplate `
* Add Opentelemetry tags `RabbitListenerObservationConvention`
This commit is contained in:
vmeunier
2024-09-19 21:24:33 +02:00
committed by GitHub
parent aa21c73500
commit ed0d7905ac
3 changed files with 113 additions and 22 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2024 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.
@@ -26,8 +26,8 @@ import io.micrometer.observation.docs.ObservationDocumentation;
* Spring Rabbit Observation for listeners.
*
* @author Gary Russell
* @author Vincent Meunier
* @since 3.0
*
*/
public enum RabbitListenerObservation implements ObservationDocumentation {
@@ -36,7 +36,6 @@ public enum RabbitListenerObservation implements ObservationDocumentation {
*/
LISTENER_OBSERVATION {
@Override
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
return DefaultRabbitListenerObservationConvention.class;
@@ -69,6 +68,34 @@ public enum RabbitListenerObservation implements ObservationDocumentation {
return "spring.rabbit.listener.id";
}
},
/**
* The queue the listener is plugged to.
*
* @since 3.2
*/
DESTINATION_NAME {
@Override
public String asString() {
return "messaging.destination.name";
}
},
/**
* The delivery tag.
*
* @since 3.2
*/
DELIVERY_TAG {
@Override
public String asString() {
return "messaging.rabbitmq.message.delivery_tag";
}
}
}
@@ -86,8 +113,14 @@ public enum RabbitListenerObservation implements ObservationDocumentation {
@Override
public KeyValues getLowCardinalityKeyValues(RabbitMessageReceiverContext context) {
return KeyValues.of(RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(),
context.getListenerId());
final var messageProperties = context.getCarrier().getMessageProperties();
return KeyValues.of(
RabbitListenerObservation.ListenerLowCardinalityTags.LISTENER_ID.asString(), context.getListenerId(),
RabbitListenerObservation.ListenerLowCardinalityTags.DESTINATION_NAME.asString(),
messageProperties.getConsumerQueue(),
RabbitListenerObservation.ListenerLowCardinalityTags.DELIVERY_TAG.asString(),
String.valueOf(messageProperties.getDeliveryTag())
);
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2024 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.
@@ -26,6 +26,7 @@ import io.micrometer.observation.docs.ObservationDocumentation;
* Spring RabbitMQ Observation for {@link org.springframework.amqp.rabbit.core.RabbitTemplate}.
*
* @author Gary Russell
* @author Vincent Meunier
* @since 3.0
*
*/
@@ -68,8 +69,35 @@ public enum RabbitTemplateObservation implements ObservationDocumentation {
return "spring.rabbit.template.name";
}
},
/**
* The destination exchange (empty if default exchange).
* @since 3.2
*/
EXCHANGE {
@Override
public String asString() {
return "messaging.destination.name";
}
},
/**
* The destination routing key.
* @since 3.2
*/
ROUTING_KEY {
@Override
public String asString() {
return "messaging.rabbitmq.destination.routing_key";
}
}
}
/**
@@ -85,8 +113,11 @@ public enum RabbitTemplateObservation implements ObservationDocumentation {
@Override
public KeyValues getLowCardinalityKeyValues(RabbitMessageSenderContext context) {
return KeyValues.of(RabbitTemplateObservation.TemplateLowCardinalityTags.BEAN_NAME.asString(),
context.getBeanName());
return KeyValues.of(
TemplateLowCardinalityTags.BEAN_NAME.asString(), context.getBeanName(),
TemplateLowCardinalityTags.EXCHANGE.asString(), context.getExchange(),
TemplateLowCardinalityTags.ROUTING_KEY.asString(), context.getRoutingKey()
);
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022 the original author or authors.
* Copyright 2022-2024 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,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
@@ -35,6 +34,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.core.tck.MeterRegistryAssert;
import io.micrometer.observation.ObservationRegistry;
@@ -47,7 +47,6 @@ import io.micrometer.tracing.test.simple.SpansAssert;
/**
* @author Artem Bilan
* @author Gary Russell
*
* @since 3.0
*/
@RabbitAvailable(queues = { "int.observation.testQ1", "int.observation.testQ2" })
@@ -72,40 +71,69 @@ public class ObservationIntegrationTests extends SampleTestRunner {
.hasSize(4);
List<FinishedSpan> producerSpans = finishedSpans.stream()
.filter(span -> span.getKind().equals(Kind.PRODUCER))
.collect(Collectors.toList());
.toList();
List<FinishedSpan> consumerSpans = finishedSpans.stream()
.filter(span -> span.getKind().equals(Kind.CONSUMER))
.collect(Collectors.toList());
.toList();
SpanAssert.assertThat(producerSpans.get(0))
.hasTag("spring.rabbit.template.name", "template");
.hasTag("spring.rabbit.template.name", "template")
.hasTag("messaging.destination.name", "")
.hasTag("messaging.rabbitmq.destination.routing_key", "int.observation.testQ1");
SpanAssert.assertThat(producerSpans.get(0))
.hasRemoteServiceNameEqualTo("RabbitMQ");
SpanAssert.assertThat(producerSpans.get(1))
.hasTag("spring.rabbit.template.name", "template");
.hasTag("spring.rabbit.template.name", "template")
.hasTag("messaging.destination.name", "")
.hasTag("messaging.rabbitmq.destination.routing_key", "int.observation.testQ2");
SpanAssert.assertThat(consumerSpans.get(0))
.hasTagWithKey("spring.rabbit.listener.id");
.hasTagWithKey("spring.rabbit.listener.id")
.hasTag("messaging.destination.name", "int.observation.testQ1")
.hasTag("messaging.rabbitmq.message.delivery_tag", "1");
SpanAssert.assertThat(consumerSpans.get(0))
.hasRemoteServiceNameEqualTo("RabbitMQ");
assertThat(consumerSpans.get(0).getTags().get("spring.rabbit.listener.id")).isIn("obs1", "obs2");
SpanAssert.assertThat(consumerSpans.get(1))
.hasTagWithKey("spring.rabbit.listener.id");
assertThat(consumerSpans.get(1).getTags().get("spring.rabbit.listener.id")).isIn("obs1", "obs2");
SpanAssert.assertThat(consumerSpans.get(1))
.hasTagWithKey("spring.rabbit.listener.id")
.hasTag("messaging.destination.name", "int.observation.testQ2")
.hasTag("messaging.rabbitmq.message.delivery_tag", "1");
assertThat(consumerSpans.get(0).getTags().get("spring.rabbit.listener.id"))
.isNotEqualTo(consumerSpans.get(1).getTags().get("spring.rabbit.listener.id"));
MeterRegistryAssert.assertThat(getMeterRegistry())
.hasTimerWithNameAndTags("spring.rabbit.template",
KeyValues.of("spring.rabbit.template.name", "template"))
KeyValues.of(
KeyValue.of("spring.rabbit.template.name", "template"),
KeyValue.of("messaging.destination.name", ""),
KeyValue.of("messaging.rabbitmq.destination.routing_key", "int.observation.testQ1")
)
)
.hasTimerWithNameAndTags("spring.rabbit.template",
KeyValues.of("spring.rabbit.template.name", "template"))
KeyValues.of(
KeyValue.of("spring.rabbit.template.name", "template"),
KeyValue.of("messaging.destination.name", ""),
KeyValue.of("messaging.rabbitmq.destination.routing_key", "int.observation.testQ2")
)
)
.hasTimerWithNameAndTags("spring.rabbit.listener",
KeyValues.of("spring.rabbit.listener.id", "obs1"))
KeyValues.of(
KeyValue.of("spring.rabbit.listener.id", "obs1"),
KeyValue.of("messaging.destination.name", "int.observation.testQ1"),
KeyValue.of("messaging.rabbitmq.message.delivery_tag", "1")
)
)
.hasTimerWithNameAndTags("spring.rabbit.listener",
KeyValues.of("spring.rabbit.listener.id", "obs2"));
KeyValues.of(
KeyValue.of("spring.rabbit.listener.id", "obs2"),
KeyValue.of("messaging.destination.name", "int.observation.testQ2"),
KeyValue.of("messaging.rabbitmq.message.delivery_tag", "1")
)
);
};
}
@Configuration
@EnableRabbit
public static class Config {
@@ -159,5 +187,4 @@ public class ObservationIntegrationTests extends SampleTestRunner {
}
}