Skip to content

Commit b3c0fbb

Browse files
committed
Guard command completion listener against unsupported observation context.
We now no longer attempt to complete the Observation if the context is not a MongoDB one. For commands that target the admin database and run within a parent observation, we still might have an Observation but that one points to the parent invocation and not the MongoDB one as we do not record commands for the admin database. Closes #4481
1 parent 51de522 commit b3c0fbb

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

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

+6-8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
*/
1616
package org.springframework.data.mongodb.observability;
1717

18+
import io.micrometer.observation.Observation;
19+
import io.micrometer.observation.ObservationRegistry;
20+
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
21+
1822
import org.apache.commons.logging.Log;
1923
import org.apache.commons.logging.LogFactory;
2024
import org.springframework.lang.Nullable;
@@ -27,10 +31,6 @@
2731
import com.mongodb.event.CommandStartedEvent;
2832
import com.mongodb.event.CommandSucceededEvent;
2933

30-
import io.micrometer.observation.Observation;
31-
import io.micrometer.observation.ObservationRegistry;
32-
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
33-
3434
/**
3535
* Implement MongoDB's {@link CommandListener} using Micrometer's {@link Observation} API.
3636
*
@@ -133,11 +133,10 @@ public void commandSucceeded(CommandSucceededEvent event) {
133133
}
134134

135135
Observation observation = requestContext.getOrDefault(ObservationThreadLocalAccessor.KEY, null);
136-
if (observation == null) {
136+
if (observation == null || !(observation.getContext()instanceof MongoHandlerContext context)) {
137137
return;
138138
}
139139

140-
MongoHandlerContext context = (MongoHandlerContext) observation.getContext();
141140
context.setCommandSucceededEvent(event);
142141

143142
if (log.isDebugEnabled()) {
@@ -157,11 +156,10 @@ public void commandFailed(CommandFailedEvent event) {
157156
}
158157

159158
Observation observation = requestContext.getOrDefault(ObservationThreadLocalAccessor.KEY, null);
160-
if (observation == null) {
159+
if (observation == null || !(observation.getContext()instanceof MongoHandlerContext context)) {
161160
return;
162161
}
163162

164-
MongoHandlerContext context = (MongoHandlerContext) observation.getContext();
165163
context.setCommandFailedEvent(event);
166164

167165
if (log.isDebugEnabled()) {

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

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

1818
import static io.micrometer.core.tck.MeterRegistryAssert.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import io.micrometer.common.KeyValues;
22+
import io.micrometer.core.instrument.MeterRegistry;
23+
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
24+
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
25+
import io.micrometer.observation.Observation;
26+
import io.micrometer.observation.ObservationRegistry;
27+
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
1928

2029
import org.bson.BsonDocument;
2130
import org.bson.BsonString;
@@ -33,18 +42,12 @@
3342
import com.mongodb.event.CommandStartedEvent;
3443
import com.mongodb.event.CommandSucceededEvent;
3544

36-
import io.micrometer.common.KeyValues;
37-
import io.micrometer.core.instrument.MeterRegistry;
38-
import io.micrometer.core.instrument.observation.DefaultMeterObservationHandler;
39-
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
40-
import io.micrometer.observation.Observation;
41-
import io.micrometer.observation.ObservationRegistry;
42-
4345
/**
4446
* Series of test cases exercising {@link MongoObservationCommandListener}.
4547
*
4648
* @author Marcin Grzejszczak
4749
* @author Greg Turnquist
50+
* @author Mark Paluch
4851
*/
4952
class MongoObservationCommandListenerTests {
5053

@@ -176,6 +179,38 @@ void commandWithErrorShouldCreateTimerWhenParentSampleInRequestContext() {
176179
assertThatTimerRegisteredWithTags();
177180
}
178181

182+
@Test // GH-4481
183+
void completionShouldIgnoreIncompatibleObservationContext() {
184+
185+
// given
186+
RequestContext traceRequestContext = getContext();
187+
188+
Observation observation = mock(Observation.class);
189+
traceRequestContext.put(ObservationThreadLocalAccessor.KEY, observation);
190+
191+
// when
192+
listener.commandSucceeded(new CommandSucceededEvent(traceRequestContext, 0, null, "insert", null, 0));
193+
194+
verify(observation).getContext();
195+
verifyNoMoreInteractions(observation);
196+
}
197+
198+
@Test // GH-4481
199+
void failureShouldIgnoreIncompatibleObservationContext() {
200+
201+
// given
202+
RequestContext traceRequestContext = getContext();
203+
204+
Observation observation = mock(Observation.class);
205+
traceRequestContext.put(ObservationThreadLocalAccessor.KEY, observation);
206+
207+
// when
208+
listener.commandFailed(new CommandFailedEvent(traceRequestContext, 0, null, "insert", 0, null));
209+
210+
verify(observation).getContext();
211+
verifyNoMoreInteractions(observation);
212+
}
213+
179214
private RequestContext getContext() {
180215
return ((SynchronousContextProvider) ContextProviderFactory.create(observationRegistry)).getContext();
181216
}

0 commit comments

Comments
 (0)