-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-3124: OpenTelemetry implementation #1837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
53e5434 to
f2e383d
Compare
sanych-sun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review. Also please check failing tests
| { | ||
| // Check if all requests are of the same type | ||
| var firstType = requests[0].ModelType; | ||
| var allSameType = requests.All(r => r.ModelType == firstType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having this code here will lead to multiple enumerations of the requests. We had a ticket to remove the multiple enumerations. Should we move this code into the CreateBulkWriteOperation and merge it into the mapping loop?
| connectionId, | ||
| $"{state.CommandName} command failed", | ||
| null, | ||
| reply); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we construct the exception just to record it and never throw it? Looks suspicious.
| <PackageReference Include="Snappier" Version="1.0.0" /> | ||
| <PackageReference Include="ZstdSharp.Port" Version="0.7.3" /> | ||
| <PackageReference Include="System.Buffers" Version="4.5.1" /> | ||
| <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.0" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose the System.Diagnostics.DiagnosticSource version should match the target framework.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And looks like it's not needed for net6.
| return new OperationContext(Clock, InitialTimestamp, Timeout, CancellationToken) | ||
| { | ||
| RootContext = RootContext, | ||
| OperationName = operationName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should copy values of all otel related fields in WithTimeout method as well. Otherwise they will be lost if we have to apply the timeout.
|
|
||
| internal OperationContext WithOperationMetadata(string operationName, string databaseName, string collectionName, bool isTracingEnabled) | ||
| { | ||
| return new OperationContext(Clock, InitialTimestamp, Timeout, CancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Providing InitialTimestamp and Timeout here looks wrong to me, because copied operationContext is suppose to "continue from the point where it was copied". We probably should provide RemainingTimeout instead, in the same way as WithTimeout doing..
| }; | ||
| } | ||
|
|
||
| internal OperationContext WithOperationMetadata(string operationName, string databaseName, string collectionName, bool isTracingEnabled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add unit tests for the new method.
| public bool LoadBalanced => _loadBalanced; | ||
| public TimeSpan LocalThreshold { get { return _localThreshold; } } | ||
| public LoggingSettings LoggingSettings { get { return _loggingSettings; } } | ||
| public TracingOptions TracingOptions { get { return _tracingOptions; } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add comparison of the TracingOptions to the Equals method?
BorisDog
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, partial review.
| <PackageReference Include="Snappier" Version="1.0.0" /> | ||
| <PackageReference Include="ZstdSharp.Port" Version="0.7.3" /> | ||
| <PackageReference Include="System.Buffers" Version="4.5.1" /> | ||
| <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we include this in BSON lib instead, just in case we'd want to add serialization Otel?
| /// </summary> | ||
| public static class MongoTelemetry | ||
| { | ||
| private static readonly string s_driverVersion = ClientDocumentHelper.GetAssemblyVersion(typeof(MongoClient).Assembly); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__driverVersion
| /// The ActivitySource used by MongoDB driver for OpenTelemetry tracing. | ||
| /// Applications can subscribe to this source to receive MongoDB traces. | ||
| /// </summary> | ||
| public static readonly ActivitySource ActivitySource = new("MongoDB.Driver", s_driverVersion); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want/need to expose this publicly? Is it the recommended way?
| { | ||
| var activity = ActivitySource.StartActivity(commandName, ActivityKind.Client); | ||
|
|
||
| if (activity == null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: activity is checked for null by the callers, so maybe check in one place only?
|
|
||
| SetConnectionTags(activity, connectionId); | ||
|
|
||
| if (command != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what cases is the command null?
| else | ||
| { | ||
| // For command-level exceptions, only set error status without recording details | ||
| activity.SetStatus(ActivityStatusCode.Error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor: maybe this whole if/else block should move into MongoTelemetry.RecordException?
| /// When set to true, no OpenTelemetry activities will be created for this client's operations. | ||
| /// Default is false (tracing enabled if configured via TracerProvider). | ||
| /// </summary> | ||
| public bool Disabled { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this additional Disabled switch?
| _shouldTrackState = _shouldTrackSucceeded || _shouldTrackFailed; | ||
|
|
||
| // Check if tracing is disabled for this client | ||
| _shouldTrace = _tracingOptions?.Disabled != true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably not sufficient.
Do we still want to trace if there are not listeners?
| operationContext.OperationName, | ||
| operationContext.DatabaseName, | ||
| operationContext.CollectionName) | ||
| : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider more compact overload:
MongoTelemetry.StartOperationActivity(operationContext);
And ideally (if it's reasonable of course) everything related to telemetry should be handled in single place, so should
TransactionActivityScope be under MongoTelemetry? Should MongoTelemetry.StartOperationActivity also invoke TransactionActivityScope ?
| public CollectionNamespace QueryNamespace; | ||
| public ExpectedResponseType ExpectedResponseType; | ||
| public bool ShouldRedactReply; | ||
| public Activity CommandActivity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this Activity need to be disposed?
| _settings.TranslationOptions); | ||
|
|
||
| private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, CancellationToken cancellationToken) | ||
| private OperationContext CreateOperationContext(IClientSessionHandle session, TimeSpan? timeout, CancellationToken cancellationToken, string operationName = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw I know Cancellation token should be the last parameter here but I did it this way for now since this work is still a draft and I didn't want to have to update every call.
Same idea stands for ExecuteReadOperation and ExecuteWriteOperation
No description provided.