Skip to content

Commit bd60fac

Browse files
committed
* Add a DocumentedObservation infrastructure
1 parent 7a41bfb commit bd60fac

File tree

8 files changed

+237
-12
lines changed

8 files changed

+237
-12
lines changed

spring-integration-core/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@
2121
import org.springframework.integration.history.MessageHistory;
2222
import org.springframework.integration.support.management.metrics.MetricsCaptor;
2323
import org.springframework.integration.support.management.metrics.SampleFacade;
24-
import org.springframework.integration.support.management.observation.MessageReceiverContext;
24+
import org.springframework.integration.support.management.observation.IntegrationObservations;
2525
import org.springframework.integration.support.utils.IntegrationUtils;
2626
import org.springframework.messaging.Message;
2727
import org.springframework.messaging.MessageHandler;
2828
import org.springframework.util.Assert;
2929

30-
import io.micrometer.observation.Observation;
3130
import io.micrometer.observation.ObservationRegistry;
3231
import reactor.core.CoreSubscriber;
3332

@@ -62,9 +61,7 @@ public void handleMessage(Message<?> message) {
6261
}
6362

6463
private void handleWithObservation(Message<?> message, ObservationRegistry observationRegistry) {
65-
Observation.createNotStarted(CONSUME_OBSERVATION_NAME, new MessageReceiverContext(message), observationRegistry)
66-
.lowCardinalityKeyValue("type", "handler")
67-
.lowCardinalityKeyValue("name", getComponentName() == null ? "unknown" : getComponentName())
64+
IntegrationObservations.handlerObservation(observationRegistry, message, getComponentName())
6865
.observe(() -> doHandleMessage(message));
6966
}
7067

