diff --git a/dd-java-agent/dd-java-agent.gradle b/dd-java-agent/dd-java-agent.gradle index 81ccc6669ad..f4086300939 100644 --- a/dd-java-agent/dd-java-agent.gradle +++ b/dd-java-agent/dd-java-agent.gradle @@ -97,13 +97,6 @@ dependencies { testCompile deps.opentracingMock testCompile deps.testLogging testCompile deps.guava - testCompile group: 'org.assertj', name: 'assertj-core', version: '2.9.+' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' - - testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2' - testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2' - // run embedded mongodb for integration testing - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' } tasks.withType(Test).configureEach { diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy deleted file mode 100644 index 4ddc61f661f..00000000000 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy +++ /dev/null @@ -1,45 +0,0 @@ -import datadog.trace.api.DDTags -import io.opentracing.Span -import io.opentracing.tag.Tags -import org.bson.BsonArray -import org.bson.BsonDocument -import org.bson.BsonString -import spock.lang.Shared -import spock.lang.Specification - -import static datadog.trace.instrumentation.mongo.MongoClientDecorator.DECORATE - -class MongoClientDecoratorTest extends Specification { - - @Shared - def query1, query2 - - def setupSpec() { - query1 = new BsonDocument("find", new BsonString("show")) - query1.put("stuff", new BsonString("secret")) - - - query2 = new BsonDocument("insert", new BsonString("table")) - def nestedDoc = new BsonDocument("count", new BsonString("show")) - nestedDoc.put("id", new BsonString("secret")) - query2.put("docs", new BsonArray(Arrays.asList(new BsonString("secret"), nestedDoc))) - } - - def "test query scrubbing"() { - setup: - def span = Mock(Span) - // all "secret" strings should be scrubbed out of these queries - - when: - DECORATE.onStatement(span, query) - - then: - 1 * span.setTag(Tags.DB_STATEMENT.key, expected) - 1 * span.setTag(DDTags.RESOURCE_NAME, expected) - 0 * _ - - where: - query << [query1, query2] - expected = query.toString().replaceAll("secret", "?") - } -} diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java b/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java deleted file mode 100644 index e3dd177876b..00000000000 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package datadog.trace.instrumentation.mongo; - -import static org.assertj.core.api.Java6Assertions.assertThat; - -import com.mongodb.ServerAddress; -import com.mongodb.connection.ClusterId; -import com.mongodb.connection.ConnectionDescription; -import com.mongodb.connection.ServerId; -import com.mongodb.event.CommandStartedEvent; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.api.DDSpanTypes; -import io.opentracing.tag.Tags; -import java.util.Arrays; -import java.util.List; -import org.bson.BsonArray; -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.junit.Test; - -public class MongoClientInstrumentationTest { - - private static ConnectionDescription makeConnection() { - return new ConnectionDescription(new ServerId(new ClusterId(), new ServerAddress())); - } - - @Test - public void mongoSpan() { - final CommandStartedEvent cmd = - new CommandStartedEvent(1, makeConnection(), "databasename", "query", new BsonDocument()); - - final DDSpan span = new DDTracer().buildSpan("foo").start(); - MongoClientDecorator.DECORATE.afterStart(span); - MongoClientDecorator.DECORATE.onStatement(span, cmd.getCommand()); - - assertThat(span.context().getSpanType()).isEqualTo("mongodb"); - assertThat(span.context().getResourceName()) - .isEqualTo(span.context().getTags().get("db.statement")); - assertThat(span.getSpanType()).isEqualTo(DDSpanTypes.MONGO); - } - - @Test - public void queryScrubbing() { - // all "secret" strings should be scrubbed out of these queries - final BsonDocument query1 = new BsonDocument("find", new BsonString("show")); - query1.put("stuff", new BsonString("secret")); - final BsonDocument query2 = new BsonDocument("insert", new BsonString("table")); - final BsonDocument query2_1 = new BsonDocument("count", new BsonString("show")); - query2_1.put("id", new BsonString("secret")); - query2.put("docs", new BsonArray(Arrays.asList(new BsonString("secret"), query2_1))); - final List queries = Arrays.asList(query1, query2); - for (final BsonDocument query : queries) { - final CommandStartedEvent cmd = - new CommandStartedEvent(1, makeConnection(), "databasename", "query", query); - - final DDSpan span = new DDTracer().buildSpan("foo").start(); - MongoClientDecorator.DECORATE.afterStart(span); - MongoClientDecorator.DECORATE.onStatement(span, cmd.getCommand()); - - assertThat(span.getSpanType()).isEqualTo(DDSpanTypes.MONGO); - assertThat(span.getTags().get(Tags.DB_STATEMENT.getKey())) - .isEqualTo(query.toString().replaceAll("secret", "?")); - } - } -} diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle b/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle deleted file mode 100644 index df7028ecb5d..00000000000 --- a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle +++ /dev/null @@ -1,27 +0,0 @@ -muzzle { - pass { - group = "org.mongodb" - module = "mongodb-driver-async" - versions = "[3.3,)" - extraDependency 'org.mongodb:mongo-java-driver:3.3.0' - assertInverse = true - } -} - -apply from: "${rootDir}/gradle/java.gradle" - -dependencies { - // use mongo listener - compile(project(':dd-java-agent:instrumentation:mongo-3.1')) { - transitive = false - } - compileOnly group: 'org.mongodb', name: 'mongo-java-driver', version: '3.3.0' - compileOnly group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' - - compile project(':dd-java-agent:agent-tooling') - - compile deps.bytebuddy - compile deps.opentracing - annotationProcessor deps.autoservice - implementation deps.autoservice -} diff --git a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle similarity index 80% rename from dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle rename to dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle index 5f1a0d2a0af..747c30ad60a 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle @@ -28,9 +28,8 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - - testCompile group: 'org.assertj', name: 'assertj-core', version: '2.9.+' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' + testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' latestDepTestCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '+' diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java similarity index 77% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java index 08474ce55b6..a5d4782cdc6 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java @@ -1,5 +1,9 @@ package datadog.trace.instrumentation.mongo; +import com.mongodb.connection.ClusterId; +import com.mongodb.connection.ConnectionDescription; +import com.mongodb.connection.ConnectionId; +import com.mongodb.connection.ServerId; import com.mongodb.event.CommandStartedEvent; import datadog.trace.agent.decorator.DatabaseClientDecorator; import datadog.trace.api.DDSpanTypes; @@ -48,19 +52,25 @@ protected String dbUser(final CommandStartedEvent event) { @Override protected String dbInstance(final CommandStartedEvent event) { + // Use description if set. + final ConnectionDescription connectionDescription = event.getConnectionDescription(); + if (connectionDescription != null) { + final ConnectionId connectionId = connectionDescription.getConnectionId(); + if (connectionId != null) { + final ServerId serverId = connectionId.getServerId(); + if (serverId != null) { + ClusterId clusterId = serverId.getClusterId(); + if (clusterId != null) { + String description = clusterId.getDescription(); + if (description != null) { + return description; + } + } + } + } + } + // Fallback to db name. return event.getDatabaseName(); - // This would be the "proper" db.instance: - // final ConnectionDescription connectionDescription = event.getConnectionDescription(); - // if (connectionDescription != null) { - // final ConnectionId connectionId = connectionDescription.getConnectionId(); - // if (connectionId != null) { - // final ServerId serverId = connectionId.getServerId(); - // if (serverId != null) { - // return serverId.toString(); - // } - // } - // } - // return null; } public Span onStatement(final Span span, final BsonDocument statement) { diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy new file mode 100644 index 00000000000..66ded8bb60e --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy @@ -0,0 +1,260 @@ +import com.mongodb.MongoClient +import com.mongodb.MongoClientOptions +import com.mongodb.MongoTimeoutException +import com.mongodb.ServerAddress +import com.mongodb.client.MongoCollection +import com.mongodb.client.MongoDatabase +import datadog.opentracing.DDSpan +import datadog.trace.agent.test.asserts.TraceAssert +import datadog.trace.api.DDSpanTypes +import io.opentracing.tag.Tags +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import spock.lang.Shared + +import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + +class MongoClientTest extends MongoBaseTest { + + @Shared + MongoClient client + + def setup() throws Exception { + client = new MongoClient(new ServerAddress("localhost", port), + MongoClientOptions.builder() + .description("some-description") + .build()) + } + + def cleanup() throws Exception { + client?.close() + client = null + } + + def "test create collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + db.createCollection(collectionName) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{\"create\":\"$collectionName\",\"capped\":\"?\"}") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test create collection no description"() { + setup: + MongoDatabase db = new MongoClient("localhost", port).getDatabase(dbName) + + when: + db.createCollection(collectionName) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{\"create\":\"$collectionName\",\"capped\":\"?\"}", dbName) + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test get collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + int count = db.getCollection(collectionName).count() + + then: + count == 0 + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test insert"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + collection.insertOne(new Document("password", "SECRET")) + + then: + collection.count() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}") + } + trace(1, 1) { + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test update"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + def coll = db.getCollection(collectionName) + coll.insertOne(new Document("password", "OLDPW")) + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = collection.updateOne( + new BsonDocument("password", new BsonString("OLDPW")), + new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW")))) + + then: + result.modifiedCount == 1 + collection.count() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{\"update\":\"?\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}") + } + trace(1, 1) { + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test delete"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + def coll = db.getCollection(collectionName) + coll.insertOne(new Document("password", "SECRET")) + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))) + + then: + result.deletedCount == 1 + collection.count() == 0 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{\"delete\":\"?\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}") + } + trace(1, 1) { + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test error"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + collection.updateOne(new BsonDocument(), new BsonDocument()) + + then: + thrown(IllegalArgumentException) + // Unfortunately not caught by our instrumentation. + assertTraces(0) {} + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test client failure"() { + setup: + def options = MongoClientOptions.builder().serverSelectionTimeout(10).build() + def client = new MongoClient(new ServerAddress("localhost", UNUSABLE_PORT), [], options) + + when: + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + + then: + thrown(MongoTimeoutException) + // Unfortunately not caught by our instrumentation. + assertTraces(0) {} + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def mongoSpan(TraceAssert trace, int index, String statement, String instance = "some-description", Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "mongo" + operationName "mongo.query" + resourceName { + assert it.replace(" ", "") == statement + return true + } + spanType DDSpanTypes.MONGO + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$Tags.COMPONENT.key" "java-mongo" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$Tags.DB_INSTANCE.key" instance + "$Tags.DB_STATEMENT.key" { + it.replace(" ", "") == statement + } + "$Tags.DB_TYPE.key" "mongo" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } +} diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle new file mode 100644 index 00000000000..0461926bfb0 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle @@ -0,0 +1,47 @@ +// Set properties before any plugins get loaded +ext { + // Since we're using CompletableFutures in the test... + minJavaVersionForTests = JavaVersion.VERSION_1_8 +} + +muzzle { + pass { + group = "org.mongodb" + module = "mongodb-driver-async" + versions = "[3.3,)" + extraDependency 'org.mongodb:mongo-java-driver:3.3.0' + assertInverse = true + } +} + +apply from: "${rootDir}/gradle/java.gradle" + +apply plugin: 'org.unbroken-dome.test-sets' + +testSets { + latestDepTest { + dirName = 'test' + } +} + +dependencies { + // use mongo listener + compile(project(':dd-java-agent:instrumentation:mongo:driver-3.1')) { + transitive = false + } + compileOnly group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' + + compile project(':dd-java-agent:agent-tooling') + + compile deps.bytebuddy + compile deps.opentracing + annotationProcessor deps.autoservice + implementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' + + testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' + latestDepTestCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '+' +} diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy new file mode 100644 index 00000000000..1ba2cdc099d --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -0,0 +1,298 @@ +import com.mongodb.ConnectionString +import com.mongodb.async.SingleResultCallback +import com.mongodb.async.client.MongoClient +import com.mongodb.async.client.MongoClientSettings +import com.mongodb.async.client.MongoClients +import com.mongodb.async.client.MongoCollection +import com.mongodb.async.client.MongoDatabase +import com.mongodb.client.result.DeleteResult +import com.mongodb.client.result.UpdateResult +import com.mongodb.connection.ClusterSettings +import datadog.opentracing.DDSpan +import datadog.trace.agent.test.asserts.TraceAssert +import datadog.trace.api.DDSpanTypes +import io.opentracing.tag.Tags +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import spock.lang.Shared +import spock.lang.Timeout + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CountDownLatch + +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + +@Timeout(10) +class MongoAsyncClientTest extends MongoBaseTest { + + @Shared + MongoClient client + + def setup() throws Exception { + client = MongoClients.create( + MongoClientSettings.builder() + .clusterSettings( + ClusterSettings.builder() + .description("some-description") + .applyConnectionString(new ConnectionString("mongodb://localhost:$port")) + .build()) + .build()) + } + + def cleanup() throws Exception { + client?.close() + client = null + } + + def "test create collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + db.createCollection(collectionName, toCallback {}) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" || + it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test create collection no description"() { + setup: + MongoDatabase db = MongoClients.create("mongodb://localhost:$port").getDatabase(dbName) + + when: + db.createCollection(collectionName, toCallback {}) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, { + assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" || + it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + }, dbName) + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test get collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + def count = new CompletableFuture() + db.getCollection(collectionName).count toCallback { count.complete(it) } + + then: + count.get() == 0 + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test insert"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def count = new CompletableFuture() + collection.insertOne(new Document("password", "SECRET"), toCallback { + collection.count toCallback { count.complete(it) } + }) + + then: + count.get() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}" || + it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}" + true + } + } + trace(1, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test update"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + def coll = db.getCollection(collectionName) + def latch2 = new CountDownLatch(1) + coll.insertOne(new Document("password", "OLDPW"), toCallback { latch2.countDown() }) + latch2.await() + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = new CompletableFuture() + def count = new CompletableFuture() + collection.updateOne( + new BsonDocument("password", new BsonString("OLDPW")), + new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))), toCallback { + result.complete(it) + collection.count toCallback { count.complete(it) } + }) + + then: + result.get().modifiedCount == 1 + count.get() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"update\":\"?\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}" || + it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}" + true + } + } + trace(1, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test delete"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + def coll = db.getCollection(collectionName) + def latch2 = new CountDownLatch(1) + coll.insertOne(new Document("password", "SECRET"), toCallback { latch2.countDown() }) + latch2.await() + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = new CompletableFuture() + def count = new CompletableFuture() + collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")), toCallback { + result.complete(it) + collection.count toCallback { count.complete(it) } + }) + + then: + result.get().deletedCount == 1 + count.get() == 0 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"delete\":\"?\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}" || + it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" + true + } + } + trace(1, 1) { + mongoSpan(it, 0) { + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + SingleResultCallback toCallback(Closure closure) { + return new SingleResultCallback() { + @Override + void onResult(Object result, Throwable t) { + if (t) { + closure.call(t) + } else { + closure.call(result) + } + } + } + } + + def mongoSpan(TraceAssert trace, int index, Closure statementEval, String instance = "some-description", Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "mongo" + operationName "mongo.query" + resourceName statementEval + spanType DDSpanTypes.MONGO + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$Tags.COMPONENT.key" "java-mongo" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$Tags.DB_INSTANCE.key" instance + "$Tags.DB_STATEMENT.key" statementEval + "$Tags.DB_TYPE.key" "mongo" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } +} diff --git a/dd-java-agent/instrumentation/mongo/mongo.gradle b/dd-java-agent/instrumentation/mongo/mongo.gradle new file mode 100644 index 00000000000..f91b66b0cca --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/mongo.gradle @@ -0,0 +1,28 @@ +apply from: "${rootDir}/gradle/java.gradle" + +dependencies { + testAnnotationProcessor deps.autoservice + testImplementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' +} + +// Forcing strict test execution order (no parallel execution) to ensure proper mongo executable initialization. +List testTasks = [] +tasks.withType(Test) { Test testTask -> + testTasks.each { + testTask.shouldRunAfter(it) + } + testTasks.add(testTask) +} +subprojects { + afterEvaluate { + tasks.withType(Test) { Test testTask -> + testTasks.each { + testTask.shouldRunAfter(it) + } + testTasks.add(testTask) + } + } +} diff --git a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy new file mode 100644 index 00000000000..c945733e356 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy @@ -0,0 +1,54 @@ +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.utils.PortUtils +import de.flapdoodle.embed.mongo.MongodExecutable +import de.flapdoodle.embed.mongo.MongodProcess +import de.flapdoodle.embed.mongo.MongodStarter +import de.flapdoodle.embed.mongo.config.IMongodConfig +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder +import de.flapdoodle.embed.mongo.config.Net +import de.flapdoodle.embed.mongo.distribution.Version +import de.flapdoodle.embed.process.runtime.Network +import spock.lang.Shared + +/** + * Testing needs to be in a centralized project. + * If tests in multiple different projects are using embedded mongo, + * they downloader is at risk of a race condition. + */ +class MongoBaseTest extends AgentTestRunner { + // https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo#executable-collision + private static final MongodStarter STARTER = MongodStarter.getDefaultInstance() + + @Shared + int port = PortUtils.randomOpenPort() + @Shared + MongodExecutable mongodExe + @Shared + MongodProcess mongod + + def setup() throws Exception { + final IMongodConfig mongodConfig = + new MongodConfigBuilder() + .version(Version.Main.PRODUCTION) + .net(new Net("localhost", port, Network.localhostIsIPv6())) + .build() + + mongodExe = STARTER.prepare(mongodConfig) + mongod = mongodExe.start() + } + + def cleanup() throws Exception { + mongod?.stop() + mongod = null + mongodExe?.stop() + mongodExe = null + } + + def "test port open"() { + when: + new Socket("localhost", port) + + then: + noExceptionThrown() + } +} diff --git a/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java b/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java new file mode 100644 index 00000000000..d830b1adf33 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java @@ -0,0 +1,12 @@ +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import net.bytebuddy.agent.builder.AgentBuilder; + +@AutoService(Instrumenter.class) +public class NoOpInstrumentation implements Instrumenter { + + @Override + public AgentBuilder instrument(final AgentBuilder agentBuilder) { + return agentBuilder; + } +} diff --git a/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java b/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java index f5729f94c64..cbb51e6784d 100644 --- a/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java +++ b/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java @@ -41,7 +41,7 @@ protected String dbUser(final MemcachedConnection session) { @Override protected String dbInstance(final MemcachedConnection connection) { - return connection.connectionsStatus(); + return null; } public Span onOperation(final Span span, final String methodName) { diff --git a/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy b/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy index 826c2312ce6..2f02de169e2 100644 --- a/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy +++ b/dd-java-agent/instrumentation/spymemcached-2.12/src/test/groovy/datadog/trace/instrumentation/spymemcached/SpymemcachedTest.groovy @@ -635,7 +635,6 @@ class SpymemcachedTest extends AgentTestRunner { "${Tags.COMPONENT.key}" COMPONENT_NAME "${Tags.SPAN_KIND.key}" Tags.SPAN_KIND_CLIENT "${Tags.DB_TYPE.key}" CompletionListener.DB_TYPE - "$Tags.DB_INSTANCE.key" ~/Connection Status \{ \w*\/127.0.0.1:\d+ active: true, authed: true, last read: \d+ ms ago }/ if (error == "canceled") { "${CompletionListener.DB_COMMAND_CANCELLED}" true diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java deleted file mode 100644 index 86d2f58c9d0..00000000000 --- a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package datadog.trace.agent.integration; - -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_DB_NAME; -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_HOST; -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_PORT; - -import com.mongodb.async.SingleResultCallback; -import com.mongodb.async.client.MongoClient; -import com.mongodb.async.client.MongoClients; -import com.mongodb.async.client.MongoDatabase; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.agent.test.IntegrationTestUtils; -import datadog.trace.common.writer.ListWriter; -import io.opentracing.tag.Tags; -import java.util.concurrent.atomic.AtomicBoolean; -import org.bson.Document; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class MongoAsyncClientInstrumentationTest { - private static MongoClient client; - private static final ListWriter writer = new ListWriter(); - private static final DDTracer tracer = new DDTracer(writer); - - @BeforeClass - public static void setup() throws Exception { - IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer); - MongoClientInstrumentationTest.startLocalMongo(); - // Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before - // going to tests - writer.clear(); - - client = MongoClients.create("mongodb://" + MONGO_HOST + ":" + MONGO_PORT); - } - - @AfterClass - public static void destroy() throws Exception { - if (null != client) { - client.close(); - client = null; - } - MongoClientInstrumentationTest.stopLocalMongo(); - } - - @Test - public void asyncClientHasListener() { - Assert.assertEquals(1, client.getSettings().getCommandListeners().size()); - Assert.assertEquals( - "TracingCommandListener", - client.getSettings().getCommandListeners().get(0).getClass().getSimpleName()); - } - - @Test - public void insertOperation() throws Exception { - final MongoDatabase db = client.getDatabase(MONGO_DB_NAME); - final String collectionName = "asyncCollection"; - final AtomicBoolean done = new AtomicBoolean(false); - - db.createCollection( - collectionName, - new SingleResultCallback() { - @Override - public void onResult(final Void result, final Throwable t) { - done.set(true); - } - }); - while (!done.get()) { - Thread.sleep(1); - } - - db.getCollection(collectionName) - .insertOne( - new Document("foo", "bar"), - new SingleResultCallback() { - @Override - public void onResult(final Void result, final Throwable t) { - done.set(true); - } - }); - while (!done.get()) { - Thread.sleep(1); - } - - done.set(false); - db.getCollection(collectionName) - .count( - new SingleResultCallback() { - @Override - public void onResult(final Long result, final Throwable t) { - Assert.assertEquals(1, result.longValue()); - done.set(true); - } - }); - - while (!done.get()) { - Thread.sleep(1); - } - - // the final trace may still be reporting to the ListWriter, - // but we're only testing the first trace. - Assert.assertTrue(writer.size() >= 1); - - final String createCollectionQuery = - "{ \"create\" : \"asyncCollection\", \"autoIndexId\" : \"?\", \"capped\" : \"?\" }"; - final DDSpan trace0 = writer.get(0).get(0); - Assert.assertEquals("mongo.query", trace0.getOperationName()); - Assert.assertEquals(createCollectionQuery, trace0.getResourceName()); - Assert.assertEquals("mongodb", trace0.getType()); - Assert.assertEquals("mongo", trace0.getServiceName()); - - Assert.assertEquals("java-mongo", trace0.getTags().get(Tags.COMPONENT.getKey())); - Assert.assertEquals(createCollectionQuery, trace0.getTags().get(Tags.DB_STATEMENT.getKey())); - Assert.assertEquals(MONGO_DB_NAME, trace0.getTags().get(Tags.DB_INSTANCE.getKey())); - Assert.assertEquals(MONGO_HOST, trace0.getTags().get(Tags.PEER_HOSTNAME.getKey())); - Assert.assertEquals("127.0.0.1", trace0.getTags().get(Tags.PEER_HOST_IPV4.getKey())); - Assert.assertEquals(MONGO_PORT, trace0.getTags().get(Tags.PEER_PORT.getKey())); - Assert.assertEquals("mongo", trace0.getTags().get(Tags.DB_TYPE.getKey())); - } -} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java deleted file mode 100644 index 9942c6e5350..00000000000 --- a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package datadog.trace.agent.integration; - -import com.mongodb.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.agent.test.IntegrationTestUtils; -import datadog.trace.common.writer.ListWriter; -import de.flapdoodle.embed.mongo.MongodExecutable; -import de.flapdoodle.embed.mongo.MongodProcess; -import de.flapdoodle.embed.mongo.MongodStarter; -import de.flapdoodle.embed.mongo.config.IMongodConfig; -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; -import de.flapdoodle.embed.mongo.config.Net; -import de.flapdoodle.embed.mongo.distribution.Version; -import de.flapdoodle.embed.process.runtime.Network; -import io.opentracing.tag.Tags; -import java.util.concurrent.TimeoutException; -import org.bson.Document; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class MongoClientInstrumentationTest { - public static final String MONGO_DB_NAME = "embedded"; - public static final String MONGO_HOST = "localhost"; - public static final int MONGO_PORT = 12345; - private static MongodExecutable mongodExe; - private static MongodProcess mongod; - - private static MongoClient client; - private static final ListWriter writer = new ListWriter(); - private static final DDTracer tracer = new DDTracer(writer); - - public static void startLocalMongo() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance(); - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net(MONGO_HOST, MONGO_PORT, Network.localhostIsIPv6())) - .build(); - - mongodExe = starter.prepare(mongodConfig); - mongod = mongodExe.start(); - } - - public static void stopLocalMongo() throws Exception { - if (null != mongod) { - mongod.stop(); - mongod = null; - } - if (null != mongodExe) { - mongodExe.stop(); - mongodExe = null; - } - } - - @BeforeClass - public static void setup() throws Exception { - IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer); - startLocalMongo(); - // Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before - // going to tests - writer.clear(); - - client = new MongoClient(MONGO_HOST, MONGO_PORT); - } - - @AfterClass - public static void destroy() throws Exception { - if (null != client) { - client.close(); - client = null; - } - stopLocalMongo(); - } - - @Test - public void syncClientHasListener() { - Assert.assertEquals(1, client.getMongoClientOptions().getCommandListeners().size()); - Assert.assertEquals( - "TracingCommandListener", - client.getMongoClientOptions().getCommandListeners().get(0).getClass().getSimpleName()); - } - - @Test - public void insertOperation() throws TimeoutException, InterruptedException { - final MongoDatabase db = client.getDatabase(MONGO_DB_NAME); - final String collectionName = "testCollection"; - db.createCollection(collectionName); - final MongoCollection collection = db.getCollection(collectionName); - - collection.insertOne(new Document("foo", "bar")); - - Assert.assertEquals(1, collection.count()); - - Assert.assertEquals(3, writer.size()); - - final String createCollectionQuery = - "{ \"create\" : \"testCollection\", \"autoIndexId\" : \"?\", \"capped\" : \"?\" }"; - final DDSpan trace0 = writer.get(0).get(0); - Assert.assertEquals("mongo.query", trace0.getOperationName()); - Assert.assertEquals(createCollectionQuery, trace0.getResourceName()); - Assert.assertEquals("mongodb", trace0.getType()); - Assert.assertEquals("mongo", trace0.getServiceName()); - - Assert.assertEquals("java-mongo", trace0.getTags().get(Tags.COMPONENT.getKey())); - Assert.assertEquals(createCollectionQuery, trace0.getTags().get(Tags.DB_STATEMENT.getKey())); - Assert.assertEquals(MONGO_DB_NAME, trace0.getTags().get(Tags.DB_INSTANCE.getKey())); - Assert.assertEquals(MONGO_HOST, trace0.getTags().get(Tags.PEER_HOSTNAME.getKey())); - Assert.assertEquals("127.0.0.1", trace0.getTags().get(Tags.PEER_HOST_IPV4.getKey())); - Assert.assertEquals(MONGO_PORT, trace0.getTags().get(Tags.PEER_PORT.getKey())); - Assert.assertEquals("mongo", trace0.getTags().get(Tags.DB_TYPE.getKey())); - } -} diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy index c5a9351885c..eae9c175501 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy @@ -64,6 +64,11 @@ class SpanAssert { checked.resourceName = true } + def resourceName(Closure eval) { + assert eval(span.resourceName) + checked.resourceName = true + } + def resourceNameContains(String... resourceNameParts) { assertSpanNameContains(span.resourceName, resourceNameParts) checked.resourceName = true diff --git a/settings.gradle b/settings.gradle index d31e0b382df..0cd07e4c116 100644 --- a/settings.gradle +++ b/settings.gradle @@ -66,8 +66,9 @@ include ':dd-java-agent:instrumentation:jsp-2.3' include ':dd-java-agent:instrumentation:kafka-clients-0.11' include ':dd-java-agent:instrumentation:kafka-streams-0.11' include ':dd-java-agent:instrumentation:lettuce-5' -include ':dd-java-agent:instrumentation:mongo-3.1' -include ':dd-java-agent:instrumentation:mongo-async-3.3' +include ':dd-java-agent:instrumentation:mongo' +include ':dd-java-agent:instrumentation:mongo:driver-3.1' +include ':dd-java-agent:instrumentation:mongo:driver-async-3.3' include ':dd-java-agent:instrumentation:netty-4.0' include ':dd-java-agent:instrumentation:netty-4.1' include ':dd-java-agent:instrumentation:okhttp-3'