Skip to content

Commit 6bf4fa0

Browse files
nhachichavbabaninrozza
authored
Add tracing support using Micrometer (#1695)
Add tracing support using Micrometer via the Observation API JAVA-5733 --------- Co-authored-by: Viacheslav Babanin <[email protected]> Co-authored-by: Ross Lawley <[email protected]>
1 parent e1bf5f5 commit 6bf4fa0

File tree

89 files changed

+2543
-105
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+2543
-105
lines changed

buildSrc/src/main/kotlin/project/Companion.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ import org.gradle.kotlin.dsl.getByType
2323
internal val Project.libs: LibrariesForLibs
2424
get() = extensions.getByType()
2525

26-
internal const val DEFAULT_JAVA_VERSION = 17
26+
const val DEFAULT_JAVA_VERSION = 17

config/checkstyle/suppressions.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
<!-- Allow printStackTrace in this file -->
6262
<suppress checks="Regexp" files="CallbackResultHolder"/>
63+
<suppress checks="Regexp" files="MicrometerTracer"/>
6364

6465
<!--Do not check documentation tests classes -->
6566
<suppress checks="Javadoc*" files=".*documentation.*"/>

driver-core/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ dependencies {
5555
optionalImplementation(libs.snappy.java)
5656
optionalImplementation(libs.zstd.jni)
5757

58+
optionalImplementation(platform(libs.micrometer.observation.bom))
59+
optionalImplementation(libs.micrometer.observation)
60+
5861
testImplementation(project(path = ":bson", configuration = "testArtifacts"))
5962
testImplementation(libs.reflections)
6063
testImplementation(libs.netty.tcnative.boringssl.static)

driver-core/src/main/com/mongodb/MongoClientSettings.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.mongodb.connection.TransportSettings;
3232
import com.mongodb.event.CommandListener;
3333
import com.mongodb.lang.Nullable;
34+
import com.mongodb.observability.ObservabilitySettings;
3435
import com.mongodb.spi.dns.DnsClient;
3536
import com.mongodb.spi.dns.InetAddressResolver;
3637
import org.bson.UuidRepresentation;
@@ -116,6 +117,7 @@ public final class MongoClientSettings {
116117
private final ContextProvider contextProvider;
117118
private final DnsClient dnsClient;
118119
private final InetAddressResolver inetAddressResolver;
120+
private final ObservabilitySettings observabilitySettings;
119121
@Nullable
120122
private final Long timeoutMS;
121123

@@ -215,6 +217,7 @@ public static final class Builder {
215217
private ReadConcern readConcern = ReadConcern.DEFAULT;
216218
private CodecRegistry codecRegistry = MongoClientSettings.getDefaultCodecRegistry();
217219
private TransportSettings transportSettings;
220+
private ObservabilitySettings observabilitySettings;
218221
private List<CommandListener> commandListeners = new ArrayList<>();
219222

220223
private final LoggerSettings.Builder loggerSettingsBuilder = LoggerSettings.builder();
@@ -260,6 +263,7 @@ private Builder(final MongoClientSettings settings) {
260263
timeoutMS = settings.getTimeout(MILLISECONDS);
261264
inetAddressResolver = settings.getInetAddressResolver();
262265
transportSettings = settings.getTransportSettings();
266+
observabilitySettings = settings.getObservabilitySettings();
263267
autoEncryptionSettings = settings.getAutoEncryptionSettings();
264268
contextProvider = settings.getContextProvider();
265269
loggerSettingsBuilder.applySettings(settings.getLoggerSettings());
@@ -506,6 +510,19 @@ public Builder transportSettings(final TransportSettings transportSettings) {
506510
return this;
507511
}
508512

513+
/** Sets the {@link ObservabilitySettings} to apply.
514+
*
515+
* @param observabilitySettings the observability settings
516+
* @return this
517+
* @see #getObservabilitySettings()
518+
* @since 5.7
519+
*/
520+
@Alpha(Reason.CLIENT)
521+
public Builder observabilitySettings(final ObservabilitySettings observabilitySettings) {
522+
this.observabilitySettings = notNull("observabilitySettings", observabilitySettings);
523+
return this;
524+
}
525+
509526
/**
510527
* Adds the given command listener.
511528
*
@@ -1040,6 +1057,18 @@ public ContextProvider getContextProvider() {
10401057
return contextProvider;
10411058
}
10421059

1060+
1061+
/**
1062+
* Get the observability settings.
1063+
* @return the observability settings
1064+
* @since 5.7
1065+
*/
1066+
@Alpha(Reason.CLIENT)
1067+
@Nullable
1068+
public ObservabilitySettings getObservabilitySettings() {
1069+
return observabilitySettings;
1070+
}
1071+
10431072
@Override
10441073
public boolean equals(final Object o) {
10451074
if (this == o) {
@@ -1137,6 +1166,7 @@ private MongoClientSettings(final Builder builder) {
11371166
socketSettings = builder.socketSettingsBuilder.build();
11381167
connectionPoolSettings = builder.connectionPoolSettingsBuilder.build();
11391168
sslSettings = builder.sslSettingsBuilder.build();
1169+
observabilitySettings = builder.observabilitySettings;
11401170
compressorList = builder.compressorList;
11411171
uuidRepresentation = builder.uuidRepresentation;
11421172
serverApi = builder.serverApi;

driver-core/src/main/com/mongodb/MongoNamespace.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb;
1818

1919
import com.mongodb.annotations.Immutable;
20+
import com.mongodb.annotations.Internal;
2021
import org.bson.codecs.pojo.annotations.BsonCreator;
2122
import org.bson.codecs.pojo.annotations.BsonIgnore;
2223
import org.bson.codecs.pojo.annotations.BsonProperty;
@@ -40,12 +41,15 @@ public final class MongoNamespace {
4041
* @deprecated there is no replacement for this constant, as it is only needed for the OP_QUERY wire protocol message, which has
4142
* been replaced by OP_MSG
4243
*/
43-
@Deprecated
44-
public static final String COMMAND_COLLECTION_NAME = "$cmd";
45-
4644
private static final Set<Character> PROHIBITED_CHARACTERS_IN_DATABASE_NAME =
4745
new HashSet<>(asList('\0', '/', '\\', ' ', '"', '.'));
4846

47+
@Internal
48+
public static final String COMMAND_COLLECTION_NAME = "$cmd";
49+
50+
@Internal
51+
public static final MongoNamespace ADMIN_DB_COMMAND_NAMESPACE = new MongoNamespace("admin", COMMAND_COLLECTION_NAME);
52+
4953
private final String databaseName;
5054
private final String collectionName;
5155
@BsonIgnore

driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.mongodb.internal.logging.StructuredLogger;
5252
import com.mongodb.internal.session.SessionContext;
5353
import com.mongodb.internal.time.Timeout;
54+
import com.mongodb.internal.tracing.Span;
5455
import com.mongodb.lang.Nullable;
5556
import org.bson.BsonBinaryReader;
5657
import org.bson.BsonDocument;
@@ -94,6 +95,8 @@
9495
import static com.mongodb.internal.connection.ProtocolHelper.isCommandOk;
9596
import static com.mongodb.internal.logging.LogMessage.Level.DEBUG;
9697
import static com.mongodb.internal.thread.InterruptionUtil.translateInterruptedException;
98+
import static com.mongodb.internal.tracing.MongodbObservation.HighCardinalityKeyNames.QUERY_TEXT;
99+
import static com.mongodb.internal.tracing.MongodbObservation.LowCardinalityKeyNames.RESPONSE_STATUS_CODE;
97100
import static java.util.Arrays.asList;
98101

99102
/**
@@ -432,22 +435,59 @@ public boolean reauthenticationIsTriggered(@Nullable final Throwable t) {
432435
private <T> T sendAndReceiveInternal(final CommandMessage message, final Decoder<T> decoder,
433436
final OperationContext operationContext) {
434437
CommandEventSender commandEventSender;
438+
Span tracingSpan;
435439
try (ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(this)) {
436440
message.encode(bsonOutput, operationContext);
437-
commandEventSender = createCommandEventSender(message, bsonOutput, operationContext);
438-
commandEventSender.sendStartedEvent();
441+
tracingSpan = operationContext
442+
.getTracingManager()
443+
.createTracingSpan(message,
444+
operationContext,
445+
() -> message.getCommandDocument(bsonOutput),
446+
cmdName -> SECURITY_SENSITIVE_COMMANDS.contains(cmdName)
447+
|| SECURITY_SENSITIVE_HELLO_COMMANDS.contains(cmdName),
448+
() -> getDescription().getServerAddress(),
449+
() -> getDescription().getConnectionId()
450+
);
451+
452+
boolean isLoggingCommandNeeded = isLoggingCommandNeeded();
453+
boolean isTracingCommandPayloadNeeded = tracingSpan != null && operationContext.getTracingManager().isCommandPayloadEnabled();
454+
455+
// Only hydrate the command document if necessary
456+
BsonDocument commandDocument = null;
457+
if (isLoggingCommandNeeded || isTracingCommandPayloadNeeded) {
458+
commandDocument = message.getCommandDocument(bsonOutput);
459+
}
460+
if (isLoggingCommandNeeded) {
461+
commandEventSender = new LoggingCommandEventSender(
462+
SECURITY_SENSITIVE_COMMANDS, SECURITY_SENSITIVE_HELLO_COMMANDS, description, commandListener,
463+
operationContext, message, commandDocument,
464+
COMMAND_PROTOCOL_LOGGER, loggerSettings);
465+
commandEventSender.sendStartedEvent();
466+
} else {
467+
commandEventSender = new NoOpCommandEventSender();
468+
}
469+
if (isTracingCommandPayloadNeeded) {
470+
tracingSpan.tagHighCardinality(QUERY_TEXT.asString(), commandDocument);
471+
}
472+
439473
try {
440474
sendCommandMessage(message, bsonOutput, operationContext);
441475
} catch (Exception e) {
476+
if (tracingSpan != null) {
477+
tracingSpan.error(e);
478+
}
442479
commandEventSender.sendFailedEvent(e);
443480
throw e;
444481
}
445482
}
446483

447484
if (message.isResponseExpected()) {
448-
return receiveCommandMessageResponse(decoder, commandEventSender, operationContext);
485+
return receiveCommandMessageResponse(decoder, commandEventSender, operationContext, tracingSpan);
449486
} else {
450487
commandEventSender.sendSucceededEventForOneWayCommand();
488+
if (tracingSpan != null) {
489+
tracingSpan.end();
490+
}
451491
return null;
452492
}
453493
}
@@ -466,7 +506,7 @@ public <T> void send(final CommandMessage message, final Decoder<T> decoder, fin
466506
@Override
467507
public <T> T receive(final Decoder<T> decoder, final OperationContext operationContext) {
468508
isTrue("Response is expected", hasMoreToCome);
469-
return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), operationContext);
509+
return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), operationContext, null);
470510
}
471511

472512
@Override
@@ -512,7 +552,7 @@ private void trySendMessage(final CommandMessage message, final ByteBufferBsonOu
512552
}
513553

514554
private <T> T receiveCommandMessageResponse(final Decoder<T> decoder, final CommandEventSender commandEventSender,
515-
final OperationContext operationContext) {
555+
final OperationContext operationContext, @Nullable final Span tracingSpan) {
516556
boolean commandSuccessful = false;
517557
try (ResponseBuffers responseBuffers = receiveResponseBuffers(operationContext)) {
518558
updateSessionContext(operationContext.getSessionContext(), responseBuffers);
@@ -537,7 +577,17 @@ private <T> T receiveCommandMessageResponse(final Decoder<T> decoder, final Comm
537577
if (!commandSuccessful) {
538578
commandEventSender.sendFailedEvent(e);
539579
}
580+
if (tracingSpan != null) {
581+
if (e instanceof MongoCommandException) {
582+
tracingSpan.tagLowCardinality(RESPONSE_STATUS_CODE.withValue(String.valueOf(((MongoCommandException) e).getErrorCode())));
583+
}
584+
tracingSpan.error(e);
585+
}
540586
throw e;
587+
} finally {
588+
if (tracingSpan != null) {
589+
tracingSpan.end();
590+
}
541591
}
542592
}
543593

@@ -553,7 +603,18 @@ private <T> void sendAndReceiveAsyncInternal(final CommandMessage message, final
553603

554604
try {
555605
message.encode(bsonOutput, operationContext);
556-
CommandEventSender commandEventSender = createCommandEventSender(message, bsonOutput, operationContext);
606+
607+
CommandEventSender commandEventSender;
608+
if (isLoggingCommandNeeded()) {
609+
BsonDocument commandDocument = message.getCommandDocument(bsonOutput);
610+
commandEventSender = new LoggingCommandEventSender(
611+
SECURITY_SENSITIVE_COMMANDS, SECURITY_SENSITIVE_HELLO_COMMANDS, description, commandListener,
612+
operationContext, message, commandDocument,
613+
COMMAND_PROTOCOL_LOGGER, loggerSettings);
614+
} else {
615+
commandEventSender = new NoOpCommandEventSender();
616+
}
617+
557618
commandEventSender.sendStartedEvent();
558619
Compressor localSendCompressor = sendCompressor;
559620
if (localSendCompressor == null || SECURITY_SENSITIVE_COMMANDS.contains(message.getCommandDocument(bsonOutput).getFirstKey())) {
@@ -952,19 +1013,13 @@ public void onResult(@Nullable final ByteBuf result, @Nullable final Throwable t
9521013

9531014
private static final StructuredLogger COMMAND_PROTOCOL_LOGGER = new StructuredLogger("protocol.command");
9541015

955-
private CommandEventSender createCommandEventSender(final CommandMessage message, final ByteBufferBsonOutput bsonOutput,
956-
final OperationContext operationContext) {
1016+
private boolean isLoggingCommandNeeded() {
9571017
boolean listensOrLogs = commandListener != null || COMMAND_PROTOCOL_LOGGER.isRequired(DEBUG, getClusterId());
958-
if (!recordEverything && (isMonitoringConnection || !opened() || !authenticated.get() || !listensOrLogs)) {
959-
return new NoOpCommandEventSender();
960-
}
961-
return new LoggingCommandEventSender(
962-
SECURITY_SENSITIVE_COMMANDS, SECURITY_SENSITIVE_HELLO_COMMANDS, description, commandListener,
963-
operationContext, message, bsonOutput,
964-
COMMAND_PROTOCOL_LOGGER, loggerSettings);
1018+
return recordEverything || (!isMonitoringConnection && opened() && authenticated.get() && listensOrLogs);
9651019
}
9661020

9671021
private ClusterId getClusterId() {
9681022
return description.getConnectionId().getServerId().getClusterId();
9691023
}
1024+
9701025
}

driver-core/src/main/com/mongodb/internal/connection/LoggingCommandEventSender.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class LoggingCommandEventSender implements CommandEventSender {
7878
@Nullable final CommandListener commandListener,
7979
final OperationContext operationContext,
8080
final CommandMessage message,
81-
final ByteBufferBsonOutput bsonOutput,
81+
final BsonDocument commandDocument,
8282
final StructuredLogger logger,
8383
final LoggerSettings loggerSettings) {
8484
this.description = description;
@@ -88,7 +88,7 @@ class LoggingCommandEventSender implements CommandEventSender {
8888
this.loggerSettings = loggerSettings;
8989
this.startTimeNanos = System.nanoTime();
9090
this.message = message;
91-
this.commandDocument = message.getCommandDocument(bsonOutput);
91+
this.commandDocument = commandDocument;
9292
this.commandName = commandDocument.getFirstKey();
9393
this.redactionRequired = securitySensitiveCommands.contains(commandName)
9494
|| (securitySensitiveHelloCommands.contains(commandName) && commandDocument.containsKey("speculativeAuthenticate"));

0 commit comments

Comments
 (0)