spring-integration-core/src/main/java/org/springframework/integration/support/management/IntegrationManagement.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ public interface IntegrationManagement extends NamedComponent, DisposableBean {
3939

4040
String SEND_TIMER_NAME = METER_PREFIX + "send";
4141

42-
String CONSUME_OBSERVATION_NAME = SEND_TIMER_NAME;
43-
4442
String RECEIVE_COUNTER_NAME = METER_PREFIX + "receive";
4543

4644
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.support.management.observation;
18+
19+
import io.micrometer.common.KeyValues;
20+
21+
/**
22+
* A default {@link MessageReceiverObservationConvention} implementation.
23+
* Provides low cardinalities as a {@link IntegrationObservation.HandlerTags} values.
24+
*
25+
* @author Artem Bilan
26+
*
27+
* @since 6.0
28+
*/
29+
class DefaultMessageReceiverObservationConvention implements MessageReceiverObservationConvention {
30+
31+
@Override
32+
public KeyValues getLowCardinalityKeyValues(MessageReceiverContext context) {
33+
return KeyValues.of(
34+
IntegrationObservation.HandlerTags.COMPONENT_NAME.withValue(context.getHandlerName()),
35+
IntegrationObservation.HandlerTags.COMPONENT_TYPE.withValue("handler"));
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.support.management.observation;
18+
19+
import io.micrometer.common.docs.KeyName;
20+
import io.micrometer.observation.docs.DocumentedObservation;
21+
22+
/**
23+
* The {@link DocumentedObservation} implementation for Spring Integration infrastructure.
24+
*
25+
* @author Artem Bilan
26+
*
27+
* @since 6.0
28+
*/
29+
public enum IntegrationObservation implements DocumentedObservation {
30+
31+
/**
32+
* Observation for message handlers.
33+
*/
34+
HANDLER {
35+
@Override
36+
public String getName() {
37+
return "spring.integration.handler";
38+
}
39+
40+
@Override
41+
public Class<DefaultMessageReceiverObservationConvention> getDefaultConvention() {
42+
return DefaultMessageReceiverObservationConvention.class;
43+
}
44+
45+
@Override
46+
public KeyName[] getLowCardinalityKeyNames() {
47+
return HandlerTags.values();
48+
}
49+
50+
};
51+
52+
/**
53+
* Key names for message handler observations.
54+
*/
55+
enum HandlerTags implements KeyName {
56+
57+
/**
58+
* Name of the message handler component.
59+
*/
60+
COMPONENT_NAME {
61+
@Override
62+
public String asString() {
63+
return "name";
64+
}
65+
66+
},
67+
68+
/**
69+
* Type of the component - 'handler'.
70+
*/
71+
COMPONENT_TYPE {
72+
@Override
73+
public String asString() {
74+
return "type";
75+
}
76+
77+
}
78+
79+
}
80+
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.support.management.observation;
18+
19+
import org.springframework.messaging.Message;
20+
21+
import io.micrometer.observation.Observation;
22+
import io.micrometer.observation.ObservationRegistry;
23+
24+
/**
25+
* A convenient factory for {@link IntegrationObservation} instances.
26+
*
27+
* @author Artem Bilan
28+
*
29+
* @since 6.0
30+
*/
31+
public final class IntegrationObservations {
32+
33+
private static final DefaultMessageReceiverObservationConvention DEFAULT_MESSAGE_RECEIVER_OBSERVATION_CONVENTION =
34+
new DefaultMessageReceiverObservationConvention();
35+
36+
/**
37+
* The factory method for the {@link IntegrationObservation#HANDLER}.
38+
* @param observationRegistry the {@link ObservationRegistry} to use.
39+
* @param message the {@link Message} as a context for observation.
40+
* @param handlerName the name of message handler as a context value.
41+
* @return an {@link Observation} to observe a message handling.
42+
*/
43+
public static Observation handlerObservation(ObservationRegistry observationRegistry, Message<?> message,
44+
String handlerName) {
45+
46+
return IntegrationObservation.HANDLER.observation(null, DEFAULT_MESSAGE_RECEIVER_OBSERVATION_CONVENTION,
47+
new MessageReceiverContext(message, handlerName), observationRegistry);
48+
}
49+
50+
private IntegrationObservations() {
51+
}
52+
53+
}

spring-integration-core/src/main/java/org/springframework/integration/support/management/observation/MessageReceiverContext.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.integration.support.management.observation;
1818

19+
import org.springframework.lang.Nullable;
1920
import org.springframework.messaging.Message;
2021

2122
import io.micrometer.observation.transport.ReceiverContext;
@@ -29,9 +30,23 @@
2930
*/
3031
public class MessageReceiverContext extends ReceiverContext<Message<?>> {
3132

32-
public MessageReceiverContext(Message<?> message) {
33+
private final Message<?> message;
34+
35+
private final String handlerName;
36+
37+
public MessageReceiverContext(Message<?> message, @Nullable String handlerName) {
3338
super((carrier, key) -> carrier.getHeaders().get(key, String.class));
34-
setCarrier(message);
39+
this.message = message;
40+
this.handlerName = handlerName != null ? handlerName : "unknown";
41+
}
42+
43+
@Override
44+
public Message<?> getCarrier() {
45+
return this.message;
46+
}
47+
48+
public String getHandlerName() {
49+
return this.handlerName;
3550
}
3651

3752
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.support.management.observation;
18+
19+
import org.springframework.messaging.Message;
20+
21+
import io.micrometer.observation.Observation;
22+
import io.micrometer.observation.transport.ReceiverContext;
23+
24+
/**
25+
* The {@link ReceiverContext} extension for {@link Message} context.
26+
*
27+
* @author Artem Bilan
28+
*
29+
* @since 6.0
30+
*/
31+
public interface MessageReceiverObservationConvention
32+
extends Observation.ObservationConvention<MessageReceiverContext> {
33+
34+
@Override
35+
default boolean supportsContext(Observation.Context context) {
36+
return context instanceof MessageReceiverContext;
37+
}
38+
39+
@Override
40+
default String getContextualName(MessageReceiverContext context) {
41+
return context.getHandlerName() + " receive";
42+
}
43+
44+
}

spring-integration-core/src/test/java/org/springframework/integration/channel/interceptor/ObservationPropagationChannelInterceptorTests.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import org.springframework.integration.handler.BridgeHandler;
4444
import org.springframework.integration.support.MutableMessage;
4545
import org.springframework.integration.support.MutableMessageBuilder;
46-
import org.springframework.integration.support.management.IntegrationManagement;
4746
import org.springframework.integration.support.management.observation.MessageSenderContext;
4847
import org.springframework.lang.Nullable;
4948
import org.springframework.messaging.Message;
@@ -202,7 +201,7 @@ void observationPropagatedOverQueueChannel() throws InterruptedException {
202201
}
203202

204203
@Test
205-
void observationContextPropagatedOverDirectChannel() {
204+
void observationContextPropagatedOverExecutorChannel() {
206205
BridgeHandler handler = new BridgeHandler();
207206
handler.registerObservationRegistry(this.observationRegistry);
208207
handler.setBeanName("testBridge");
@@ -234,7 +233,7 @@ void observationContextPropagatedOverDirectChannel() {
234233
.hasSize(2)
235234
.satisfies(simpleSpans -> assertSpans(simpleSpans)
236235
.hasASpanWithName("sending")
237-
.assertThatASpanWithNameEqualTo(IntegrationManagement.CONSUME_OBSERVATION_NAME)
236+
.assertThatASpanWithNameEqualTo("testBridge receive")
238237
.hasTag("foo", "some foo value")
239238
.hasTag("bar", "some bar value")
240239
.hasTag("type", "handler")

0 commit comments

Comments
 (0)