Extract Mongo library instrumentation (#2789)
* Extract mongo library instrumentation * Finish * Drift * Cleanup * build twice * Spot
This commit is contained in:
parent
e87564ef12
commit
b416ece9c3
|
@ -121,6 +121,7 @@ public abstract class DatabaseClientTracer<CONNECTION, STATEMENT, SANITIZEDSTATE
|
|||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String dbConnectionString(CONNECTION connection) {
|
||||
return null;
|
||||
}
|
||||
|
@ -129,6 +130,7 @@ public abstract class DatabaseClientTracer<CONNECTION, STATEMENT, SANITIZEDSTATE
|
|||
netPeerAttributes.setNetPeer(span, peerAddress(connection));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract InetSocketAddress peerAddress(CONNECTION connection);
|
||||
|
||||
protected void onStatement(
|
||||
|
|
|
@ -10,10 +10,9 @@ muzzle {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(':instrumentation:mongo:mongo-common:javaagent'))
|
||||
implementation(project(':instrumentation:mongo:mongo-3.1:library'))
|
||||
|
||||
library group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0'
|
||||
|
||||
testImplementation project(':instrumentation:mongo:mongo-testing')
|
||||
testImplementation group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5'
|
||||
testImplementation project(':instrumentation:mongo:mongo-3.1:testing')
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
|||
import com.google.auto.service.AutoService;
|
||||
import com.mongodb.MongoClientOptions;
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.javaagent.instrumentation.mongo.TracingCommandListener;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.List;
|
||||
|
@ -68,11 +67,11 @@ public class MongoClientInstrumentationModule extends InstrumentationModule {
|
|||
@Advice.This MongoClientOptions.Builder builder,
|
||||
@Advice.FieldValue("commandListeners") List<CommandListener> commandListeners) {
|
||||
for (CommandListener commandListener : commandListeners) {
|
||||
if (commandListener instanceof TracingCommandListener) {
|
||||
if (commandListener == MongoInstrumentationSingletons.LISTENER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
builder.addCommandListener(new TracingCommandListener());
|
||||
builder.addCommandListener(MongoInstrumentationSingletons.LISTENER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mongo.v3_1;
|
||||
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.mongo.v3_1.MongoTracing;
|
||||
|
||||
public final class MongoInstrumentationSingletons {
|
||||
|
||||
public static final CommandListener LISTENER =
|
||||
MongoTracing.create(GlobalOpenTelemetry.get()).newCommandListener();
|
||||
|
||||
private MongoInstrumentationSingletons() {}
|
||||
}
|
|
@ -3,167 +3,12 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
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 org.bson.BsonDocument
|
||||
import org.bson.BsonString
|
||||
import org.bson.Document
|
||||
import spock.lang.Shared
|
||||
|
||||
class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>> {
|
||||
|
||||
@Shared
|
||||
MongoClient client
|
||||
|
||||
def setupSpec() throws Exception {
|
||||
client = new MongoClient(new ServerAddress("localhost", port),
|
||||
MongoClientOptions.builder()
|
||||
.description("some-description")
|
||||
.build())
|
||||
}
|
||||
|
||||
def cleanupSpec() throws Exception {
|
||||
client?.close()
|
||||
client = null
|
||||
}
|
||||
import io.opentelemetry.instrumentation.mongo.v3_1.AbstractMongo31ClientTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
|
||||
class MongoClientTest extends AbstractMongo31ClientTest implements AgentTestTrait {
|
||||
@Override
|
||||
void createCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionNoDescription(String dbName, String collectionName) {
|
||||
MongoDatabase db = new MongoClient("localhost", port).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
|
||||
def clientOptions = client.mongoClientOptions
|
||||
def newClientOptions = MongoClientOptions.builder(clientOptions).build()
|
||||
MongoDatabase db = new MongoClient(new ServerAddress("localhost", port), newClientOptions).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
return db.getCollection(collectionName).count()
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupInsert(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
return db.getCollection(collectionName)
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
int insert(MongoCollection<Document> collection) {
|
||||
collection.insertOne(new Document("password", "SECRET"))
|
||||
return collection.count()
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupUpdate(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
def coll = db.getCollection(collectionName)
|
||||
coll.insertOne(new Document("password", "OLDPW"))
|
||||
return coll
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
int update(MongoCollection<Document> collection) {
|
||||
def result = collection.updateOne(
|
||||
new BsonDocument("password", new BsonString("OLDPW")),
|
||||
new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))))
|
||||
collection.count()
|
||||
return result.modifiedCount
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupDelete(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
def coll = db.getCollection(collectionName)
|
||||
coll.insertOne(new Document("password", "SECRET"))
|
||||
return coll
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
int delete(MongoCollection<Document> collection) {
|
||||
def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")))
|
||||
collection.count()
|
||||
return result.deletedCount
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupGetMore(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
def coll = db.getCollection(collectionName)
|
||||
coll.insertMany([new Document("_id", 0), new Document("_id", 1), new Document("_id", 2)])
|
||||
return coll
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
void getMore(MongoCollection<Document> collection) {
|
||||
collection.find().filter(new Document("_id", new Document('$gte', 0)))
|
||||
.batchSize(2).into(new ArrayList())
|
||||
}
|
||||
|
||||
@Override
|
||||
void error(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
return db.getCollection(collectionName)
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
collection.updateOne(new BsonDocument(), new BsonDocument())
|
||||
}
|
||||
|
||||
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 = createCollectionName()
|
||||
void configureMongoClientOptions(MongoClientOptions.Builder options) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
apply from: "$rootDir/gradle/instrumentation-library.gradle"
|
||||
apply plugin: "net.ltgt.errorprone"
|
||||
|
||||
dependencies {
|
||||
library group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0'
|
||||
|
||||
testImplementation project(':instrumentation:mongo:mongo-3.1:testing')
|
||||
}
|
|
@ -3,13 +3,14 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mongo;
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import com.mongodb.ServerAddress;
|
||||
import com.mongodb.connection.ConnectionDescription;
|
||||
import com.mongodb.event.CommandStartedEvent;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
import io.opentelemetry.api.trace.SpanBuilder;
|
||||
import io.opentelemetry.instrumentation.api.tracer.DatabaseClientTracer;
|
||||
import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes;
|
||||
|
@ -29,46 +30,49 @@ import org.bson.BsonDocument;
|
|||
import org.bson.BsonValue;
|
||||
import org.bson.json.JsonWriter;
|
||||
import org.bson.json.JsonWriterSettings;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class MongoClientTracer
|
||||
final class MongoClientTracer
|
||||
extends DatabaseClientTracer<CommandStartedEvent, BsonDocument, String> {
|
||||
private static final MongoClientTracer TRACER = new MongoClientTracer();
|
||||
|
||||
private final int maxNormalizedQueryLength;
|
||||
private final JsonWriterSettings jsonWriterSettings;
|
||||
@Nullable private final JsonWriterSettings jsonWriterSettings;
|
||||
|
||||
public MongoClientTracer() {
|
||||
this(32 * 1024);
|
||||
}
|
||||
|
||||
public MongoClientTracer(int maxNormalizedQueryLength) {
|
||||
super(NetPeerAttributes.INSTANCE);
|
||||
MongoClientTracer(OpenTelemetry openTelemetry, int maxNormalizedQueryLength) {
|
||||
super(openTelemetry, NetPeerAttributes.INSTANCE);
|
||||
this.maxNormalizedQueryLength = maxNormalizedQueryLength;
|
||||
this.jsonWriterSettings = createJsonWriterSettings(maxNormalizedQueryLength);
|
||||
}
|
||||
|
||||
public static MongoClientTracer tracer() {
|
||||
return TRACER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInstrumentationName() {
|
||||
return "io.opentelemetry.javaagent.mongo-common";
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO(anuraaga): Migrate off of StringWriter to avoid synchronization.
|
||||
@SuppressWarnings("JdkObsolete")
|
||||
protected String sanitizeStatement(BsonDocument command) {
|
||||
StringWriter stringWriter = new StringWriter(128);
|
||||
writeScrubbed(command, new JsonWriter(stringWriter, jsonWriterSettings), true);
|
||||
// jsonWriterSettings is generally not null but could be due to security manager or unknown
|
||||
// API incompatibilities, which we can't detect by Muzzle because we use reflection.
|
||||
JsonWriter jsonWriter =
|
||||
jsonWriterSettings != null
|
||||
? new JsonWriter(stringWriter, jsonWriterSettings)
|
||||
: new JsonWriter(stringWriter);
|
||||
writeScrubbed(command, jsonWriter, true);
|
||||
// If using MongoDB driver >= 3.7, the substring invocation will be a no-op due to use of
|
||||
// JsonWriterSettings.Builder.maxLength in the static initializer for JSON_WRITER_SETTINGS
|
||||
return stringWriter
|
||||
.getBuffer()
|
||||
.substring(0, Math.min(maxNormalizedQueryLength, stringWriter.getBuffer().length()));
|
||||
StringBuffer buf = stringWriter.getBuffer();
|
||||
if (buf.length() <= maxNormalizedQueryLength) {
|
||||
return buf.toString();
|
||||
}
|
||||
return buf.substring(0, maxNormalizedQueryLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String spanName(CommandStartedEvent event, BsonDocument document, String normalizedQuery) {
|
||||
protected String spanName(
|
||||
CommandStartedEvent event, BsonDocument document, String normalizedQuery) {
|
||||
return conventionSpanName(dbName(event), event.getCommandName(), collectionName(event));
|
||||
}
|
||||
|
||||
|
@ -92,6 +96,7 @@ public class MongoClientTracer
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected String dbConnectionString(CommandStartedEvent event) {
|
||||
ConnectionDescription connectionDescription = event.getConnectionDescription();
|
||||
if (connectionDescription != null) {
|
||||
|
@ -109,6 +114,7 @@ public class MongoClientTracer
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected InetSocketAddress peerAddress(CommandStartedEvent event) {
|
||||
if (event.getConnectionDescription() != null
|
||||
&& event.getConnectionDescription().getServerAddress() != null) {
|
||||
|
@ -130,7 +136,7 @@ public class MongoClientTracer
|
|||
return event.getCommandName();
|
||||
}
|
||||
|
||||
private static final Method IS_TRUNCATED_METHOD;
|
||||
@Nullable private static final Method IS_TRUNCATED_METHOD;
|
||||
|
||||
static {
|
||||
IS_TRUNCATED_METHOD =
|
||||
|
@ -140,6 +146,7 @@ public class MongoClientTracer
|
|||
.orElse(null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JsonWriterSettings createJsonWriterSettings(int maxNormalizedQueryLength) {
|
||||
JsonWriterSettings settings = null;
|
||||
try {
|
||||
|
@ -175,14 +182,17 @@ public class MongoClientTracer
|
|||
builderClass.getMethod("build", (Class<?>[]) null).invoke(builder, (Object[]) null);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
if (settings == null) {
|
||||
try {
|
||||
// Constructor removed in 4.0+ so use reflection. 4.0+ will have used the builder above.
|
||||
settings = JsonWriterSettings.class.getConstructor(Boolean.TYPE).newInstance(false);
|
||||
} catch (InstantiationException
|
||||
| IllegalAccessException
|
||||
| InvocationTargetException
|
||||
| NoSuchMethodException ignored) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,6 +274,7 @@ public class MongoClientTracer
|
|||
"createIndexes",
|
||||
"listIndexes"));
|
||||
|
||||
@Nullable
|
||||
private static String collectionName(CommandStartedEvent event) {
|
||||
if (event.getCommandName().equals("getMore")) {
|
||||
if (event.getCommand().containsKey("collection")) {
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1;
|
||||
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
|
||||
/** Entrypoint to OpenTelemetry instrumentation of the MongoDB client. */
|
||||
public final class MongoTracing {
|
||||
|
||||
/** Returns a new {@link MongoTracing} configured with the given {@link OpenTelemetry}. */
|
||||
public static MongoTracing create(OpenTelemetry openTelemetry) {
|
||||
return newBuilder(openTelemetry).build();
|
||||
}
|
||||
|
||||
/** Returns a new {@link MongoTracingBuilder} configured with the given {@link OpenTelemetry}. */
|
||||
public static MongoTracingBuilder newBuilder(OpenTelemetry openTelemetry) {
|
||||
return new MongoTracingBuilder(openTelemetry);
|
||||
}
|
||||
|
||||
private final MongoClientTracer tracer;
|
||||
|
||||
MongoTracing(OpenTelemetry openTelemetry, int maxNormalizedQueryLength) {
|
||||
this.tracer = new MongoClientTracer(openTelemetry, maxNormalizedQueryLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link CommandListener} that can be used with methods like {@link
|
||||
* com.mongodb.MongoClientOptions.Builder#addCommandListener(CommandListener)}.
|
||||
*/
|
||||
public CommandListener newCommandListener() {
|
||||
return new TracingCommandListener(tracer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1;
|
||||
|
||||
import io.opentelemetry.api.OpenTelemetry;
|
||||
|
||||
/** A builder of {@link MongoTracing}. */
|
||||
public final class MongoTracingBuilder {
|
||||
|
||||
// Visible for testing
|
||||
static final int DEFAULT_MAX_NORMALIZED_QUERY_LENGTH = 32 * 1024;
|
||||
|
||||
private final OpenTelemetry openTelemetry;
|
||||
|
||||
private int maxNormalizedQueryLength = DEFAULT_MAX_NORMALIZED_QUERY_LENGTH;
|
||||
|
||||
MongoTracingBuilder(OpenTelemetry openTelemetry) {
|
||||
this.openTelemetry = openTelemetry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max length of recorded queries after normalization. Defaults to {@value
|
||||
* DEFAULT_MAX_NORMALIZED_QUERY_LENGTH}.
|
||||
*/
|
||||
public MongoTracingBuilder setMaxNormalizedQueryLength(int maxNormalizedQueryLength) {
|
||||
this.maxNormalizedQueryLength = maxNormalizedQueryLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns a new {@link MongoTracing} with the settings of this {@link MongoTracingBuilder}. */
|
||||
public MongoTracing build() {
|
||||
return new MongoTracing(openTelemetry, maxNormalizedQueryLength);
|
||||
}
|
||||
}
|
|
@ -3,9 +3,7 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mongo;
|
||||
|
||||
import static io.opentelemetry.javaagent.instrumentation.mongo.MongoClientTracer.tracer;
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1;
|
||||
|
||||
import com.mongodb.event.CommandFailedEvent;
|
||||
import com.mongodb.event.CommandListener;
|
||||
|
@ -15,13 +13,19 @@ import io.opentelemetry.context.Context;
|
|||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class TracingCommandListener implements CommandListener {
|
||||
final class TracingCommandListener implements CommandListener {
|
||||
|
||||
private final Map<Integer, Context> contextMap = new ConcurrentHashMap<>();
|
||||
private final MongoClientTracer tracer;
|
||||
private final Map<Integer, Context> contextMap;
|
||||
|
||||
TracingCommandListener(MongoClientTracer tracer) {
|
||||
this.tracer = tracer;
|
||||
contextMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commandStarted(CommandStartedEvent event) {
|
||||
Context context = tracer().startSpan(Context.current(), event, event.getCommand());
|
||||
Context context = tracer.startSpan(Context.current(), event, event.getCommand());
|
||||
contextMap.put(event.getRequestId(), context);
|
||||
}
|
||||
|
||||
|
@ -29,7 +33,7 @@ public class TracingCommandListener implements CommandListener {
|
|||
public void commandSucceeded(CommandSucceededEvent event) {
|
||||
Context context = contextMap.remove(event.getRequestId());
|
||||
if (context != null) {
|
||||
tracer().end(context);
|
||||
tracer.end(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +41,7 @@ public class TracingCommandListener implements CommandListener {
|
|||
public void commandFailed(CommandFailedEvent event) {
|
||||
Context context = contextMap.remove(event.getRequestId());
|
||||
if (context != null) {
|
||||
tracer().endExceptionally(context, event.getThrowable());
|
||||
tracer.endExceptionally(context, event.getThrowable());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1
|
||||
|
||||
import com.mongodb.MongoClientOptions
|
||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
||||
|
||||
class MongoClientTest extends AbstractMongo31ClientTest implements LibraryTestTrait {
|
||||
@Override
|
||||
void configureMongoClientOptions(MongoClientOptions.Builder options) {
|
||||
options.addCommandListener(MongoTracing.create(openTelemetry).newCommandListener())
|
||||
}
|
||||
}
|
|
@ -3,10 +3,13 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1
|
||||
|
||||
import static io.opentelemetry.instrumentation.mongo.v3_1.MongoTracingBuilder.DEFAULT_MAX_NORMALIZED_QUERY_LENGTH
|
||||
import static java.util.Arrays.asList
|
||||
|
||||
import com.mongodb.event.CommandStartedEvent
|
||||
import io.opentelemetry.javaagent.instrumentation.mongo.MongoClientTracer
|
||||
import io.opentelemetry.api.OpenTelemetry
|
||||
import org.bson.BsonArray
|
||||
import org.bson.BsonDocument
|
||||
import org.bson.BsonInt32
|
||||
|
@ -16,7 +19,7 @@ import spock.lang.Specification
|
|||
class MongoClientTracerTest extends Specification {
|
||||
def 'should sanitize statements to json'() {
|
||||
setup:
|
||||
def tracer = new MongoClientTracer()
|
||||
def tracer = new MongoClientTracer(OpenTelemetry.noop(), DEFAULT_MAX_NORMALIZED_QUERY_LENGTH)
|
||||
|
||||
expect:
|
||||
sanitizeStatementAcrossVersions(tracer,
|
||||
|
@ -36,7 +39,7 @@ class MongoClientTracerTest extends Specification {
|
|||
|
||||
def 'should only preserve string value if it is the value of the first top-level key'() {
|
||||
setup:
|
||||
def tracer = new MongoClientTracer()
|
||||
def tracer = new MongoClientTracer(OpenTelemetry.noop(), DEFAULT_MAX_NORMALIZED_QUERY_LENGTH)
|
||||
|
||||
expect:
|
||||
sanitizeStatementAcrossVersions(tracer,
|
||||
|
@ -48,7 +51,7 @@ class MongoClientTracerTest extends Specification {
|
|||
|
||||
def 'should truncate simple command'() {
|
||||
setup:
|
||||
def tracer = new MongoClientTracer(20)
|
||||
def tracer = new MongoClientTracer(OpenTelemetry.noop(), 20)
|
||||
|
||||
def normalized = sanitizeStatementAcrossVersions(tracer,
|
||||
new BsonDocument("cmd", new BsonString("c"))
|
||||
|
@ -61,11 +64,11 @@ class MongoClientTracerTest extends Specification {
|
|||
|
||||
def 'should truncate array'() {
|
||||
setup:
|
||||
def tracer = new MongoClientTracer(27)
|
||||
def tracer = new MongoClientTracer(OpenTelemetry.noop(), 27)
|
||||
|
||||
def normalized = sanitizeStatementAcrossVersions(tracer,
|
||||
new BsonDocument("cmd", new BsonString("c"))
|
||||
.append("f1", new BsonArray(Arrays.asList(new BsonString("c1"), new BsonString("c2"))))
|
||||
.append("f1", new BsonArray(asList(new BsonString("c1"), new BsonString("c2"))))
|
||||
.append("f2", new BsonString("c3")))
|
||||
expect:
|
||||
// this can vary because of different whitespace for different mongo versions
|
||||
|
@ -74,7 +77,7 @@ class MongoClientTracerTest extends Specification {
|
|||
|
||||
def 'test span name with no dbName'() {
|
||||
setup:
|
||||
def tracer = new MongoClientTracer()
|
||||
def tracer = new MongoClientTracer(OpenTelemetry.noop(), DEFAULT_MAX_NORMALIZED_QUERY_LENGTH)
|
||||
def event = new CommandStartedEvent(
|
||||
0, null, null, command, new BsonDocument(command, new BsonInt32(1)))
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
apply from: "$rootDir/gradle/java.gradle"
|
||||
|
||||
dependencies {
|
||||
api project(':instrumentation:mongo:mongo-testing')
|
||||
|
||||
compileOnly group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0'
|
||||
|
||||
implementation deps.groovy
|
||||
implementation deps.opentelemetryApi
|
||||
implementation deps.spock
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.mongo.v3_1
|
||||
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
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 io.opentelemetry.instrumentation.mongo.testing.AbstractMongoClientTest
|
||||
import io.opentelemetry.instrumentation.test.utils.PortUtils
|
||||
import org.bson.BsonDocument
|
||||
import org.bson.BsonString
|
||||
import org.bson.Document
|
||||
import spock.lang.Shared
|
||||
|
||||
abstract class AbstractMongo31ClientTest extends AbstractMongoClientTest<MongoCollection<Document>> {
|
||||
|
||||
abstract void configureMongoClientOptions(MongoClientOptions.Builder options);
|
||||
|
||||
@Shared
|
||||
MongoClient client
|
||||
|
||||
def setupSpec() throws Exception {
|
||||
def options = MongoClientOptions.builder().description("some-description")
|
||||
configureMongoClientOptions(options)
|
||||
client = new MongoClient(new ServerAddress("localhost", port), options.build())
|
||||
}
|
||||
|
||||
def cleanupSpec() throws Exception {
|
||||
client?.close()
|
||||
client = null
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionNoDescription(String dbName, String collectionName) {
|
||||
def options = MongoClientOptions.builder()
|
||||
configureMongoClientOptions(options)
|
||||
MongoDatabase db = new MongoClient(new ServerAddress("localhost", port), options.build()).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName) {
|
||||
def clientOptions = client.mongoClientOptions
|
||||
def newClientOptions = MongoClientOptions.builder(clientOptions).build()
|
||||
MongoDatabase db = new MongoClient(new ServerAddress("localhost", port), newClientOptions).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionCallingBuildTwice(String dbName, String collectionName) {
|
||||
def options = MongoClientOptions.builder().description("some-description")
|
||||
configureMongoClientOptions(options)
|
||||
options.build()
|
||||
MongoDatabase db = new MongoClient(new ServerAddress("localhost", port), options.build()).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
return db.getCollection(collectionName).count()
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupInsert(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
return db.getCollection(collectionName)
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
int insert(MongoCollection<Document> collection) {
|
||||
collection.insertOne(new Document("password", "SECRET"))
|
||||
return collection.count()
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupUpdate(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
def coll = db.getCollection(collectionName)
|
||||
coll.insertOne(new Document("password", "OLDPW"))
|
||||
return coll
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
int update(MongoCollection<Document> collection) {
|
||||
def result = collection.updateOne(
|
||||
new BsonDocument("password", new BsonString("OLDPW")),
|
||||
new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))))
|
||||
collection.count()
|
||||
return result.modifiedCount
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupDelete(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
def coll = db.getCollection(collectionName)
|
||||
coll.insertOne(new Document("password", "SECRET"))
|
||||
return coll
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
int delete(MongoCollection<Document> collection) {
|
||||
def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")))
|
||||
collection.count()
|
||||
return result.deletedCount
|
||||
}
|
||||
|
||||
@Override
|
||||
MongoCollection<Document> setupGetMore(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
def coll = db.getCollection(collectionName)
|
||||
coll.insertMany([new Document("_id", 0), new Document("_id", 1), new Document("_id", 2)])
|
||||
return coll
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
return collection
|
||||
}
|
||||
|
||||
@Override
|
||||
void getMore(MongoCollection<Document> collection) {
|
||||
collection.find().filter(new Document("_id", new Document('$gte', 0)))
|
||||
.batchSize(2).into(new ArrayList())
|
||||
}
|
||||
|
||||
@Override
|
||||
void error(String dbName, String collectionName) {
|
||||
MongoCollection<Document> collection = runUnderTrace("setup") {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
return db.getCollection(collectionName)
|
||||
}
|
||||
ignoreTracesAndClear(1)
|
||||
collection.updateOne(new BsonDocument(), new BsonDocument())
|
||||
}
|
||||
|
||||
def "test client failure"() {
|
||||
setup:
|
||||
def options = MongoClientOptions.builder().serverSelectionTimeout(10).build()
|
||||
def client = new MongoClient(new ServerAddress("localhost", PortUtils.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 = createCollectionName()
|
||||
}
|
||||
}
|
|
@ -19,11 +19,10 @@ muzzle {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(':instrumentation:mongo:mongo-common:javaagent'))
|
||||
implementation(project(':instrumentation:mongo:mongo-3.1:library'))
|
||||
|
||||
// a couple of test attribute verifications don't pass until 3.8.0
|
||||
library group: 'org.mongodb', name: 'mongo-java-driver', version: '3.8.0'
|
||||
|
||||
testImplementation project(':instrumentation:mongo:mongo-testing')
|
||||
testImplementation group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5'
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import com.mongodb.MongoClientSettings;
|
|||
import com.mongodb.async.SingleResultCallback;
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.instrumentation.mongo.TracingCommandListener;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.HashMap;
|
||||
|
@ -74,11 +73,11 @@ public class MongoClientInstrumentationModule extends InstrumentationModule {
|
|||
@Advice.This MongoClientSettings.Builder builder,
|
||||
@Advice.FieldValue("commandListeners") List<CommandListener> commandListeners) {
|
||||
for (CommandListener commandListener : commandListeners) {
|
||||
if (commandListener instanceof TracingCommandListener) {
|
||||
if (commandListener == MongoInstrumentationSingletons.LISTENER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
builder.addCommandListener(new TracingCommandListener());
|
||||
builder.addCommandListener(MongoInstrumentationSingletons.LISTENER);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mongo.v3_7;
|
||||
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.mongo.v3_1.MongoTracing;
|
||||
|
||||
public final class MongoInstrumentationSingletons {
|
||||
|
||||
public static final CommandListener LISTENER =
|
||||
MongoTracing.create(GlobalOpenTelemetry.get()).newCommandListener();
|
||||
|
||||
private MongoInstrumentationSingletons() {}
|
||||
}
|
|
@ -13,12 +13,14 @@ import com.mongodb.client.MongoClient
|
|||
import com.mongodb.client.MongoClients
|
||||
import com.mongodb.client.MongoCollection
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import io.opentelemetry.instrumentation.mongo.testing.AbstractMongoClientTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
import org.bson.BsonDocument
|
||||
import org.bson.BsonString
|
||||
import org.bson.Document
|
||||
import spock.lang.Shared
|
||||
|
||||
class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>> {
|
||||
class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>> implements AgentTestTrait {
|
||||
|
||||
@Shared
|
||||
MongoClient client
|
||||
|
@ -64,6 +66,19 @@ class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>>
|
|||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionCallingBuildTwice(String dbName, String collectionName) {
|
||||
def clientSettings = MongoClientSettings.builder()
|
||||
.applyToClusterSettings({ builder ->
|
||||
builder.hosts(Arrays.asList(
|
||||
new ServerAddress("localhost", port)))
|
||||
.description("some-description")
|
||||
})
|
||||
clientSettings.build()
|
||||
MongoDatabase db = MongoClients.create(clientSettings.build()).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
|
|
|
@ -10,7 +10,7 @@ muzzle {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(':instrumentation:mongo:mongo-common:javaagent'))
|
||||
implementation(project(':instrumentation:mongo:mongo-3.1:library'))
|
||||
|
||||
library group: 'org.mongodb', name: 'mongodb-driver-core', version: '4.0.0'
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import com.mongodb.MongoClientSettings;
|
|||
import com.mongodb.event.CommandListener;
|
||||
import com.mongodb.internal.async.SingleResultCallback;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.instrumentation.mongo.TracingCommandListener;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.HashMap;
|
||||
|
@ -74,11 +73,11 @@ public class MongoClientInstrumentationModule extends InstrumentationModule {
|
|||
@Advice.This MongoClientSettings.Builder builder,
|
||||
@Advice.FieldValue("commandListeners") List<CommandListener> commandListeners) {
|
||||
for (CommandListener commandListener : commandListeners) {
|
||||
if (commandListener instanceof TracingCommandListener) {
|
||||
if (commandListener == MongoInstrumentationSingletons.LISTENER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
builder.addCommandListener(new TracingCommandListener());
|
||||
builder.addCommandListener(MongoInstrumentationSingletons.LISTENER);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mongo.v4_0;
|
||||
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.mongo.v3_1.MongoTracing;
|
||||
|
||||
public final class MongoInstrumentationSingletons {
|
||||
|
||||
public static final CommandListener LISTENER =
|
||||
MongoTracing.create(GlobalOpenTelemetry.get()).newCommandListener();
|
||||
|
||||
private MongoInstrumentationSingletons() {}
|
||||
}
|
|
@ -5,12 +5,16 @@
|
|||
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.ServerAddress
|
||||
import com.mongodb.client.result.DeleteResult
|
||||
import com.mongodb.client.result.UpdateResult
|
||||
import com.mongodb.reactivestreams.client.MongoClient
|
||||
import com.mongodb.reactivestreams.client.MongoClients
|
||||
import com.mongodb.reactivestreams.client.MongoCollection
|
||||
import com.mongodb.reactivestreams.client.MongoDatabase
|
||||
import io.opentelemetry.instrumentation.mongo.testing.AbstractMongoClientTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import org.bson.BsonDocument
|
||||
|
@ -21,7 +25,7 @@ import org.reactivestreams.Subscriber
|
|||
import org.reactivestreams.Subscription
|
||||
import spock.lang.Shared
|
||||
|
||||
class Mongo4ReactiveClientTest extends AbstractMongoClientTest<MongoCollection<Document>> {
|
||||
class Mongo4ReactiveClientTest extends AbstractMongoClientTest<MongoCollection<Document>> implements AgentTestTrait {
|
||||
|
||||
@Shared
|
||||
MongoClient client
|
||||
|
@ -52,6 +56,18 @@ class Mongo4ReactiveClientTest extends AbstractMongoClientTest<MongoCollection<D
|
|||
throw new AssumptionViolatedException("not tested on 4.0")
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionCallingBuildTwice(String dbName, String collectionName) {
|
||||
def settings = MongoClientSettings.builder()
|
||||
.applyToClusterSettings({ builder ->
|
||||
builder.hosts(Arrays.asList(
|
||||
new ServerAddress("localhost", port)))
|
||||
})
|
||||
settings.build()
|
||||
MongoDatabase db = MongoClients.create(settings.build()).getDatabase(dbName)
|
||||
db.createCollection(collectionName).subscribe(toSubscriber {})
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
|
|
|
@ -5,17 +5,21 @@
|
|||
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.ServerAddress
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import com.mongodb.client.MongoCollection
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import io.opentelemetry.instrumentation.mongo.testing.AbstractMongoClientTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
import org.bson.BsonDocument
|
||||
import org.bson.BsonString
|
||||
import org.bson.Document
|
||||
import org.junit.AssumptionViolatedException
|
||||
import spock.lang.Shared
|
||||
|
||||
class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>> {
|
||||
class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>> implements AgentTestTrait {
|
||||
|
||||
@Shared
|
||||
MongoClient client
|
||||
|
@ -46,6 +50,18 @@ class MongoClientTest extends AbstractMongoClientTest<MongoCollection<Document>>
|
|||
throw new AssumptionViolatedException("not tested on 4.0")
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionCallingBuildTwice(String dbName, String collectionName) {
|
||||
def settings = MongoClientSettings.builder()
|
||||
.applyToClusterSettings({ builder ->
|
||||
builder.hosts(Arrays.asList(
|
||||
new ServerAddress("localhost", port)))
|
||||
})
|
||||
settings.build()
|
||||
MongoDatabase db = MongoClients.create(settings.build()).getDatabase(dbName)
|
||||
db.createCollection(collectionName)
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
|
|
|
@ -11,12 +11,11 @@ muzzle {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(':instrumentation:mongo:mongo-common:javaagent'))
|
||||
implementation(project(':instrumentation:mongo:mongo-3.1:library'))
|
||||
|
||||
library group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0'
|
||||
|
||||
testImplementation project(':instrumentation:mongo:mongo-testing')
|
||||
testImplementation group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5'
|
||||
|
||||
testInstrumentation project(':instrumentation:mongo:mongo-3.7:javaagent')
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import com.mongodb.async.SingleResultCallback;
|
|||
import com.mongodb.async.client.MongoClientSettings;
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
|
||||
import io.opentelemetry.javaagent.instrumentation.mongo.TracingCommandListener;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.HashMap;
|
||||
|
@ -76,11 +75,11 @@ public class MongoAsyncClientInstrumentationModule extends InstrumentationModule
|
|||
@Advice.This MongoClientSettings.Builder builder,
|
||||
@Advice.FieldValue("commandListeners") List<CommandListener> commandListeners) {
|
||||
for (CommandListener commandListener : commandListeners) {
|
||||
if (commandListener instanceof TracingCommandListener) {
|
||||
if (commandListener == MongoInstrumentationSingletons.LISTENER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
builder.addCommandListener(new TracingCommandListener());
|
||||
builder.addCommandListener(MongoInstrumentationSingletons.LISTENER);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.mongoasync.v3_3;
|
||||
|
||||
import com.mongodb.event.CommandListener;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.opentelemetry.instrumentation.mongo.v3_1.MongoTracing;
|
||||
|
||||
public final class MongoInstrumentationSingletons {
|
||||
|
||||
public static final CommandListener LISTENER =
|
||||
MongoTracing.create(GlobalOpenTelemetry.get()).newCommandListener();
|
||||
|
||||
private MongoInstrumentationSingletons() {}
|
||||
}
|
|
@ -15,6 +15,8 @@ import com.mongodb.async.client.MongoDatabase
|
|||
import com.mongodb.client.result.DeleteResult
|
||||
import com.mongodb.client.result.UpdateResult
|
||||
import com.mongodb.connection.ClusterSettings
|
||||
import io.opentelemetry.instrumentation.mongo.testing.AbstractMongoClientTest
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import org.bson.BsonDocument
|
||||
|
@ -23,7 +25,7 @@ import org.bson.Document
|
|||
import org.junit.AssumptionViolatedException
|
||||
import spock.lang.Shared
|
||||
|
||||
class MongoAsyncClientTest extends AbstractMongoClientTest<MongoCollection<Document>> {
|
||||
class MongoAsyncClientTest extends AbstractMongoClientTest<MongoCollection<Document>> implements AgentTestTrait{
|
||||
|
||||
@Shared
|
||||
MongoClient client
|
||||
|
@ -64,6 +66,19 @@ class MongoAsyncClientTest extends AbstractMongoClientTest<MongoCollection<Docum
|
|||
db.createCollection(collectionName, toCallback {})
|
||||
}
|
||||
|
||||
@Override
|
||||
void createCollectionCallingBuildTwice(String dbName, String collectionName) {
|
||||
def settings = MongoClientSettings.builder()
|
||||
.clusterSettings(
|
||||
ClusterSettings.builder()
|
||||
.description("some-description")
|
||||
.applyConnectionString(new ConnectionString("mongodb://localhost:$port"))
|
||||
.build())
|
||||
settings.build()
|
||||
MongoDatabase db = MongoClients.create(settings.build()).getDatabase(dbName)
|
||||
db.createCollection(collectionName, toCallback {})
|
||||
}
|
||||
|
||||
@Override
|
||||
int getCollection(String dbName, String collectionName) {
|
||||
MongoDatabase db = client.getDatabase(dbName)
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
// not applying $rootDir/gradle/instrumentation.gradle because that brings running tests with agent
|
||||
// infrastructure, and this module only wants to run unit tests
|
||||
|
||||
ext.mavenGroupId = 'io.opentelemetry.javaagent.instrumentation'
|
||||
|
||||
apply from: "$rootDir/gradle/java.gradle"
|
||||
apply from: "$rootDir/gradle/publish.gradle"
|
||||
|
||||
archivesBaseName = projectDir.parentFile.name
|
||||
|
||||
dependencies {
|
||||
compileOnly project(':instrumentation-api')
|
||||
compileOnly project(':javaagent-api')
|
||||
compileOnly group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0'
|
||||
|
||||
testImplementation project(':instrumentation-api')
|
||||
testImplementation project(':javaagent-api')
|
||||
testImplementation group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0'
|
||||
}
|
|
@ -3,11 +3,13 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.mongo.testing
|
||||
|
||||
import static io.opentelemetry.api.trace.SpanKind.CLIENT
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan
|
||||
import static io.opentelemetry.instrumentation.test.utils.TraceUtils.runUnderTrace
|
||||
|
||||
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||
|
@ -17,7 +19,7 @@ import org.testcontainers.containers.GenericContainer
|
|||
import org.testcontainers.containers.output.Slf4jLogConsumer
|
||||
import spock.lang.Shared
|
||||
|
||||
abstract class AbstractMongoClientTest<T> extends AgentInstrumentationSpecification {
|
||||
abstract class AbstractMongoClientTest<T> extends InstrumentationSpecification {
|
||||
|
||||
@Shared
|
||||
GenericContainer mongodb
|
||||
|
@ -50,6 +52,8 @@ abstract class AbstractMongoClientTest<T> extends AgentInstrumentationSpecificat
|
|||
// This test asserts that duplicate traces are not created in those cases.
|
||||
abstract void createCollectionWithAlreadyBuiltClientOptions(String dbName, String collectionName)
|
||||
|
||||
abstract void createCollectionCallingBuildTwice(String dbName, String collectionName)
|
||||
|
||||
abstract int getCollection(String dbName, String collectionName)
|
||||
|
||||
abstract T setupInsert(String dbName, String collectionName)
|
||||
|
@ -120,6 +124,29 @@ abstract class AbstractMongoClientTest<T> extends AgentInstrumentationSpecificat
|
|||
collectionName = createCollectionName()
|
||||
}
|
||||
|
||||
def "test create collection calling build twice"() {
|
||||
when:
|
||||
runUnderTrace("parent") {
|
||||
createCollectionCallingBuildTwice(dbName, collectionName)
|
||||
}
|
||||
|
||||
then:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
basicSpan(it, 0, "parent")
|
||||
mongoSpan(it, 1, "create", collectionName, dbName, span(0)) {
|
||||
assert it == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" ||
|
||||
it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}"
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
dbName = "test_db"
|
||||
collectionName = createCollectionName()
|
||||
}
|
||||
|
||||
def "test get collection"() {
|
||||
when:
|
||||
def count = runUnderTrace("parent") {
|
|
@ -186,10 +186,11 @@ include ':instrumentation:logback:logback-1.0:library'
|
|||
include ':instrumentation:logback:logback-1.0:testing'
|
||||
include ':instrumentation:methods:javaagent'
|
||||
include ':instrumentation:mongo:mongo-3.1:javaagent'
|
||||
include ':instrumentation:mongo:mongo-3.1:library'
|
||||
include ':instrumentation:mongo:mongo-3.1:testing'
|
||||
include ':instrumentation:mongo:mongo-3.7:javaagent'
|
||||
include ':instrumentation:mongo:mongo-4.0:javaagent'
|
||||
include ':instrumentation:mongo:mongo-async-3.3:javaagent'
|
||||
include ':instrumentation:mongo:mongo-common:javaagent'
|
||||
include ':instrumentation:mongo:mongo-testing'
|
||||
include ':instrumentation:netty:netty-3.8:javaagent'
|
||||
include ':instrumentation:netty:netty-4.0:javaagent'
|
||||
|
|
Loading…
Reference in New Issue