Improve configuration support for Observability integration.
Closes: #4216
This commit is contained in:
committed by
Mark Paluch
parent
daef8b6e8e
commit
e9ac77c058
@@ -0,0 +1,16 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Annotation to active Spring Data MongoDB's usage of Micrometer's Observation API.
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(MongoMetricsConfiguration.class)
|
||||
public @interface EnableMongoObservability {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Class to configure needed beans for MongoDB + Micrometer.
|
||||
*/
|
||||
public class MongoMetricsConfiguration {
|
||||
|
||||
@Bean
|
||||
MongoObservationCommandListener mongoObservationCommandListener(ObservationRegistry registry) {
|
||||
return new MongoObservationCommandListener(registry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoTracingObservationHandler mongoTracingObservationHandler(Tracer tracer) {
|
||||
return new MongoTracingObservationHandler(tracer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import com.mongodb.client.SynchronousContextProvider;
|
||||
|
||||
/**
|
||||
* Helper functions to ease registration of Spring Data MongoDB's observability.
|
||||
*/
|
||||
public class MongoMetricsConfigurationHelper {
|
||||
|
||||
public static SynchronousContextProvider synchronousContextProvider(Tracer tracer, ObservationRegistry registry) {
|
||||
return () -> new SynchronousTraceRequestContext(tracer).withObservation(Observation.start("name", registry));
|
||||
}
|
||||
|
||||
public static void addObservationHandler(ObservationRegistry registry, Tracer tracer) {
|
||||
registry.observationConfig().observationHandler(new MongoTracingObservationHandler(tracer));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import reactor.core.CoreSubscriber;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import com.mongodb.reactivestreams.client.ReactiveContextProvider;
|
||||
|
||||
/**
|
||||
* Helper functions to ease registration of Spring Data MongoDB's observability.
|
||||
*/
|
||||
public class MongoMetricsReactiveConfigurationHelper {
|
||||
|
||||
public static ReactiveContextProvider reactiveContextProvider(ObservationRegistry registry) {
|
||||
return subscriber -> {
|
||||
if (subscriber instanceof CoreSubscriber<?> coreSubscriber) {
|
||||
return new ReactiveTraceRequestContext(coreSubscriber.currentContext())
|
||||
.withObservation(Observation.start("name", registry));
|
||||
}
|
||||
return new ReactiveTraceRequestContext(Context.empty()).withObservation(Observation.start("name", registry));
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Span;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.handler.TracingObservationHandler;
|
||||
@@ -49,6 +50,10 @@ public class MongoTracingObservationHandler implements TracingObservationHandler
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
public void register(ObservationRegistry observationRegistry) {
|
||||
observationRegistry.observationConfig().observationHandler(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tracer getTracer() {
|
||||
return this.tracer;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class ReactiveTraceRequestContext extends TraceRequestContext {
|
||||
|
||||
ReactiveTraceRequestContext withObservation(Observation value) {
|
||||
|
||||
put(Observation.class, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
ReactiveTraceRequestContext(ContextView context) {
|
||||
super(context.stream().collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.tracing.Span;
|
||||
import io.micrometer.tracing.TraceContext;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class SynchronousTraceRequestContext extends TraceRequestContext {
|
||||
|
||||
SynchronousTraceRequestContext(Tracer tracer) {
|
||||
super(context(tracer));
|
||||
}
|
||||
|
||||
SynchronousTraceRequestContext withObservation(Observation value) {
|
||||
|
||||
put(Observation.class, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private static Map<Object, Object> context(Tracer tracer) {
|
||||
|
||||
Map<Object, Object> map = new ConcurrentHashMap<>();
|
||||
|
||||
Span currentSpan = tracer.currentSpan();
|
||||
|
||||
if (currentSpan == null) {
|
||||
return map;
|
||||
}
|
||||
|
||||
map.put(Span.class, currentSpan);
|
||||
map.put(TraceContext.class, currentSpan.context());
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.mongodb.RequestContext;
|
||||
|
||||
/**
|
||||
* A {@link Map}-based {@link RequestContext}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Greg Turnquist
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class TraceRequestContext implements RequestContext {
|
||||
|
||||
private final Map<Object, Object> map;
|
||||
|
||||
public TraceRequestContext() {
|
||||
this(new HashMap<>());
|
||||
}
|
||||
|
||||
public TraceRequestContext(Map<Object, Object> context) {
|
||||
this.map = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T get(Object key) {
|
||||
return (T) map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(Object key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object key) {
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Map.Entry<Object, Object>> stream() {
|
||||
return map.entrySet().stream();
|
||||
}
|
||||
}
|
||||
@@ -77,10 +77,10 @@ class MongoObservationCommandListenerForTracingTests {
|
||||
void successfullyCompletedCommandShouldCreateSpanWhenParentSampleInRequestContext() {
|
||||
|
||||
// given
|
||||
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt();
|
||||
TraceRequestContext traceRequestContext = createTestRequestContextWithParentObservationAndStartIt();
|
||||
|
||||
// when
|
||||
commandStartedAndSucceeded(testRequestContext);
|
||||
commandStartedAndSucceeded(traceRequestContext);
|
||||
|
||||
// then
|
||||
assertThatMongoSpanIsClientWithTags().hasIpThatIsBlank().hasPortThatIsNotSet();
|
||||
@@ -91,10 +91,10 @@ class MongoObservationCommandListenerForTracingTests {
|
||||
|
||||
// given
|
||||
handler.setSetRemoteIpAndPortEnabled(true);
|
||||
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt();
|
||||
TraceRequestContext traceRequestContext = createTestRequestContextWithParentObservationAndStartIt();
|
||||
|
||||
// when
|
||||
commandStartedAndSucceeded(testRequestContext);
|
||||
commandStartedAndSucceeded(traceRequestContext);
|
||||
|
||||
// then
|
||||
assertThatMongoSpanIsClientWithTags().hasIpThatIsNotBlank().hasPortThatIsSet();
|
||||
@@ -104,10 +104,10 @@ class MongoObservationCommandListenerForTracingTests {
|
||||
void commandWithErrorShouldCreateTimerWhenParentSampleInRequestContext() {
|
||||
|
||||
// given
|
||||
TestRequestContext testRequestContext = createTestRequestContextWithParentObservationAndStartIt();
|
||||
TraceRequestContext traceRequestContext = createTestRequestContextWithParentObservationAndStartIt();
|
||||
|
||||
// when
|
||||
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
|
||||
new ConnectionDescription( //
|
||||
new ServerId( //
|
||||
new ClusterId("description"), //
|
||||
@@ -115,17 +115,17 @@ class MongoObservationCommandListenerForTracingTests {
|
||||
"database", "insert", //
|
||||
new BsonDocument("collection", new BsonString("user"))));
|
||||
listener.commandFailed( //
|
||||
new CommandFailedEvent(testRequestContext, 0, null, "insert", 0, new IllegalAccessException()));
|
||||
new CommandFailedEvent(traceRequestContext, 0, null, "insert", 0, new IllegalAccessException()));
|
||||
|
||||
// then
|
||||
assertThatMongoSpanIsClientWithTags().assertThatThrowable().isInstanceOf(IllegalAccessException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a parent {@link Observation} then wrap it inside a {@link TestRequestContext}.
|
||||
* Create a parent {@link Observation} then wrap it inside a {@link TraceRequestContext}.
|
||||
*/
|
||||
@NotNull
|
||||
private TestRequestContext createTestRequestContextWithParentObservationAndStartIt() {
|
||||
private TraceRequestContext createTestRequestContextWithParentObservationAndStartIt() {
|
||||
|
||||
Observation parent = Observation.start("name", observationRegistry);
|
||||
return TestRequestContext.withObservation(parent);
|
||||
@@ -134,13 +134,13 @@ class MongoObservationCommandListenerForTracingTests {
|
||||
/**
|
||||
* Execute MongoDB's {@link com.mongodb.event.CommandListener#commandStarted(CommandStartedEvent)} and
|
||||
* {@link com.mongodb.event.CommandListener#commandSucceeded(CommandSucceededEvent)} operations against the
|
||||
* {@link TestRequestContext} in order to inject some test data.
|
||||
* {@link TraceRequestContext} in order to inject some test data.
|
||||
*
|
||||
* @param testRequestContext
|
||||
* @param traceRequestContext
|
||||
*/
|
||||
private void commandStartedAndSucceeded(TestRequestContext testRequestContext) {
|
||||
private void commandStartedAndSucceeded(TraceRequestContext traceRequestContext) {
|
||||
|
||||
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
|
||||
new ConnectionDescription( //
|
||||
new ServerId( //
|
||||
new ClusterId("description"), //
|
||||
@@ -148,7 +148,7 @@ class MongoObservationCommandListenerForTracingTests {
|
||||
"database", "insert", //
|
||||
new BsonDocument("collection", new BsonString("user"))));
|
||||
|
||||
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(traceRequestContext, 0, null, "insert", null, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -87,7 +87,7 @@ class MongoObservationCommandListenerTests {
|
||||
void commandStartedShouldNotInstrumentWhenNoParentSampleInRequestContext() {
|
||||
|
||||
// when
|
||||
listener.commandStarted(new CommandStartedEvent(new TestRequestContext(), 0, null, "some name", "", null));
|
||||
listener.commandStarted(new CommandStartedEvent(new TraceRequestContext(), 0, null, "some name", "", null));
|
||||
|
||||
// then
|
||||
assertThat(meterRegistry).hasNoMetrics();
|
||||
@@ -98,17 +98,17 @@ class MongoObservationCommandListenerTests {
|
||||
|
||||
// given
|
||||
Observation parent = Observation.start("name", observationRegistry);
|
||||
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent);
|
||||
TraceRequestContext traceRequestContext = TestRequestContext.withObservation(parent);
|
||||
|
||||
// when
|
||||
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
|
||||
new ConnectionDescription( //
|
||||
new ServerId( //
|
||||
new ClusterId("description"), //
|
||||
new ServerAddress("localhost", 1234))),
|
||||
"database", "insert", //
|
||||
new BsonDocument("collection", new BsonString("user"))));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(traceRequestContext, 0, null, "insert", null, 0));
|
||||
|
||||
// then
|
||||
assertThatTimerRegisteredWithTags();
|
||||
@@ -119,17 +119,17 @@ class MongoObservationCommandListenerTests {
|
||||
|
||||
// given
|
||||
Observation parent = Observation.start("name", observationRegistry);
|
||||
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent);
|
||||
TraceRequestContext traceRequestContext = TestRequestContext.withObservation(parent);
|
||||
|
||||
// when
|
||||
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
|
||||
new ConnectionDescription( //
|
||||
new ServerId( //
|
||||
new ClusterId("description"), //
|
||||
new ServerAddress("localhost", 1234))), //
|
||||
"database", "aggregate", //
|
||||
new BsonDocument("aggregate", new BsonString("user"))));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "aggregate", null, 0));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(traceRequestContext, 0, null, "aggregate", null, 0));
|
||||
|
||||
// then
|
||||
assertThatTimerRegisteredWithTags();
|
||||
@@ -140,12 +140,12 @@ class MongoObservationCommandListenerTests {
|
||||
|
||||
// given
|
||||
Observation parent = Observation.start("name", observationRegistry);
|
||||
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent);
|
||||
TraceRequestContext traceRequestContext = TestRequestContext.withObservation(parent);
|
||||
|
||||
// when
|
||||
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, null, "database", "insert",
|
||||
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, null, "database", "insert",
|
||||
new BsonDocument("collection", new BsonString("user"))));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(testRequestContext, 0, null, "insert", null, 0));
|
||||
listener.commandSucceeded(new CommandSucceededEvent(traceRequestContext, 0, null, "insert", null, 0));
|
||||
|
||||
// then
|
||||
assertThat(meterRegistry).hasTimerWithNameAndTags(HighCardinalityCommandKeyNames.MONGODB_COMMAND.asString(),
|
||||
@@ -157,10 +157,10 @@ class MongoObservationCommandListenerTests {
|
||||
|
||||
// given
|
||||
Observation parent = Observation.start("name", observationRegistry);
|
||||
TestRequestContext testRequestContext = TestRequestContext.withObservation(parent);
|
||||
TraceRequestContext traceRequestContext = TestRequestContext.withObservation(parent);
|
||||
|
||||
// when
|
||||
listener.commandStarted(new CommandStartedEvent(testRequestContext, 0, //
|
||||
listener.commandStarted(new CommandStartedEvent(traceRequestContext, 0, //
|
||||
new ConnectionDescription( //
|
||||
new ServerId( //
|
||||
new ClusterId("description"), //
|
||||
@@ -168,7 +168,7 @@ class MongoObservationCommandListenerTests {
|
||||
"database", "insert", //
|
||||
new BsonDocument("collection", new BsonString("user"))));
|
||||
listener.commandFailed( //
|
||||
new CommandFailedEvent(testRequestContext, 0, null, "insert", 0, new IllegalAccessException()));
|
||||
new CommandFailedEvent(traceRequestContext, 0, null, "insert", 0, new IllegalAccessException()));
|
||||
|
||||
// then
|
||||
assertThatTimerRegisteredWithTags();
|
||||
|
||||
@@ -1,78 +1,26 @@
|
||||
/*
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.mongodb.observability;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.mongodb.RequestContext;
|
||||
|
||||
/**
|
||||
* A {@link Map}-based {@link RequestContext}. (For test purposes only).
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Greg Turnquist
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class TestRequestContext implements RequestContext {
|
||||
|
||||
private final Map<Object, Object> map = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public <T> T get(Object key) {
|
||||
return (T) map.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasKey(Object key) {
|
||||
return map.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return map.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, Object value) {
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object key) {
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Map.Entry<Object, Object>> stream() {
|
||||
return map.entrySet().stream();
|
||||
}
|
||||
class TestRequestContext extends TraceRequestContext {
|
||||
|
||||
static TestRequestContext withObservation(Observation value) {
|
||||
return new TestRequestContext(value);
|
||||
}
|
||||
|
||||
TestRequestContext testRequestContext = new TestRequestContext();
|
||||
testRequestContext.put(Observation.class, value);
|
||||
return testRequestContext;
|
||||
private TestRequestContext(Observation value) {
|
||||
super(context(value));
|
||||
}
|
||||
|
||||
private static Map<Object, Object> context(Observation value) {
|
||||
|
||||
Map<Object, Object> map = new ConcurrentHashMap<>();
|
||||
|
||||
map.put(Observation.class, value);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,15 +23,16 @@ import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.tracing.Tracer;
|
||||
import io.micrometer.tracing.test.SampleTestRunner;
|
||||
import io.micrometer.tracing.test.reporter.BuildingBlocks;
|
||||
import io.micrometer.tracing.test.simple.SimpleTracer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
@@ -56,8 +57,8 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import com.mongodb.ContextProvider;
|
||||
import com.mongodb.MongoClientSettings;
|
||||
import com.mongodb.RequestContext;
|
||||
import com.mongodb.WriteConcern;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.SynchronousContextProvider;
|
||||
@@ -71,7 +72,6 @@ import com.mongodb.client.SynchronousContextProvider;
|
||||
* @author Greg Turnquist
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Disabled("Run this manually to visually test spans in Zipkin")
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
public class ZipkinIntegrationTests extends SampleTestRunner {
|
||||
@@ -139,33 +139,41 @@ public class ZipkinIntegrationTests extends SampleTestRunner {
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoDatabaseFactory mongoDatabaseFactory(MongoObservationCommandListener commandListener,
|
||||
ObservationRegistry registry) {
|
||||
MongoDatabaseFactory mongoDatabaseFactory(MongoClientSettings settings) {
|
||||
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), "observable");
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoClientSettings mongoClientSettings(MongoObservationCommandListener commandListener,
|
||||
ContextProvider contextProvider) {
|
||||
|
||||
ConnectionString connectionString = new ConnectionString(
|
||||
String.format("mongodb://%s:%s/?w=majority&uuidrepresentation=javaLegacy", "127.0.0.1", 27017));
|
||||
|
||||
RequestContext requestContext = TestRequestContext.withObservation(Observation.start("name", registry));
|
||||
SynchronousContextProvider contextProvider = () -> requestContext;
|
||||
|
||||
MongoClientSettings settings = MongoClientSettings.builder() //
|
||||
.addCommandListener(commandListener) //
|
||||
.contextProvider(contextProvider) //
|
||||
.applyConnectionString(connectionString) //
|
||||
.build();
|
||||
|
||||
return new SimpleMongoClientDatabaseFactory(MongoClients.create(settings), "observable");
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Bean
|
||||
MappingMongoConverter mongoConverter(MongoDatabaseFactory factory) {
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
mappingContext.afterPropertiesSet();
|
||||
SynchronousContextProvider contextProvider(ObservationRegistry registry) {
|
||||
return () -> TestRequestContext.withObservation(Observation.start("name", registry));
|
||||
}
|
||||
|
||||
@Bean
|
||||
MappingMongoConverter mongoConverter(MongoMappingContext mappingContext, MongoDatabaseFactory factory) {
|
||||
return new MappingMongoConverter(new DefaultDbRefResolver(factory), mappingContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoMappingContext mappingContext() {
|
||||
return new MongoMappingContext();
|
||||
}
|
||||
|
||||
@Bean
|
||||
MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory, MongoConverter mongoConverter) {
|
||||
|
||||
@@ -203,5 +211,11 @@ public class ZipkinIntegrationTests extends SampleTestRunner {
|
||||
ObservationRegistry registry() {
|
||||
return OBSERVATION_REGISTRY;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Tracer tracer() {
|
||||
return new SimpleTracer();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,87 @@ include::{root-target}_conventions.adoc[]
|
||||
include::{root-target}_metrics.adoc[]
|
||||
|
||||
include::{root-target}_spans.adoc[]
|
||||
|
||||
[[observability.registration]]
|
||||
== Observability Registration
|
||||
|
||||
Spring Data MongoDB currently has the most up-to-date code to support Observability in your MongoDB application.
|
||||
These changes, however, haven't been picked up by Spring Boot (yet).
|
||||
Until those changes are applied, if you wish to use Spring Data MongoDB's flavor of Observability, you must carry out the following steps.
|
||||
|
||||
. First of all, you must opt into Spring Data MongoDB's configuration settings by adding the `@EnableMongoObservability` to either your `@SpringBootApplication` class or one of your configuration classes.
|
||||
. Your project must include *Spring Boot Actuator*.
|
||||
. Next you must add one of the following bean definitions based on whether you're using non-reactive or reactive Spring Data MongoDB.
|
||||
+
|
||||
.Registering a synchronous (non-reactive) MongoDB Micrometer setup
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
MongoClientSettingsBuilderCustomizer mongoMetricsSynchronousContextProvider(Tracer tracer,
|
||||
ObservationRegistry registry) {
|
||||
return (clientSettingsBuilder) -> {
|
||||
clientSettingsBuilder.contextProvider( //
|
||||
MongoMetricsConfigurationHelper.synchronousContextProvider(tracer, registry));
|
||||
};
|
||||
}
|
||||
----
|
||||
====
|
||||
+
|
||||
.Registering a reactive MongoDB Micrometer setup
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
MongoClientSettingsBuilderCustomizer mongoMetricsReactiveContextProvider(ObservationRegistry registry) {
|
||||
return (clientSettingsBuilder) -> {
|
||||
clientSettingsBuilder.contextProvider( //
|
||||
MongoMetricsReactiveConfigurationHelper.reactiveContextProvider(registry));
|
||||
};
|
||||
}
|
||||
----
|
||||
====
|
||||
+
|
||||
IMPORTANT: ONLY add one of these two bean definitions!
|
||||
. Add the following bean definition to listen for MongoDB command events and record them with Micrometer.
|
||||
+
|
||||
.Registering to listen for MongoDB commands.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
MongoClientSettingsBuilderCustomizer mongoObservationCommandListenerCustomizer(MongoDBContainer mongoDBContainer,
|
||||
MongoObservationCommandListener commandListener) {
|
||||
return (clientSettingsBuilder) -> clientSettingsBuilder //
|
||||
.addCommandListener(commandListener);
|
||||
}
|
||||
----
|
||||
====
|
||||
. Add the following bean definition to register Spring Data MongoDB's trace observation handler
|
||||
+
|
||||
.Registering
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
ObservationRegistryCustomizer<ObservationRegistry> mongoTracingHandlerCustomizer(
|
||||
MongoTracingObservationHandler handler) {
|
||||
return handler::register;
|
||||
}
|
||||
----
|
||||
====
|
||||
. Disable Spring Boot's autoconfigured MongoDB command listener and enable tracing manually by adding the following properties to your `application.properties`
|
||||
+
|
||||
.Custom settings to apply
|
||||
====
|
||||
[source]
|
||||
----
|
||||
# Disable Spring Boot's autoconfigured tracing
|
||||
management.metrics.mongo.command.enabled=false
|
||||
# Enable it manually
|
||||
management.tracing.enabled=true
|
||||
----
|
||||
Be sure to add any other relevant settings needed to configure the tracer you are using based upon Micrometer's reference documentation.
|
||||
====
|
||||
|
||||
This should do it! You are now running with Spring Data MongoDB's usage of Spring Observability's `Observation` API.
|
||||
|
||||
Reference in New Issue
Block a user