Skip to content

Commit

Permalink
Add OpenTracing command listener to Mongo client.
Browse files Browse the repository at this point in the history
  • Loading branch information
juazugas committed Dec 14, 2020
1 parent f280048 commit 03a7217
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 2 deletions.
6 changes: 6 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<opentracing-concurrent.version>0.2.0</opentracing-concurrent.version>
<opentracing-jdbc.version>0.0.12</opentracing-jdbc.version>
<opentracing-kafka.version>0.1.13</opentracing-kafka.version>
<opentracing-mongo.version>0.1.5</opentracing-mongo.version>
<jaeger.version>0.34.3</jaeger.version>
<quarkus-http.version>3.0.16.Final</quarkus-http.version>
<jboss-servlet-api_4.0_spec.version>1.0.0.Final</jboss-servlet-api_4.0_spec.version>
Expand Down Expand Up @@ -2584,6 +2585,11 @@
<artifactId>opentracing-kafka-client</artifactId>
<version>${opentracing-kafka.version}</version>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-mongo-common</artifactId>
<version>${opentracing-mongo.version}</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
Expand Down
6 changes: 6 additions & 0 deletions docs/src/main/asciidoc/mongodb.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,12 @@ This behavior must first be enabled by setting the `quarkus.mongodb.metrics.enab
So when you access the `/q/metrics` endpoint of your application you will have information about the connection pool status.
When using link:microprofile-metrics[SmallRye Metrics], connection pool metrics will be available under the `vendor` scope.

== Tracing

If you are using the `quarkus-smallrye-opentracing` extension, `quarkus-mongodb-client` can register traces about the commands executed.
This behavior must first be enabled by setting the `quarkus.mongodb.tracing.enabled` property to `true` in your `application.properties`.

Read the link:opentracing[OpenTracing] guide, to configure and how to use the jaeger tracer.

== The legacy client

Expand Down
22 changes: 22 additions & 0 deletions docs/src/main/asciidoc/opentracing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,28 @@ mp.messaging.incoming.prices.interceptor.classes=io.opentracing.contrib.kafka.Tr

NOTE: `interceptor.classes` accept a list of classes separated by a comma.


=== MongoDB client

The https://github.com/opentracing-contrib/java-mongo-driver[Mongo Driver instrumentation] will add a span for each command executed by your application. To enable it, add the following dependency to your pom.xml:

[source, xml]
----
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-mongo-common</artifactId>
</dependency>
----

It contains the OpenTracing CommandListener that will be registered on the configuration of the mongo client.
Following the link:mongodb[MongoDB guide], the command listener will be registered defining the config property as follows:

[source, properties]
----
# Enable tracing commands in mongodb client
quarkus.mongodb.tracing.enabled=true
----

[[configuration-reference]]
== Jaeger Configuration Reference

Expand Down
21 changes: 21 additions & 0 deletions extensions/mongodb-client/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@
<artifactId>quarkus-resteasy-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-opentracing-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-mongo-common</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-mock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-util</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ public class MongoClientBuildTimeConfig {
@ConfigItem(name = "metrics.enabled")
public boolean metricsEnabled;

/**
* Whether or not tracing spans of driver commands are sent in case the smallrye-opentracing extension is present.
*/
@ConfigItem(name = "tracing.enabled")
public boolean tracingEnabled;

/**
* If set to true, the default clients will always be created even if there are no injection points that use them
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;

Expand All @@ -35,6 +37,8 @@
import io.quarkus.arc.processor.BuildExtension;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.arc.processor.InjectionPointInfo;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -45,6 +49,7 @@
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.SslNativeConfigBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem;
Expand All @@ -55,6 +60,7 @@
import io.quarkus.mongodb.runtime.MongoClientSupport;
import io.quarkus.mongodb.runtime.MongoClients;
import io.quarkus.mongodb.runtime.MongodbConfig;
import io.quarkus.mongodb.tracing.MongoTracingCommandListener;
import io.quarkus.runtime.metrics.MetricsFactory;
import io.quarkus.smallrye.health.deployment.spi.HealthBuildItem;

Expand All @@ -66,6 +72,20 @@ public class MongoClientProcessor {
private static final DotName MONGO_CLIENT = DotName.createSimple(MongoClient.class.getName());
private static final DotName REACTIVE_MONGO_CLIENT = DotName.createSimple(ReactiveMongoClient.class.getName());

static class MongoClientTracingEnabled implements BooleanSupplier {
MongoClientBuildTimeConfig mConfig;

@Override
public boolean getAsBoolean() {
return mConfig.tracingEnabled;
}
}

@BuildStep(onlyIf = MongoClientTracingEnabled.class)
IndexDependencyBuildItem addMongoTracingDependencies() {
return new IndexDependencyBuildItem("io.opentracing.contrib", "opentracing-mongo-common");
}

@BuildStep
CodecProviderBuildItem collectCodecProviders(CombinedIndexBuildItem indexBuildItem) {
Collection<ClassInfo> codecProviderClasses = indexBuildItem.getIndex()
Expand Down Expand Up @@ -93,10 +113,20 @@ BsonDiscriminatorBuildItem collectBsonDiscriminators(CombinedIndexBuildItem inde
}

@BuildStep
CommandListenerBuildItem collectCommandListeners(CombinedIndexBuildItem indexBuildItem) {
CommandListenerBuildItem collectCommandListeners(CombinedIndexBuildItem indexBuildItem, Capabilities capabilities,
MongoClientBuildTimeConfig buildTimeConfig) {
Predicate<String> isTracingCommandListenerPredicate = name -> name
.equalsIgnoreCase("io.opentracing.contrib.mongo.common.TracingCommandListener");
Collection<ClassInfo> commandListenerClasses = indexBuildItem.getIndex()
.getAllKnownImplementors(DotName.createSimple(CommandListener.class.getName()));
List<String> names = commandListenerClasses.stream().map(ci -> ci.name().toString()).collect(Collectors.toList());
List<String> names = commandListenerClasses.stream()
.map(ci -> ci.name().toString())
// filter the TracingCommandListener to avoid the non-default constructor class.
.filter(isTracingCommandListenerPredicate.negate())
.collect(Collectors.toList());
if (buildTimeConfig.tracingEnabled && capabilities.isPresent(Capability.OPENTRACING)) {
names.add(MongoTracingCommandListener.class.getName());
}
return new CommandListenerBuildItem(names);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.quarkus.mongodb;

import static org.assertj.core.api.Assertions.assertThat;

import javax.inject.Inject;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.mongodb.client.MongoClient;

import io.opentracing.mock.MockSpan;
import io.opentracing.mock.MockTracer;
import io.opentracing.util.GlobalTracer;
import io.opentracing.util.GlobalTracerTestUtil;
import io.quarkus.mongodb.tracing.MongoTracingCommandListener;
import io.quarkus.test.QuarkusUnitTest;

/**
* Test the inclusion and config of the {@link MongoTracingCommandListener}.
*
* @see io.quarkus.smallrye.opentracing.deployment.TracingTest
*/
public class MongoTracingCommandListenerTest extends MongoTestBase {

@Inject
MongoClient client;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(
() -> ShrinkWrap.create(JavaArchive.class).addClasses(MongoTestBase.class))
.withConfigurationResource("application-tracing-mongo.properties");

static MockTracer mockTracer = new MockTracer();
static {
GlobalTracer.register(mockTracer);
}

@BeforeEach
public void before() {
mockTracer.reset();
}

@AfterAll
public static void afterAll() {
GlobalTracerTestUtil.resetGlobalTracer();
}

@AfterEach
void cleanup() {
if (client != null) {
client.close();
}
}

@Test
void testClientInitialization() {
assertThat(mockTracer.finishedSpans()).hasSize(0);

assertThat(client.listDatabaseNames().first()).isNotEmpty();

assertThat(mockTracer.finishedSpans()).hasSize(1);
MockSpan span = mockTracer.finishedSpans().get(0);
assertThat(span.operationName()).isEqualTo("listDatabases");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
quarkus.mongodb.connection-string=mongodb://localhost:27018
quarkus.mongodb.tracing.enabled=true


quarkus.jaeger.enabled=true
quarkus.jaeger.service-name=tracing-test
quarkus.jaeger.sampler-param=1
quarkus.jaeger.sampler-type=const
11 changes: 11 additions & 0 deletions extensions/mongodb-client/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-opentracing</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-mongo-common</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>svm</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.quarkus.mongodb.tracing;

import com.mongodb.event.CommandFailedEvent;
import com.mongodb.event.CommandListener;
import com.mongodb.event.CommandStartedEvent;
import com.mongodb.event.CommandSucceededEvent;

import io.opentracing.contrib.mongo.common.TracingCommandListener;
import io.opentracing.util.GlobalTracer;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;

/**
* Command Listener for Mongo client delegated to {@link TracingCommandListener}.
*
*/
public class MongoTracingCommandListener implements CommandListener {

private static final Logger LOGGER = LoggerFactory.getLogger(MongoTracingCommandListener.class);

private TracingCommandListener delegate;

public MongoTracingCommandListener() {
this.delegate = new TracingCommandListener.Builder(GlobalTracer.get()).build();
LOGGER.debug("TracingCommandListener Delegate created");
}

@Override
public void commandStarted(CommandStartedEvent event) {
LOGGER.trace("commandStarted event " + event.getCommandName());
delegate.commandStarted(event);
}

@Override
public void commandFailed(CommandFailedEvent event) {
LOGGER.trace("commandFailed event " + event.getCommandName());
delegate.commandFailed(event);
}

@Override
public void commandSucceeded(CommandSucceededEvent event) {
LOGGER.trace("commandSucceded event " + event.getCommandName());
delegate.commandSucceeded(event);
}

}

0 comments on commit 03a7217

Please sign in to comment.