convert spring integration tests to java (#11999)
Co-authored-by: Jay DeLuca <jaydeluca4@gmail.com>
This commit is contained in:
parent
53f019b8f6
commit
6700efdd23
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
|
||||||
|
|
||||||
class ComplexPropagationTest extends AbstractComplexPropagationTest implements AgentTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
|
||||||
|
|
||||||
class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest implements AgentTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
|
||||||
|
|
||||||
class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest implements AgentTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,123 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
|
||||||
import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes
|
|
||||||
import io.opentelemetry.semconv.NetworkAttributes
|
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CONSUMER
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.PRODUCER
|
|
||||||
|
|
||||||
class SpringIntegrationAndRabbitTest extends AgentInstrumentationSpecification implements WithRabbitProducerConsumerTrait {
|
|
||||||
def setupSpec() {
|
|
||||||
startRabbit()
|
|
||||||
}
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
stopRabbit()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should cooperate with existing RabbitMQ instrumentation"() {
|
|
||||||
when:
|
|
||||||
runWithSpan("parent") {
|
|
||||||
producerContext.getBean("producer", Runnable).run()
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(2) {
|
|
||||||
trace(0, 7) {
|
|
||||||
span(0) {
|
|
||||||
name "parent"
|
|
||||||
attributes {}
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "producer"
|
|
||||||
childOf span(0)
|
|
||||||
attributes {}
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
// span created by rabbitmq instrumentation
|
|
||||||
name "exchange.declare"
|
|
||||||
childOf span(1)
|
|
||||||
kind CLIENT
|
|
||||||
attributes {
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null }
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_PORT" Long
|
|
||||||
"$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null }
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span(3) {
|
|
||||||
// span created by rabbitmq instrumentation
|
|
||||||
name "testTopic publish"
|
|
||||||
childOf span(1)
|
|
||||||
kind PRODUCER
|
|
||||||
attributes {
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null }
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_PORT" Long
|
|
||||||
"$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null }
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "testTopic"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_OPERATION" "publish"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE" Long
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY" String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// spring-cloud-stream-binder-rabbit listener puts all messages into a BlockingQueue immediately after receiving
|
|
||||||
// that's why the rabbitmq CONSUMER span will never have any child span (and propagate context, actually)
|
|
||||||
span(4) {
|
|
||||||
// span created by rabbitmq instrumentation
|
|
||||||
name ~/testTopic.anonymous.[-\w]+ process/
|
|
||||||
childOf span(3)
|
|
||||||
kind CONSUMER
|
|
||||||
attributes {
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null }
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_PORT" Long
|
|
||||||
"$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null }
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "testTopic"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_OPERATION" "process"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE" Long
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY" String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// spring-integration will detect that spring-rabbit has already created a consumer span and back off
|
|
||||||
span(5) {
|
|
||||||
// span created by spring-rabbit instrumentation
|
|
||||||
name "testTopic process"
|
|
||||||
childOf span(3)
|
|
||||||
kind CONSUMER
|
|
||||||
attributes {
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME" "testTopic"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_OPERATION" "process"
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID" String
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE" Long
|
|
||||||
}
|
|
||||||
}
|
|
||||||
span(6) {
|
|
||||||
name "consumer"
|
|
||||||
childOf span(5)
|
|
||||||
attributes {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trace(1, 1) {
|
|
||||||
span(0) {
|
|
||||||
// span created by rabbitmq instrumentation
|
|
||||||
name "basic.ack"
|
|
||||||
kind CLIENT
|
|
||||||
attributes {
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_ADDRESS" { it == "127.0.0.1" || it == "0:0:0:0:0:0:0:1" || it == null }
|
|
||||||
"$NetworkAttributes.NETWORK_PEER_PORT" Long
|
|
||||||
"$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == "ipv6" || it == null }
|
|
||||||
"$MessagingIncubatingAttributes.MESSAGING_SYSTEM" "rabbitmq"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
|
||||||
|
|
||||||
class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest implements AgentTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class ComplexPropagationTest extends AbstractComplexPropagationTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
ComplexPropagationTest() {
|
||||||
|
super(testing, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
SpringCloudStreamProducerTest() {
|
||||||
|
super(testing, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
SpringCloudStreamRabbitTest() {
|
||||||
|
super(testing, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.SpanKind;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.semconv.NetworkAttributes;
|
||||||
|
import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringIntegrationAndRabbitTest {
|
||||||
|
|
||||||
|
@RegisterExtension RabbitExtension rabbit;
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
SpringIntegrationAndRabbitTest() {
|
||||||
|
rabbit = new RabbitExtension(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCooperateWithExistingRabbitMqInstrumentation() {
|
||||||
|
testing.waitForTraces(13); // from rabbitmq instrumentation of startup
|
||||||
|
testing.clearData();
|
||||||
|
|
||||||
|
runWithSpan("parent", () -> rabbit.getBean("producer", Runnable.class).run());
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("parent").hasTotalAttributeCount(0),
|
||||||
|
span ->
|
||||||
|
span.hasName("producer").hasParent(trace.getSpan(0)).hasTotalAttributeCount(0),
|
||||||
|
span -> span.hasName("exchange.declare"),
|
||||||
|
span ->
|
||||||
|
span.hasName("exchange.declare")
|
||||||
|
.hasParent(trace.getSpan(1))
|
||||||
|
.hasKind(SpanKind.CLIENT)
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_ADDRESS,
|
||||||
|
s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_PORT,
|
||||||
|
l -> l.isInstanceOf(Long.class)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq")),
|
||||||
|
span -> span.hasName("queue.declare"),
|
||||||
|
span -> span.hasName("queue.bind"),
|
||||||
|
span ->
|
||||||
|
span.hasName("testTopic publish")
|
||||||
|
.hasParent(trace.getSpan(1))
|
||||||
|
.hasKind(SpanKind.PRODUCER)
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_ADDRESS,
|
||||||
|
s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_PORT,
|
||||||
|
l -> l.isInstanceOf(Long.class)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"),
|
||||||
|
equalTo(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME,
|
||||||
|
"testTopic"),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_OPERATION, "publish"),
|
||||||
|
satisfies(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE,
|
||||||
|
l -> l.isInstanceOf(Long.class)),
|
||||||
|
satisfies(
|
||||||
|
MessagingIncubatingAttributes
|
||||||
|
.MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY,
|
||||||
|
s -> s.isInstanceOf(String.class))),
|
||||||
|
// spring-cloud-stream-binder-rabbit listener puts all messages into a BlockingQueue
|
||||||
|
// immediately after receiving
|
||||||
|
// that's why the rabbitmq CONSUMER span will never have any child span (and
|
||||||
|
// propagate context, actually)
|
||||||
|
span ->
|
||||||
|
span.satisfies(
|
||||||
|
spanData ->
|
||||||
|
assertThat(spanData.getName())
|
||||||
|
.matches("testTopic.anonymous.[-\\w]+ process"))
|
||||||
|
.hasParent(trace.getSpan(6))
|
||||||
|
.hasKind(SpanKind.CONSUMER)
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_ADDRESS,
|
||||||
|
s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_PORT,
|
||||||
|
l -> l.isInstanceOf(Long.class)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"),
|
||||||
|
equalTo(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME,
|
||||||
|
"testTopic"),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_OPERATION, "process"),
|
||||||
|
satisfies(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE,
|
||||||
|
l -> l.isInstanceOf(Long.class)),
|
||||||
|
satisfies(
|
||||||
|
MessagingIncubatingAttributes
|
||||||
|
.MESSAGING_RABBITMQ_DESTINATION_ROUTING_KEY,
|
||||||
|
s -> s.isInstanceOf(String.class))),
|
||||||
|
// spring-integration will detect that spring-rabbit has already created a consumer
|
||||||
|
// span and back off
|
||||||
|
span ->
|
||||||
|
span.hasName("testTopic process")
|
||||||
|
.hasParent(trace.getSpan(6))
|
||||||
|
.hasKind(SpanKind.CONSUMER)
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"),
|
||||||
|
equalTo(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME,
|
||||||
|
"testTopic"),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_OPERATION, "process"),
|
||||||
|
satisfies(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID,
|
||||||
|
s -> s.isInstanceOf(String.class)),
|
||||||
|
satisfies(
|
||||||
|
MessagingIncubatingAttributes.MESSAGING_MESSAGE_BODY_SIZE,
|
||||||
|
l -> l.isInstanceOf(Long.class))),
|
||||||
|
span ->
|
||||||
|
span.hasName("consumer").hasParent(trace.getSpan(8)).hasTotalAttributeCount(0)),
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span ->
|
||||||
|
span.hasName("basic.ack")
|
||||||
|
.hasKind(SpanKind.CLIENT)
|
||||||
|
.hasAttributesSatisfyingExactly(
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_ADDRESS,
|
||||||
|
s -> s.isIn("127.0.0.1", "0:0:0:0:0:0:0:1", null)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_PEER_PORT,
|
||||||
|
l -> l.isInstanceOf(Long.class)),
|
||||||
|
satisfies(
|
||||||
|
NetworkAttributes.NETWORK_TYPE, s -> s.isIn("ipv4", "ipv6", null)),
|
||||||
|
equalTo(MessagingIncubatingAttributes.MESSAGING_SYSTEM, "rabbitmq"))));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||||
|
|
||||||
|
SpringIntegrationTelemetryTest() {
|
||||||
|
super(testing, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
|
||||||
|
|
||||||
class ComplexPropagationTest extends AbstractComplexPropagationTest implements LibraryTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
GlobalInterceptorSpringConfig
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
|
||||||
import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.integration.config.GlobalChannelInterceptor
|
|
||||||
import org.springframework.messaging.support.ChannelInterceptor
|
|
||||||
|
|
||||||
import static java.util.Collections.singletonList
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
class GlobalInterceptorSpringConfig {
|
|
||||||
|
|
||||||
@GlobalChannelInterceptor
|
|
||||||
@Bean
|
|
||||||
ChannelInterceptor otelInterceptor() {
|
|
||||||
SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get())
|
|
||||||
.setCapturedHeaders(singletonList("test-message-header"))
|
|
||||||
.build()
|
|
||||||
.newChannelInterceptor()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
|
||||||
import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.annotation.Configuration
|
|
||||||
import org.springframework.integration.config.GlobalChannelInterceptor
|
|
||||||
import org.springframework.messaging.support.ChannelInterceptor
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
class GlobalInterceptorWithProducerSpanSpringConfig {
|
|
||||||
|
|
||||||
@GlobalChannelInterceptor
|
|
||||||
@Bean
|
|
||||||
ChannelInterceptor otelInterceptor() {
|
|
||||||
SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get())
|
|
||||||
.setProducerSpanEnabled(true)
|
|
||||||
.build()
|
|
||||||
.newChannelInterceptor()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
|
||||||
|
|
||||||
class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest implements LibraryTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
GlobalInterceptorWithProducerSpanSpringConfig
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
|
||||||
|
|
||||||
class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest implements LibraryTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
GlobalInterceptorSpringConfig
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
|
||||||
|
|
||||||
class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest implements LibraryTestTrait {
|
|
||||||
@Override
|
|
||||||
Class<?> additionalContextClass() {
|
|
||||||
GlobalInterceptorSpringConfig
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class ComplexPropagationTest extends AbstractComplexPropagationTest {
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||||
|
|
||||||
|
public ComplexPropagationTest() {
|
||||||
|
super(testing, GlobalInterceptorSpringConfig.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||||
|
import org.springframework.messaging.support.ChannelInterceptor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class GlobalInterceptorSpringConfig {
|
||||||
|
|
||||||
|
@GlobalChannelInterceptor
|
||||||
|
@Bean
|
||||||
|
ChannelInterceptor otelInterceptor() {
|
||||||
|
return SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get())
|
||||||
|
.setCapturedHeaders(singletonList("test-message-header"))
|
||||||
|
.build()
|
||||||
|
.newChannelInterceptor();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||||
|
import io.opentelemetry.instrumentation.spring.integration.v4_1.SpringIntegrationTelemetry;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.integration.config.GlobalChannelInterceptor;
|
||||||
|
import org.springframework.messaging.support.ChannelInterceptor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class GlobalInterceptorWithProducerSpanSpringConfig {
|
||||||
|
|
||||||
|
@GlobalChannelInterceptor
|
||||||
|
@Bean
|
||||||
|
ChannelInterceptor otelInterceptor() {
|
||||||
|
return SpringIntegrationTelemetry.builder(GlobalOpenTelemetry.get())
|
||||||
|
.setProducerSpanEnabled(true)
|
||||||
|
.build()
|
||||||
|
.newChannelInterceptor();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringCloudStreamProducerTest extends AbstractSpringCloudStreamProducerTest {
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||||
|
|
||||||
|
public SpringCloudStreamProducerTest() {
|
||||||
|
super(testing, GlobalInterceptorWithProducerSpanSpringConfig.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringCloudStreamRabbitTest extends AbstractSpringCloudStreamRabbitTest {
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||||
|
|
||||||
|
public SpringCloudStreamRabbitTest() {
|
||||||
|
super(testing, GlobalInterceptorSpringConfig.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
class SpringIntegrationTelemetryTest extends AbstractSpringIntegrationTracingTest {
|
||||||
|
@RegisterExtension
|
||||||
|
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
|
||||||
|
|
||||||
|
public SpringIntegrationTelemetryTest() {
|
||||||
|
super(testing, GlobalInterceptorSpringConfig.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,151 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
|
||||||
import org.springframework.boot.SpringApplication
|
|
||||||
import org.springframework.boot.SpringBootConfiguration
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.event.EventListener
|
|
||||||
import org.springframework.integration.channel.DirectChannel
|
|
||||||
import org.springframework.integration.channel.ExecutorChannel
|
|
||||||
import org.springframework.messaging.Message
|
|
||||||
import org.springframework.messaging.SubscribableChannel
|
|
||||||
import org.springframework.messaging.support.MessageBuilder
|
|
||||||
import spock.lang.Shared
|
|
||||||
|
|
||||||
import java.util.concurrent.BlockingQueue
|
|
||||||
import java.util.concurrent.ExecutorService
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
|
||||||
import java.util.stream.Collectors
|
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CONSUMER
|
|
||||||
|
|
||||||
abstract class AbstractComplexPropagationTest extends InstrumentationSpecification {
|
|
||||||
|
|
||||||
abstract Class<?> additionalContextClass()
|
|
||||||
|
|
||||||
@Shared
|
|
||||||
ConfigurableApplicationContext applicationContext
|
|
||||||
|
|
||||||
def setupSpec() {
|
|
||||||
def contextClasses = [ExternalQueueConfig]
|
|
||||||
if (additionalContextClass() != null) {
|
|
||||||
contextClasses += additionalContextClass()
|
|
||||||
}
|
|
||||||
|
|
||||||
def app = new SpringApplication(contextClasses as Class<?>[])
|
|
||||||
app.setDefaultProperties([
|
|
||||||
"spring.main.web-application-type": "none"
|
|
||||||
])
|
|
||||||
applicationContext = app.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
applicationContext?.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should propagate context through a custom message queue"() {
|
|
||||||
given:
|
|
||||||
def sendChannel = applicationContext.getBean("sendChannel", SubscribableChannel)
|
|
||||||
def receiveChannel = applicationContext.getBean("receiveChannel", SubscribableChannel)
|
|
||||||
|
|
||||||
def messageHandler = new CapturingMessageHandler()
|
|
||||||
receiveChannel.subscribe(messageHandler)
|
|
||||||
|
|
||||||
when:
|
|
||||||
sendChannel.send(MessageBuilder.withPayload("test")
|
|
||||||
.setHeader("theAnswer", "42")
|
|
||||||
.build())
|
|
||||||
|
|
||||||
then:
|
|
||||||
messageHandler.join()
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 3) {
|
|
||||||
// there's no span in the context, so spring-integration adds a CONSUMER one
|
|
||||||
span(0) {
|
|
||||||
name "application.sendChannel process"
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
// message is received in a separate thread without any context, so a CONSUMER span with parent
|
|
||||||
// extracted from the incoming message is created
|
|
||||||
span(1) {
|
|
||||||
name "application.receiveChannel process"
|
|
||||||
childOf span(0)
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
name "handler"
|
|
||||||
childOf span(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
receiveChannel.unsubscribe(messageHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// this setup simulates separate producer/consumer and some "external" message queue in between
|
|
||||||
@SpringBootConfiguration
|
|
||||||
@EnableAutoConfiguration
|
|
||||||
static class ExternalQueueConfig {
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel sendChannel() {
|
|
||||||
new ExecutorChannel(Executors.newSingleThreadExecutor())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel receiveChannel() {
|
|
||||||
new DirectChannel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
BlockingQueue<Payload> externalQueue() {
|
|
||||||
new LinkedBlockingQueue<Payload>()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(destroyMethod = "shutdownNow")
|
|
||||||
ExecutorService consumerThread() {
|
|
||||||
Executors.newSingleThreadExecutor()
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventListener(ApplicationReadyEvent)
|
|
||||||
void initialize() {
|
|
||||||
sendChannel().subscribe { message ->
|
|
||||||
externalQueue().offer(Payload.from(message))
|
|
||||||
}
|
|
||||||
|
|
||||||
consumerThread().execute({
|
|
||||||
while (!Thread.interrupted()) {
|
|
||||||
def payload = externalQueue().take()
|
|
||||||
receiveChannel().send(payload.toMessage())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Payload {
|
|
||||||
String body
|
|
||||||
Map<String, String> headers
|
|
||||||
|
|
||||||
static Payload from(Message<?> message) {
|
|
||||||
def body = message.payload as String
|
|
||||||
Map<String, String> headers = message.headers.entrySet().stream()
|
|
||||||
.filter({ kv -> kv.value instanceof String })
|
|
||||||
.collect(Collectors.toMap({ it.key }, { it.value }))
|
|
||||||
new Payload(body: body, headers: headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
Message<String> toMessage() {
|
|
||||||
MessageBuilder.withPayload(body)
|
|
||||||
.copyHeaders(headers)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CONSUMER
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.PRODUCER
|
|
||||||
import static org.junit.jupiter.api.Assumptions.assumeTrue
|
|
||||||
|
|
||||||
abstract class AbstractSpringCloudStreamProducerTest extends InstrumentationSpecification implements WithRabbitProducerConsumerTrait {
|
|
||||||
private static final boolean HAS_PRODUCER_SPAN = Boolean.getBoolean("otel.instrumentation.spring-integration.producer.enabled")
|
|
||||||
|
|
||||||
abstract Class<?> additionalContextClass()
|
|
||||||
|
|
||||||
def setupSpec() {
|
|
||||||
startRabbit(additionalContextClass())
|
|
||||||
}
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
stopRabbit()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "has producer span"() {
|
|
||||||
assumeTrue(HAS_PRODUCER_SPAN)
|
|
||||||
|
|
||||||
when:
|
|
||||||
producerContext.getBean("producer", Runnable).run()
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 4) {
|
|
||||||
span(0) {
|
|
||||||
name "producer"
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "testProducer.output publish"
|
|
||||||
childOf span(0)
|
|
||||||
kind PRODUCER
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
name "testConsumer.input process"
|
|
||||||
childOf span(1)
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(3) {
|
|
||||||
name "consumer"
|
|
||||||
childOf span(2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CONSUMER
|
|
||||||
|
|
||||||
abstract class AbstractSpringCloudStreamRabbitTest extends InstrumentationSpecification implements WithRabbitProducerConsumerTrait {
|
|
||||||
|
|
||||||
abstract Class<?> additionalContextClass()
|
|
||||||
|
|
||||||
def setupSpec() {
|
|
||||||
startRabbit(additionalContextClass())
|
|
||||||
}
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
stopRabbit()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should propagate context through RabbitMQ"() {
|
|
||||||
when:
|
|
||||||
producerContext.getBean("producer", Runnable).run()
|
|
||||||
|
|
||||||
then:
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 3) {
|
|
||||||
span(0) {
|
|
||||||
name "producer"
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "testConsumer.input process"
|
|
||||||
childOf span(0)
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(2) {
|
|
||||||
name "consumer"
|
|
||||||
childOf span(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,284 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
|
||||||
import io.opentelemetry.sdk.trace.data.SpanData
|
|
||||||
import org.springframework.boot.SpringApplication
|
|
||||||
import org.springframework.boot.SpringBootConfiguration
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.context.event.EventListener
|
|
||||||
import org.springframework.integration.channel.DirectChannel
|
|
||||||
import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper
|
|
||||||
import org.springframework.messaging.Message
|
|
||||||
import org.springframework.messaging.SubscribableChannel
|
|
||||||
import org.springframework.messaging.support.ExecutorSubscribableChannel
|
|
||||||
import org.springframework.messaging.support.MessageBuilder
|
|
||||||
import spock.lang.Shared
|
|
||||||
import spock.lang.Unroll
|
|
||||||
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
import static io.opentelemetry.api.trace.SpanKind.CONSUMER
|
|
||||||
|
|
||||||
@Unroll
|
|
||||||
abstract class AbstractSpringIntegrationTracingTest extends InstrumentationSpecification {
|
|
||||||
|
|
||||||
abstract Class<?> additionalContextClass()
|
|
||||||
|
|
||||||
@Shared
|
|
||||||
ConfigurableApplicationContext applicationContext
|
|
||||||
|
|
||||||
def setupSpec() {
|
|
||||||
def contextClasses = [MessageChannelsConfig]
|
|
||||||
if (additionalContextClass() != null) {
|
|
||||||
contextClasses += additionalContextClass()
|
|
||||||
}
|
|
||||||
|
|
||||||
def app = new SpringApplication(contextClasses as Class<?>[])
|
|
||||||
app.setDefaultProperties([
|
|
||||||
"spring.main.web-application-type": "none"
|
|
||||||
])
|
|
||||||
applicationContext = app.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
def cleanupSpec() {
|
|
||||||
applicationContext?.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should propagate context (#channelName)"() {
|
|
||||||
given:
|
|
||||||
def channel = applicationContext.getBean(channelName, SubscribableChannel)
|
|
||||||
|
|
||||||
def messageHandler = new CapturingMessageHandler()
|
|
||||||
channel.subscribe(messageHandler)
|
|
||||||
|
|
||||||
when:
|
|
||||||
channel.send(MessageBuilder.withPayload("test")
|
|
||||||
.build())
|
|
||||||
|
|
||||||
then:
|
|
||||||
def capturedMessage = messageHandler.join()
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
span(0) {
|
|
||||||
name interceptorSpanName
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "handler"
|
|
||||||
childOf span(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
def interceptorSpan = span(0)
|
|
||||||
verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
channel.unsubscribe(messageHandler)
|
|
||||||
|
|
||||||
where:
|
|
||||||
channelName | interceptorSpanName
|
|
||||||
"directChannel" | "application.directChannel process"
|
|
||||||
"executorChannel" | "executorChannel process"
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should not add interceptor twice"() {
|
|
||||||
given:
|
|
||||||
def channel = applicationContext.getBean("directChannel1", SubscribableChannel)
|
|
||||||
|
|
||||||
def messageHandler = new CapturingMessageHandler()
|
|
||||||
channel.subscribe(messageHandler)
|
|
||||||
|
|
||||||
when:
|
|
||||||
channel.send(MessageBuilder.withPayload("test")
|
|
||||||
.build())
|
|
||||||
|
|
||||||
then:
|
|
||||||
def capturedMessage = messageHandler.join()
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
span(0) {
|
|
||||||
// the channel name is overwritten by the last bean registration
|
|
||||||
name "application.directChannel2 process"
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "handler"
|
|
||||||
childOf span(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
def interceptorSpan = span(0)
|
|
||||||
verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
channel.unsubscribe(messageHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should not create a span when there is already a span in the context"() {
|
|
||||||
given:
|
|
||||||
def channel = applicationContext.getBean("directChannel", SubscribableChannel)
|
|
||||||
|
|
||||||
def messageHandler = new CapturingMessageHandler()
|
|
||||||
channel.subscribe(messageHandler)
|
|
||||||
|
|
||||||
when:
|
|
||||||
runWithSpan("parent") {
|
|
||||||
channel.send(MessageBuilder.withPayload("test")
|
|
||||||
.build())
|
|
||||||
}
|
|
||||||
|
|
||||||
then:
|
|
||||||
messageHandler.join()
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
span(0) {
|
|
||||||
name "parent"
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "handler"
|
|
||||||
childOf span(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
channel.unsubscribe(messageHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "should handle multiple message channels in a chain"() {
|
|
||||||
given:
|
|
||||||
def channel1 = applicationContext.getBean("linkedChannel1", SubscribableChannel)
|
|
||||||
def channel2 = applicationContext.getBean("linkedChannel2", SubscribableChannel)
|
|
||||||
|
|
||||||
def messageHandler = new CapturingMessageHandler()
|
|
||||||
channel2.subscribe(messageHandler)
|
|
||||||
|
|
||||||
when:
|
|
||||||
channel1.send(MessageBuilder.withPayload("test")
|
|
||||||
.build())
|
|
||||||
|
|
||||||
then:
|
|
||||||
def capturedMessage = messageHandler.join()
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
span(0) {
|
|
||||||
name "application.linkedChannel1 process"
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "handler"
|
|
||||||
childOf span(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
def lastChannelSpan = span(0)
|
|
||||||
verifyCorrectSpanWasPropagated(capturedMessage, lastChannelSpan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
channel2.unsubscribe(messageHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
def "capture message header"() {
|
|
||||||
given:
|
|
||||||
def channel = applicationContext.getBean("directChannel", SubscribableChannel)
|
|
||||||
|
|
||||||
def messageHandler = new CapturingMessageHandler()
|
|
||||||
channel.subscribe(messageHandler)
|
|
||||||
|
|
||||||
when:
|
|
||||||
channel.send(MessageBuilder.withPayload("test")
|
|
||||||
.setHeader("test-message-header", "test")
|
|
||||||
.build())
|
|
||||||
|
|
||||||
then:
|
|
||||||
def capturedMessage = messageHandler.join()
|
|
||||||
|
|
||||||
assertTraces(1) {
|
|
||||||
trace(0, 2) {
|
|
||||||
span(0) {
|
|
||||||
name "application.directChannel process"
|
|
||||||
kind CONSUMER
|
|
||||||
}
|
|
||||||
span(1) {
|
|
||||||
name "handler"
|
|
||||||
childOf span(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
def interceptorSpan = span(0)
|
|
||||||
verifyCorrectSpanWasPropagated(capturedMessage, interceptorSpan)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
channel.unsubscribe(messageHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
static void verifyCorrectSpanWasPropagated(Message<?> capturedMessage, SpanData parentSpan) {
|
|
||||||
def propagatedSpan = capturedMessage.headers.get("traceparent") as String
|
|
||||||
assert propagatedSpan.contains(parentSpan.traceId), "wrong trace id"
|
|
||||||
assert propagatedSpan.contains(parentSpan.spanId), "wrong span id"
|
|
||||||
}
|
|
||||||
|
|
||||||
@SpringBootConfiguration
|
|
||||||
@EnableAutoConfiguration
|
|
||||||
static class MessageChannelsConfig {
|
|
||||||
|
|
||||||
SubscribableChannel problematicSharedChannel = new DirectChannel()
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel directChannel() {
|
|
||||||
new DirectChannel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel directChannel1() {
|
|
||||||
problematicSharedChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel directChannel2() {
|
|
||||||
problematicSharedChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel executorChannel(GlobalChannelInterceptorWrapper otelInterceptor) {
|
|
||||||
def channel = new ExecutorSubscribableChannel(Executors.newSingleThreadExecutor())
|
|
||||||
if (!Boolean.getBoolean("testLatestDeps")) {
|
|
||||||
// spring does not inject the interceptor in 4.1 because ExecutorSubscribableChannel isn't ChannelInterceptorAware
|
|
||||||
// in later versions spring injects the global interceptor into InterceptableChannel (which ExecutorSubscribableChannel is)
|
|
||||||
channel.addInterceptor(otelInterceptor.channelInterceptor)
|
|
||||||
}
|
|
||||||
channel
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel linkedChannel1() {
|
|
||||||
new DirectChannel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SubscribableChannel linkedChannel2() {
|
|
||||||
new DirectChannel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventListener(ApplicationReadyEvent)
|
|
||||||
void initialize() {
|
|
||||||
linkedChannel1().subscribe { message ->
|
|
||||||
linkedChannel2().send(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.springframework.messaging.Message
|
|
||||||
import org.springframework.messaging.MessageHandler
|
|
||||||
import org.springframework.messaging.MessagingException
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan
|
|
||||||
|
|
||||||
class CapturingMessageHandler implements MessageHandler {
|
|
||||||
final CompletableFuture<Message<?>> captured = new CompletableFuture<>()
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void handleMessage(Message<?> message) throws MessagingException {
|
|
||||||
runWithSpan("handler") {
|
|
||||||
captured.complete(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Message<?> join() {
|
|
||||||
captured.join()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.boot.SpringApplication
|
|
||||||
import org.springframework.boot.SpringBootConfiguration
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
|
||||||
import org.springframework.cloud.stream.annotation.EnableBinding
|
|
||||||
import org.springframework.cloud.stream.annotation.StreamListener
|
|
||||||
import org.springframework.cloud.stream.messaging.Sink
|
|
||||||
import org.springframework.cloud.stream.messaging.Source
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext
|
|
||||||
import org.springframework.context.annotation.Bean
|
|
||||||
import org.springframework.messaging.support.MessageBuilder
|
|
||||||
import org.testcontainers.containers.GenericContainer
|
|
||||||
import org.testcontainers.containers.wait.strategy.Wait
|
|
||||||
|
|
||||||
import java.time.Duration
|
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan
|
|
||||||
|
|
||||||
trait WithRabbitProducerConsumerTrait {
|
|
||||||
|
|
||||||
static GenericContainer rabbitMqContainer
|
|
||||||
static ConfigurableApplicationContext producerContext
|
|
||||||
static ConfigurableApplicationContext consumerContext
|
|
||||||
|
|
||||||
def startRabbit(Class<?> additionalContext = null) {
|
|
||||||
rabbitMqContainer = new GenericContainer('rabbitmq:latest')
|
|
||||||
.withExposedPorts(5672)
|
|
||||||
.waitingFor(Wait.forLogMessage(".*Server startup complete.*", 1))
|
|
||||||
.withStartupTimeout(Duration.ofMinutes(2))
|
|
||||||
rabbitMqContainer.start()
|
|
||||||
|
|
||||||
def producerApp = new SpringApplication(getContextClasses(ProducerConfig, additionalContext))
|
|
||||||
producerApp.setDefaultProperties([
|
|
||||||
"spring.application.name" : "testProducer",
|
|
||||||
"spring.jmx.enabled" : false,
|
|
||||||
"spring.main.web-application-type" : "none",
|
|
||||||
"spring.rabbitmq.host" : rabbitMqContainer.host,
|
|
||||||
"spring.rabbitmq.port" : rabbitMqContainer.getMappedPort(5672),
|
|
||||||
"spring.cloud.stream.bindings.output.destination": "testTopic"
|
|
||||||
])
|
|
||||||
producerContext = producerApp.run()
|
|
||||||
|
|
||||||
def consumerApp = new SpringApplication(getContextClasses(ConsumerConfig, additionalContext))
|
|
||||||
consumerApp.setDefaultProperties([
|
|
||||||
"spring.application.name" : "testConsumer",
|
|
||||||
"spring.jmx.enabled" : false,
|
|
||||||
"spring.main.web-application-type" : "none",
|
|
||||||
"spring.rabbitmq.host" : rabbitMqContainer.host,
|
|
||||||
"spring.rabbitmq.port" : rabbitMqContainer.getMappedPort(5672),
|
|
||||||
"spring.cloud.stream.bindings.input.destination": "testTopic"
|
|
||||||
])
|
|
||||||
consumerContext = consumerApp.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
private Class<?>[] getContextClasses(Class<?> mainContext, Class<?> additionalContext) {
|
|
||||||
def contextClasses = [mainContext]
|
|
||||||
if (additionalContext != null) {
|
|
||||||
contextClasses += additionalContext
|
|
||||||
}
|
|
||||||
contextClasses
|
|
||||||
}
|
|
||||||
|
|
||||||
def stopRabbit() {
|
|
||||||
rabbitMqContainer?.stop()
|
|
||||||
rabbitMqContainer = null
|
|
||||||
producerContext?.close()
|
|
||||||
producerContext = null
|
|
||||||
consumerContext?.close()
|
|
||||||
consumerContext = null
|
|
||||||
}
|
|
||||||
|
|
||||||
@SpringBootConfiguration
|
|
||||||
@EnableAutoConfiguration
|
|
||||||
@EnableBinding(Source)
|
|
||||||
static class ProducerConfig {
|
|
||||||
@Autowired
|
|
||||||
Source source
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
Runnable producer() {
|
|
||||||
return {
|
|
||||||
runWithSpan("producer") {
|
|
||||||
source.output().send(MessageBuilder.withPayload("test").build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SpringBootConfiguration
|
|
||||||
@EnableAutoConfiguration
|
|
||||||
@EnableBinding(Sink)
|
|
||||||
static class ConsumerConfig {
|
|
||||||
@StreamListener(Sink.INPUT)
|
|
||||||
void consume(String ignored) {
|
|
||||||
runWithSpan("consumer") {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.SpanKind;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.SpringBootConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.integration.channel.DirectChannel;
|
||||||
|
import org.springframework.integration.channel.ExecutorChannel;
|
||||||
|
import org.springframework.integration.support.MessageBuilder;
|
||||||
|
import org.springframework.messaging.Message;
|
||||||
|
import org.springframework.messaging.SubscribableChannel;
|
||||||
|
|
||||||
|
public abstract class AbstractComplexPropagationTest {
|
||||||
|
|
||||||
|
private final Class<?> additionalContextClass;
|
||||||
|
protected InstrumentationExtension testing;
|
||||||
|
|
||||||
|
ConfigurableApplicationContext applicationContext;
|
||||||
|
|
||||||
|
public AbstractComplexPropagationTest(
|
||||||
|
InstrumentationExtension testing, @Nullable Class<?> additionalContextClass) {
|
||||||
|
this.testing = testing;
|
||||||
|
this.additionalContextClass = additionalContextClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
List<Class<?>> contextClasses = new ArrayList<>();
|
||||||
|
contextClasses.add(ExternalQueueConfig.class);
|
||||||
|
if (additionalContextClass != null) {
|
||||||
|
contextClasses.add(additionalContextClass);
|
||||||
|
}
|
||||||
|
SpringApplication springApplication =
|
||||||
|
new SpringApplication(contextClasses.toArray(new Class<?>[0]));
|
||||||
|
springApplication.setDefaultProperties(
|
||||||
|
Collections.singletonMap("spring.main.web-application-type", "none"));
|
||||||
|
applicationContext = springApplication.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
if (applicationContext != null) {
|
||||||
|
applicationContext.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPropagateContextThroughAcomplexFlow() {
|
||||||
|
SubscribableChannel sendChannel =
|
||||||
|
applicationContext.getBean("sendChannel", SubscribableChannel.class);
|
||||||
|
SubscribableChannel receiveChannel =
|
||||||
|
applicationContext.getBean("receiveChannel", SubscribableChannel.class);
|
||||||
|
|
||||||
|
CapturingMessageHandler messageHandler = new CapturingMessageHandler();
|
||||||
|
receiveChannel.subscribe(messageHandler);
|
||||||
|
|
||||||
|
sendChannel.send(MessageBuilder.withPayload("test").setHeader("theAnswer", "42").build());
|
||||||
|
|
||||||
|
messageHandler.join();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("application.sendChannel process").hasKind(SpanKind.CONSUMER),
|
||||||
|
span ->
|
||||||
|
span.hasName("application.receiveChannel process")
|
||||||
|
.hasParent(trace.getSpan(0))
|
||||||
|
.hasKind(SpanKind.CONSUMER),
|
||||||
|
span -> span.hasName("handler").hasParent(trace.getSpan(1))));
|
||||||
|
|
||||||
|
receiveChannel.unsubscribe(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this setup simulates separate producer/consumer and some "external" message queue in between
|
||||||
|
@SpringBootConfiguration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
static class ExternalQueueConfig {
|
||||||
|
@Bean
|
||||||
|
SubscribableChannel sendChannel() {
|
||||||
|
return new ExecutorChannel(Executors.newSingleThreadExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SubscribableChannel receiveChannel() {
|
||||||
|
return new DirectChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
BlockingQueue<Payload> externalQueue() {
|
||||||
|
return new LinkedBlockingQueue<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(destroyMethod = "shutdownNow")
|
||||||
|
ExecutorService consumerThread() {
|
||||||
|
return Executors.newSingleThreadExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
void initialize() {
|
||||||
|
sendChannel().subscribe(message -> externalQueue().offer(Payload.from(message)));
|
||||||
|
|
||||||
|
consumerThread()
|
||||||
|
.execute(
|
||||||
|
() -> {
|
||||||
|
while (!Thread.interrupted()) {
|
||||||
|
try {
|
||||||
|
Payload payload = externalQueue().take();
|
||||||
|
receiveChannel().send(payload.toMessage());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Payload {
|
||||||
|
String body;
|
||||||
|
Map<String, String> headers;
|
||||||
|
|
||||||
|
Payload(String body, Map<String, String> headers) {
|
||||||
|
this.body = body;
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Payload from(Message<?> message) {
|
||||||
|
String body = (String) message.getPayload();
|
||||||
|
Map<String, String> headers =
|
||||||
|
message.getHeaders().entrySet().stream()
|
||||||
|
.filter(kv -> kv.getValue() instanceof String)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, kv -> (String) kv.getValue()));
|
||||||
|
return new Payload(body, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message<String> toMessage() {
|
||||||
|
return MessageBuilder.withPayload(body).copyHeaders(headers).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.SpanKind;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
public abstract class AbstractSpringCloudStreamProducerTest {
|
||||||
|
|
||||||
|
@RegisterExtension RabbitExtension rabbit;
|
||||||
|
|
||||||
|
protected final InstrumentationExtension testing;
|
||||||
|
|
||||||
|
private static final boolean HAS_PRODUCER_SPAN =
|
||||||
|
Boolean.getBoolean("otel.instrumentation.spring-integration.producer.enabled");
|
||||||
|
|
||||||
|
public AbstractSpringCloudStreamProducerTest(
|
||||||
|
InstrumentationExtension testing, Class<?> additionalContextClass) {
|
||||||
|
this.testing = testing;
|
||||||
|
rabbit = new RabbitExtension(additionalContextClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void hasProducerSpan() {
|
||||||
|
assumeTrue(HAS_PRODUCER_SPAN);
|
||||||
|
|
||||||
|
rabbit.getBean("producer", Runnable.class).run();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("producer").hasKind(SpanKind.INTERNAL),
|
||||||
|
span ->
|
||||||
|
span.hasName("testProducer.output publish")
|
||||||
|
.hasKind(SpanKind.PRODUCER)
|
||||||
|
.hasParent(trace.getSpan(0)),
|
||||||
|
span ->
|
||||||
|
span.hasName("testConsumer.input process")
|
||||||
|
.hasKind(SpanKind.CONSUMER)
|
||||||
|
.hasParent(trace.getSpan(1)),
|
||||||
|
span ->
|
||||||
|
span.hasName("consumer")
|
||||||
|
.hasKind(SpanKind.INTERNAL)
|
||||||
|
.hasParent(trace.getSpan(2))));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.SpanKind;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
public abstract class AbstractSpringCloudStreamRabbitTest {
|
||||||
|
|
||||||
|
@RegisterExtension RabbitExtension rabbit;
|
||||||
|
|
||||||
|
protected final InstrumentationExtension testing;
|
||||||
|
|
||||||
|
public AbstractSpringCloudStreamRabbitTest(
|
||||||
|
InstrumentationExtension testing, Class<?> additionalContextClass) {
|
||||||
|
this.testing = testing;
|
||||||
|
rabbit = new RabbitExtension(additionalContextClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPropagateContextThroughRabbitMq() {
|
||||||
|
rabbit.getBean("producer", Runnable.class).run();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("producer").hasKind(SpanKind.INTERNAL),
|
||||||
|
span ->
|
||||||
|
span.hasName("testConsumer.input process")
|
||||||
|
.hasKind(SpanKind.CONSUMER)
|
||||||
|
.hasParent(trace.getSpan(0)),
|
||||||
|
span ->
|
||||||
|
span.hasName("consumer")
|
||||||
|
.hasKind(SpanKind.INTERNAL)
|
||||||
|
.hasParent(trace.getSpan(1))));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.trace.SpanKind;
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||||
|
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.SpringBootConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.integration.channel.DirectChannel;
|
||||||
|
import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper;
|
||||||
|
import org.springframework.messaging.Message;
|
||||||
|
import org.springframework.messaging.SubscribableChannel;
|
||||||
|
import org.springframework.messaging.support.ExecutorSubscribableChannel;
|
||||||
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
|
|
||||||
|
abstract class AbstractSpringIntegrationTracingTest {
|
||||||
|
|
||||||
|
protected final InstrumentationExtension testing;
|
||||||
|
|
||||||
|
private final Class<?> additionalContextClass;
|
||||||
|
|
||||||
|
ConfigurableApplicationContext applicationContext;
|
||||||
|
|
||||||
|
public AbstractSpringIntegrationTracingTest(
|
||||||
|
InstrumentationExtension testing, Class<?> additionalContextClass) {
|
||||||
|
this.testing = testing;
|
||||||
|
this.additionalContextClass = additionalContextClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
List<Class<?>> contextClasses = new ArrayList<>();
|
||||||
|
contextClasses.add(MessageChannelsConfig.class);
|
||||||
|
if (additionalContextClass != null) {
|
||||||
|
contextClasses.add(additionalContextClass);
|
||||||
|
}
|
||||||
|
SpringApplication springApplication =
|
||||||
|
new SpringApplication(contextClasses.toArray(new Class<?>[0]));
|
||||||
|
springApplication.setDefaultProperties(
|
||||||
|
Collections.singletonMap("spring.main.web-application-type", "none"));
|
||||||
|
applicationContext = springApplication.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown() {
|
||||||
|
if (applicationContext != null) {
|
||||||
|
applicationContext.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(
|
||||||
|
value = {
|
||||||
|
"directChannel,application.directChannel process",
|
||||||
|
"executorChannel,executorChannel process"
|
||||||
|
},
|
||||||
|
delimiter = ',')
|
||||||
|
public void shouldPropagateContext(String channelName, String interceptorSpanName) {
|
||||||
|
SubscribableChannel channel =
|
||||||
|
applicationContext.getBean(channelName, SubscribableChannel.class);
|
||||||
|
|
||||||
|
CapturingMessageHandler messageHandler = new CapturingMessageHandler();
|
||||||
|
channel.subscribe(messageHandler);
|
||||||
|
|
||||||
|
channel.send(MessageBuilder.withPayload("test").build());
|
||||||
|
|
||||||
|
Message<?> capturedMessage = messageHandler.join();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> {
|
||||||
|
span.hasName(interceptorSpanName).hasKind(SpanKind.CONSUMER);
|
||||||
|
verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0));
|
||||||
|
},
|
||||||
|
span -> span.hasName("handler").hasParent(trace.getSpan(0))));
|
||||||
|
|
||||||
|
channel.unsubscribe(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotAddInterceptorTwice() {
|
||||||
|
SubscribableChannel channel =
|
||||||
|
applicationContext.getBean("directChannel1", SubscribableChannel.class);
|
||||||
|
|
||||||
|
CapturingMessageHandler messageHandler = new CapturingMessageHandler();
|
||||||
|
channel.subscribe(messageHandler);
|
||||||
|
|
||||||
|
channel.send(MessageBuilder.withPayload("test").build());
|
||||||
|
|
||||||
|
Message<?> capturedMessage = messageHandler.join();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> {
|
||||||
|
span.hasName("application.directChannel2 process").hasKind(SpanKind.CONSUMER);
|
||||||
|
verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0));
|
||||||
|
},
|
||||||
|
span -> span.hasName("handler").hasParent(trace.getSpan(0))));
|
||||||
|
|
||||||
|
channel.unsubscribe(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotCreateAspanWhenThereIsAlreadyAspanInTheContext() {
|
||||||
|
SubscribableChannel channel =
|
||||||
|
applicationContext.getBean("directChannel", SubscribableChannel.class);
|
||||||
|
|
||||||
|
CapturingMessageHandler messageHandler = new CapturingMessageHandler();
|
||||||
|
channel.subscribe(messageHandler);
|
||||||
|
|
||||||
|
testing.runWithSpan(
|
||||||
|
"parent",
|
||||||
|
() -> {
|
||||||
|
channel.send(MessageBuilder.withPayload("test").build());
|
||||||
|
});
|
||||||
|
|
||||||
|
messageHandler.join();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> span.hasName("parent"),
|
||||||
|
span -> span.hasName("handler").hasParent(trace.getSpan(0))));
|
||||||
|
|
||||||
|
channel.unsubscribe(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldHandleMultipleMessageChannelsInAchain() {
|
||||||
|
SubscribableChannel channel1 =
|
||||||
|
applicationContext.getBean("linkedChannel1", SubscribableChannel.class);
|
||||||
|
SubscribableChannel channel2 =
|
||||||
|
applicationContext.getBean("linkedChannel2", SubscribableChannel.class);
|
||||||
|
|
||||||
|
CapturingMessageHandler messageHandler = new CapturingMessageHandler();
|
||||||
|
channel2.subscribe(messageHandler);
|
||||||
|
|
||||||
|
channel1.send(MessageBuilder.withPayload("test").build());
|
||||||
|
|
||||||
|
Message<?> capturedMessage = messageHandler.join();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> {
|
||||||
|
span.hasName("application.linkedChannel1 process").hasKind(SpanKind.CONSUMER);
|
||||||
|
verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0));
|
||||||
|
},
|
||||||
|
span -> span.hasName("handler").hasParent(trace.getSpan(0))));
|
||||||
|
|
||||||
|
channel2.unsubscribe(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void captureMessageHeader() {
|
||||||
|
SubscribableChannel channel =
|
||||||
|
applicationContext.getBean("directChannel", SubscribableChannel.class);
|
||||||
|
|
||||||
|
CapturingMessageHandler messageHandler = new CapturingMessageHandler();
|
||||||
|
channel.subscribe(messageHandler);
|
||||||
|
|
||||||
|
channel.send(
|
||||||
|
MessageBuilder.withPayload("test").setHeader("test-message-header", "test").build());
|
||||||
|
|
||||||
|
Message<?> capturedMessage = messageHandler.join();
|
||||||
|
|
||||||
|
testing.waitAndAssertTraces(
|
||||||
|
trace ->
|
||||||
|
trace.hasSpansSatisfyingExactly(
|
||||||
|
span -> {
|
||||||
|
span.hasName("application.directChannel process").hasKind(SpanKind.CONSUMER);
|
||||||
|
verifyCorrectSpanWasPropagated(capturedMessage, trace.getSpan(0));
|
||||||
|
},
|
||||||
|
span -> span.hasName("handler").hasParent(trace.getSpan(0))));
|
||||||
|
|
||||||
|
channel.unsubscribe(messageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void verifyCorrectSpanWasPropagated(Message<?> capturedMessage, SpanData parentSpan) {
|
||||||
|
String propagatedSpan = (String) capturedMessage.getHeaders().get("traceparent");
|
||||||
|
assertThat(propagatedSpan).contains(parentSpan.getTraceId());
|
||||||
|
assertThat(propagatedSpan).contains(parentSpan.getSpanId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SpringBootConfiguration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
public static class MessageChannelsConfig {
|
||||||
|
|
||||||
|
SubscribableChannel problematicSharedChannel = new DirectChannel();
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SubscribableChannel directChannel() {
|
||||||
|
return new DirectChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SubscribableChannel directChannel1() {
|
||||||
|
return problematicSharedChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SubscribableChannel directChannel2() {
|
||||||
|
return problematicSharedChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SubscribableChannel executorChannel(GlobalChannelInterceptorWrapper otelInterceptor) {
|
||||||
|
ExecutorSubscribableChannel channel =
|
||||||
|
new ExecutorSubscribableChannel(Executors.newSingleThreadExecutor());
|
||||||
|
if (!Boolean.getBoolean("testLatestDeps")) {
|
||||||
|
// spring does not inject the interceptor in 4.1 because ExecutorSubscribableChannel isn't
|
||||||
|
// ChannelInterceptorAware
|
||||||
|
// in later versions spring injects the global interceptor into InterceptableChannel (which
|
||||||
|
// ExecutorSubscribableChannel is)
|
||||||
|
channel.addInterceptor(otelInterceptor.getChannelInterceptor());
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SubscribableChannel linkedChannel1() {
|
||||||
|
return new DirectChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SubscribableChannel linkedChannel2() {
|
||||||
|
return new DirectChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void initialize() {
|
||||||
|
linkedChannel1().subscribe(message -> linkedChannel2().send(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import org.springframework.messaging.Message;
|
||||||
|
import org.springframework.messaging.MessageHandler;
|
||||||
|
|
||||||
|
class CapturingMessageHandler implements MessageHandler {
|
||||||
|
final CompletableFuture<Message<?>> captured = new CompletableFuture<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message<?> message) {
|
||||||
|
runWithSpan(
|
||||||
|
"handler",
|
||||||
|
() -> {
|
||||||
|
captured.complete(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Message<?> join() {
|
||||||
|
return captured.join();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.spring.integration.v4_1;
|
||||||
|
|
||||||
|
import static io.opentelemetry.instrumentation.testing.GlobalTraceUtil.runWithSpan;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
|
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.SpringBootConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.cloud.stream.annotation.EnableBinding;
|
||||||
|
import org.springframework.cloud.stream.annotation.StreamListener;
|
||||||
|
import org.springframework.cloud.stream.messaging.Sink;
|
||||||
|
import org.springframework.cloud.stream.messaging.Source;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.messaging.support.MessageBuilder;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.containers.wait.strategy.Wait;
|
||||||
|
|
||||||
|
public class RabbitExtension implements BeforeEachCallback, AfterEachCallback {
|
||||||
|
|
||||||
|
private GenericContainer<?> rabbitMqContainer;
|
||||||
|
protected ConfigurableApplicationContext producerContext;
|
||||||
|
private ConfigurableApplicationContext consumerContext;
|
||||||
|
|
||||||
|
private final Class<?> additionalContextClass;
|
||||||
|
|
||||||
|
public RabbitExtension(Class<?> additionalContextClass) {
|
||||||
|
this.additionalContextClass = additionalContextClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getBean(String name, Class<T> requiredType) {
|
||||||
|
return producerContext.getBean(name, requiredType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeEach(ExtensionContext context) {
|
||||||
|
rabbitMqContainer =
|
||||||
|
new GenericContainer<>("rabbitmq:latest")
|
||||||
|
.withExposedPorts(5672)
|
||||||
|
.waitingFor(Wait.forLogMessage(".*Server startup complete.*", 1))
|
||||||
|
.withStartupTimeout(Duration.ofMinutes(2));
|
||||||
|
rabbitMqContainer.start();
|
||||||
|
|
||||||
|
SpringApplication producerApp =
|
||||||
|
new SpringApplication(getContextClasses(ProducerConfig.class, additionalContextClass));
|
||||||
|
Map<String, Object> producerProperties = new HashMap<>();
|
||||||
|
producerProperties.put("spring.application.name", "testProducer");
|
||||||
|
producerProperties.put("spring.jmx.enabled", false);
|
||||||
|
producerProperties.put("spring.main.web-application-type", "none");
|
||||||
|
producerProperties.put("spring.rabbitmq.host", rabbitMqContainer.getHost());
|
||||||
|
producerProperties.put("spring.rabbitmq.port", rabbitMqContainer.getMappedPort(5672));
|
||||||
|
producerProperties.put("spring.cloud.stream.bindings.output.destination", "testTopic");
|
||||||
|
producerApp.setDefaultProperties(producerProperties);
|
||||||
|
producerContext = producerApp.run();
|
||||||
|
|
||||||
|
SpringApplication consumerApp =
|
||||||
|
new SpringApplication(
|
||||||
|
getContextClasses(ProducerConfig.ConsumerConfig.class, additionalContextClass));
|
||||||
|
Map<String, Object> consumerProperties = new HashMap<>();
|
||||||
|
consumerProperties.put("spring.application.name", "testConsumer");
|
||||||
|
consumerProperties.put("spring.jmx.enabled", false);
|
||||||
|
consumerProperties.put("spring.main.web-application-type", "none");
|
||||||
|
consumerProperties.put("spring.rabbitmq.host", rabbitMqContainer.getHost());
|
||||||
|
consumerProperties.put("spring.rabbitmq.port", rabbitMqContainer.getMappedPort(5672));
|
||||||
|
consumerProperties.put("spring.cloud.stream.bindings.input.destination", "testTopic");
|
||||||
|
consumerApp.setDefaultProperties(consumerProperties);
|
||||||
|
consumerContext = consumerApp.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEach(ExtensionContext context) {
|
||||||
|
if (rabbitMqContainer != null) {
|
||||||
|
rabbitMqContainer.stop();
|
||||||
|
}
|
||||||
|
if (producerContext != null) {
|
||||||
|
producerContext.close();
|
||||||
|
}
|
||||||
|
if (consumerContext != null) {
|
||||||
|
consumerContext.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Class<?>[] getContextClasses(Class<?> mainContext, Class<?> additionalContext) {
|
||||||
|
List<Class<?>> contextClasses = new ArrayList<>();
|
||||||
|
contextClasses.add(mainContext);
|
||||||
|
if (additionalContext != null) {
|
||||||
|
contextClasses.add(additionalContext);
|
||||||
|
}
|
||||||
|
return contextClasses.toArray(new Class<?>[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SpringBootConfiguration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@EnableBinding(Source.class)
|
||||||
|
static class ProducerConfig {
|
||||||
|
@Autowired Source source;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Runnable producer() {
|
||||||
|
return () ->
|
||||||
|
runWithSpan(
|
||||||
|
"producer",
|
||||||
|
() -> {
|
||||||
|
source.output().send(MessageBuilder.withPayload("test").build());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SpringBootConfiguration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@EnableBinding(Sink.class)
|
||||||
|
static class ConsumerConfig {
|
||||||
|
@StreamListener(Sink.INPUT)
|
||||||
|
void consume(String ignored) {
|
||||||
|
runWithSpan(
|
||||||
|
"consumer",
|
||||||
|
() -> {
|
||||||
|
// do nothing
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue