Skip to content

Commit e9ac77c

Browse files
gregturnmp911de
authored andcommitted
Improve configuration support for Observability integration.
Closes: spring-projects#4216
1 parent daef8b6 commit e9ac77c

13 files changed

+370
-102
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.springframework.data.mongodb.observability;
2+
3+
import java.lang.annotation.*;
4+
5+
import org.springframework.context.annotation.Import;
6+
7+
/**
8+
* Annotation to active Spring Data MongoDB's usage of Micrometer's Observation API.
9+
*/
10+
@Inherited
11+
@Documented
12+
@Target(ElementType.TYPE)
13+
@Retention(RetentionPolicy.RUNTIME)
14+
@Import(MongoMetricsConfiguration.class)
15+
public @interface EnableMongoObservability {
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.springframework.data.mongodb.observability;
2+
3+
import io.micrometer.observation.ObservationRegistry;
4+
import io.micrometer.tracing.Tracer;
5+
import org.springframework.context.annotation.Bean;
6+
7+
/**
8+
* Class to configure needed beans for MongoDB + Micrometer.
9+
*/
10+
public class MongoMetricsConfiguration {
11+
12+
@Bean
13+
MongoObservationCommandListener mongoObservationCommandListener(ObservationRegistry registry) {
14+
return new MongoObservationCommandListener(registry);
15+
}
16+
17+
@Bean
18+
MongoTracingObservationHandler mongoTracingObservationHandler(Tracer tracer) {
19+
return new MongoTracingObservationHandler(tracer);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.springframework.data.mongodb.observability;
2+
3+
import io.micrometer.observation.Observation;
4+
import io.micrometer.observation.ObservationRegistry;
5+
import io.micrometer.tracing.Tracer;
6+
7+
import com.mongodb.client.SynchronousContextProvider;
8+
9+
/**
10+
* Helper functions to ease registration of Spring Data MongoDB's observability.
11+
*/
12+
public class MongoMetricsConfigurationHelper {
13+
14+
public static SynchronousContextProvider synchronousContextProvider(Tracer tracer, ObservationRegistry registry) {
15+
return () -> new SynchronousTraceRequestContext(tracer).withObservation(Observation.start("name", registry));
16+
}
17+
18+
public static void addObservationHandler(ObservationRegistry registry, Tracer tracer) {
19+
registry.observationConfig().observationHandler(new MongoTracingObservationHandler(tracer));
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.springframework.data.mongodb.observability;
2+
3+
import io.micrometer.observation.Observation;
4+
import io.micrometer.observation.ObservationRegistry;
5+
import reactor.core.CoreSubscriber;
6+
import reactor.util.context.Context;
7+
8+
import com.mongodb.reactivestreams.client.ReactiveContextProvider;
9+
10+
/**
11+
* Helper functions to ease registration of Spring Data MongoDB's observability.
12+
*/
13+
public class MongoMetricsReactiveConfigurationHelper {
14+
15+
public static ReactiveContextProvider reactiveContextProvider(ObservationRegistry registry) {
16+
return subscriber -> {
17+
if (subscriber instanceof CoreSubscriber<?> coreSubscriber) {
18+
return new ReactiveTraceRequestContext(coreSubscriber.currentContext())
19+
.withObservation(Observation.start("name", registry));
20+
}
21+
return new ReactiveTraceRequestContext(Context.empty()).withObservation(Observation.start("name", registry));
22+
};
23+
}
24+
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/observability/MongoTracingObservationHandler.java

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.mongodb.observability;
1717

1818
import io.micrometer.observation.Observation;
19+
import io.micrometer.observation.ObservationRegistry;
1920
import io.micrometer.tracing.Span;
2021
import io.micrometer.tracing.Tracer;
2122
import io.micrometer.tracing.handler.TracingObservationHandler;
@@ -49,6 +50,10 @@ public MongoTracingObservationHandler(Tracer tracer) {
4950
this.tracer = tracer;
5051
}
5152

53+
public void register(ObservationRegistry observationRegistry) {
54+
observationRegistry.observationConfig().observationHandler(this);
55+
}
56+
5257
@Override
5358
public Tracer getTracer() {
5459
return this.tracer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.springframework.data.mongodb.observability;
2+
3+
import io.micrometer.observation.Observation;
4+
import reactor.util.context.ContextView;
5+
6+
import java.util.Map;
7+
import java.util.stream.Collectors;
8+
9+
class ReactiveTraceRequestContext extends TraceRequestContext {
10+
11+
ReactiveTraceRequestContext withObservation(Observation value) {
12+
13+
put(Observation.class, value);
14+
return this;
15+
}
16+
17+
ReactiveTraceRequestContext(ContextView context) {
18+
super(context.stream().collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)));
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.springframework.data.mongodb.observability;
2+
3+
import io.micrometer.observation.Observation;
4+
import io.micrometer.tracing.Span;
5+
import io.micrometer.tracing.TraceContext;
6+
import io.micrometer.tracing.Tracer;
7+
8+
import java.util.Map;
9+
import java.util.concurrent.ConcurrentHashMap;
10+
11+
class SynchronousTraceRequestContext extends TraceRequestContext {
12+
13+
SynchronousTraceRequestContext(Tracer tracer) {
14+
super(context(tracer));
15+
}
16+
17+
SynchronousTraceRequestContext withObservation(Observation value) {
18+
19+
put(Observation.class, value);
20+
return this;
21+
}
22+
23+
private static Map<Object, Object> context(Tracer tracer) {
24+
25+
Map<Object, Object> map = new ConcurrentHashMap<>();
26+
27+
Span currentSpan = tracer.currentSpan();
28+
29+
if (currentSpan == null) {
30+
return map;
31+
}
32+
33+
map.put(Span.class, currentSpan);
34+
map.put(TraceContext.class, currentSpan.context());
35+
36+
return map;
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2013-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+
package org.springframework.data.mongodb.observability;
17+
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
import java.util.stream.Stream;
21+
22+
import com.mongodb.RequestContext;
23+
24+
/**
25+
* A {@link Map}-based {@link RequestContext}.
26+
*
27+
* @author Marcin Grzejszczak
28+
* @author Greg Turnquist
29+
* @since 4.0.0
30+
*/
31+
class TraceRequestContext implements RequestContext {
32+
33+
private final Map<Object, Object> map;
34+
35+
public TraceRequestContext() {
36+
this(new HashMap<>());
37+
}
38+
39+
public TraceRequestContext(Map<Object, Object> context) {
40+
this.map = context;
41+
}
42+
43+
@Override
44+
public <T> T get(Object key) {
45+
return (T) map.get(key);
46+
}
47+
48+
@Override
49+
public boolean hasKey(Object key) {
50+
return map.containsKey(key);
51+
}
52+
53+
@Override
54+
public boolean isEmpty() {
55+
return map.isEmpty();
56+
}
57+
58+
@Override
59+
public void put(Object key, Object value) {
60+
map.put(key, value);
61+
}
62+
63+
@Override
64+
public void delete(Object key) {
65+
map.remove(key);
66+
}
67+
68+
@Override
69+
public int size() {
70+
return map.size();
71+
}
72+
73+
@Override
74+
public Stream<Map.Entry<Object, Object>> stream() {
75+
return map.entrySet().stream();
76+
}
77+
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/observability/MongoObservationCommandListenerForTracingTests.java

+14-14
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ void setup() {
7777
void successfullyCompletedCommandShouldCreateSpanWhenParentSampleInRequestContext() {
7878

7979
// given
80-
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt();
80+
TraceRequestContext traceRequestContext = createTestRequestContextWithParentObservationAndStartIt();
8181

8282
// when
83-
commandStartedAndSucceeded(testRequestContext);
83+
commandStartedAndSucceeded(traceRequestContext);
8484

8585
// then
8686
assertThatMongoSpanIsClientWithTags().hasIpThatIsBlank().hasPortThatIsNotSet();
@@ -91,10 +91,10 @@ void successfullyCompletedCommandShouldCreateSpanWithAddressInfoWhenParentSample
9191

9292
// given
9393
handler.setSetRemoteIpAndPortEnabled(true);
94-
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt();
94+
TraceRequestContext traceRequestContext = createTestRequestContextWithParentObservationAndStartIt();
9595

9696
// when
97-
commandStartedAndSucceeded(testRequestContext);
97+
commandStartedAndSucceeded(traceRequestContext);
9898

9999
// then
100100
assertThatMongoSpanIsClientWithTags().hasIpThatIsNotBlank().hasPortThatIsSet();
@@ -104,28 +104,28 @@ void successfullyCompletedCommandShouldCreateSpanWithAddressInfoWhenParentSample
104104
void commandWithErrorShouldCreateTimerWhenParentSampleInRequestContext() {
105105

106106
// given
107-
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt();
107+
TraceRequestContext traceRequestContext = createTestRequestContextWithParentObservationAndStartIt();
108108

109109
// when
110-
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
110+
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
111111
new ConnectionDescription( //
112112
new ServerId( //
113113
new ClusterId("description"), //
114114
new ServerAddress("localhost", 1234))), //
115115
"database", "insert", //
116116
new BsonDocument("collection", new BsonString("user"))));
117117
listener.commandFailed( //
118-
new CommandFailedEvent(testRequestContext, 0, null, "insert", 0, new IllegalAccessException()));
118+
new CommandFailedEvent(traceRequestContext, 0, null, "insert", 0, new IllegalAccessException()));
119119

120120
// then
121121
assertThatMongoSpanIsClientWithTags().assertThatThrowable().isInstanceOf(IllegalAccessException.class);
122122
}
123123

124124
/**
125-
* Create a parent {@link Observation} then wrap it inside a {@link TestRequestContext}.
125+
* Create a parent {@link Observation} then wrap it inside a {@link TraceRequestContext}.
126126
*/
127127
@NotNull
128-
private TestRequestContext createTestRequestContextWithParentObservationAndStartIt() {
128+
private TraceRequestContext createTestRequestContextWithParentObservationAndStartIt() {
129129

130130
Observation parent = Observation.start("name", observationRegistry);
131131
return TestRequestContext.withObservation(parent);
@@ -134,21 +134,21 @@ private TestRequestContext createTestRequestContextWithParentObservationAndStart
134134
/**
135135
* Execute MongoDB's {@link com.mongodb.event.CommandListener#commandStarted(CommandStartedEvent)} and
136136
* {@link com.mongodb.event.CommandListener#commandSucceeded(CommandSucceededEvent)} operations against the
137-
* {@link TestRequestContext} in order to inject some test data.
137+
* {@link TraceRequestContext} in order to inject some test data.
138138
*
139-
* @param testRequestContext
139+
* @param traceRequestContext
140140
*/
141-
private void commandStartedAndSucceeded(TestRequestContext testRequestContext) {
141+
private void commandStartedAndSucceeded(TraceRequestContext traceRequestContext) {
142142

143-
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
143+
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
144144
new ConnectionDescription( //
145145
new ServerId( //
146146
new ClusterId("description"), //
147147
new ServerAddress("localhost", 1234))), //
148148
"database", "insert", //
149149
new BsonDocument("collection", new BsonString("user"))));
150150

151-
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0));
151+
listener.commandSucceeded(new CommandSucceededEvent(traceRequestContext, 0, null, "insert", null, 0));
152152
}
153153

154154
/**

0 commit comments

Comments
 (0)