1414*/
1515
1616using System ;
17+ using System . Diagnostics ;
1718using System . Threading . Tasks ;
1819using MongoDB . Driver . Core ;
1920using MongoDB . Driver . Core . Bindings ;
@@ -50,8 +51,41 @@ public TResult ExecuteReadOperation<TResult>(
5051 Ensure . IsNotNull ( readPreference , nameof ( readPreference ) ) ;
5152 ThrowIfDisposed ( ) ;
5253
53- using var binding = CreateReadBinding ( session , readPreference , allowChannelPinning ) ;
54- return operation . Execute ( operationContext , binding ) ;
54+ using var activityScope = TransactionActivityScope . CreateIfNeeded ( session ) ;
55+ using var activity = operationContext . IsTracingEnabled
56+ ? MongoTelemetry . StartOperationActivity (
57+ operationContext . OperationName ,
58+ operationContext . DatabaseName ,
59+ operationContext . CollectionName )
60+ : null ;
61+
62+ try
63+ {
64+ using var binding = CreateReadBinding ( session , readPreference , allowChannelPinning ) ;
65+ var result = operation . Execute ( operationContext , binding ) ;
66+ activity ? . SetStatus ( ActivityStatusCode . Ok ) ;
67+ return result ;
68+ }
69+ catch ( Exception ex )
70+ {
71+ // Only record exceptions that originate at the operation level
72+ // Command-level exceptions (MongoCommandException, MongoWriteException, etc.)
73+ // are already recorded by CommandEventHelper on the command span
74+ if ( activity != null )
75+ {
76+ if ( ! IsCommandLevelException ( ex ) )
77+ {
78+ MongoTelemetry . RecordException ( activity , ex ) ;
79+ }
80+ else
81+ {
82+ // For command-level exceptions, only set error status without recording details
83+ activity . SetStatus ( ActivityStatusCode . Error ) ;
84+ }
85+ }
86+
87+ throw ;
88+ }
5589 }
5690
5791 public async Task < TResult > ExecuteReadOperationAsync < TResult > (
@@ -67,8 +101,41 @@ public async Task<TResult> ExecuteReadOperationAsync<TResult>(
67101 Ensure . IsNotNull ( readPreference , nameof ( readPreference ) ) ;
68102 ThrowIfDisposed ( ) ;
69103
70- using var binding = CreateReadBinding ( session , readPreference , allowChannelPinning ) ;
71- return await operation . ExecuteAsync ( operationContext , binding ) . ConfigureAwait ( false ) ;
104+ using var activityScope = TransactionActivityScope . CreateIfNeeded ( session ) ;
105+ using var activity = operationContext . IsTracingEnabled
106+ ? MongoTelemetry . StartOperationActivity (
107+ operationContext . OperationName ,
108+ operationContext . DatabaseName ,
109+ operationContext . CollectionName )
110+ : null ;
111+
112+ try
113+ {
114+ using var binding = CreateReadBinding ( session , readPreference , allowChannelPinning ) ;
115+ var result = await operation . ExecuteAsync ( operationContext , binding ) . ConfigureAwait ( false ) ;
116+ activity ? . SetStatus ( ActivityStatusCode . Ok ) ;
117+ return result ;
118+ }
119+ catch ( Exception ex )
120+ {
121+ // Only record exceptions that originate at the operation level
122+ // Command-level exceptions (MongoCommandException, MongoWriteException, etc.)
123+ // are already recorded by CommandEventHelper on the command span
124+ if ( activity != null )
125+ {
126+ if ( ! IsCommandLevelException ( ex ) )
127+ {
128+ MongoTelemetry . RecordException ( activity , ex ) ;
129+ }
130+ else
131+ {
132+ // For command-level exceptions, only set error status without recording details
133+ activity . SetStatus ( ActivityStatusCode . Error ) ;
134+ }
135+ }
136+
137+ throw ;
138+ }
72139 }
73140
74141 public TResult ExecuteWriteOperation < TResult > (
@@ -82,8 +149,41 @@ public TResult ExecuteWriteOperation<TResult>(
82149 Ensure . IsNotNull ( operation , nameof ( operation ) ) ;
83150 ThrowIfDisposed ( ) ;
84151
85- using var binding = CreateReadWriteBinding ( session , allowChannelPinning ) ;
86- return operation . Execute ( operationContext , binding ) ;
152+ using var activityScope = TransactionActivityScope . CreateIfNeeded ( session ) ;
153+ using var activity = operationContext . IsTracingEnabled
154+ ? MongoTelemetry . StartOperationActivity (
155+ operationContext . OperationName ,
156+ operationContext . DatabaseName ,
157+ operationContext . CollectionName )
158+ : null ;
159+
160+ try
161+ {
162+ using var binding = CreateReadWriteBinding ( session , allowChannelPinning ) ;
163+ var result = operation . Execute ( operationContext , binding ) ;
164+ activity ? . SetStatus ( ActivityStatusCode . Ok ) ;
165+ return result ;
166+ }
167+ catch ( Exception ex )
168+ {
169+ // Only record exceptions that originate at the operation level
170+ // Command-level exceptions (MongoCommandException, MongoWriteException, etc.)
171+ // are already recorded by CommandEventHelper on the command span
172+ if ( activity != null )
173+ {
174+ if ( ! IsCommandLevelException ( ex ) )
175+ {
176+ MongoTelemetry . RecordException ( activity , ex ) ;
177+ }
178+ else
179+ {
180+ // For command-level exceptions, only set error status without recording details
181+ activity . SetStatus ( ActivityStatusCode . Error ) ;
182+ }
183+ }
184+
185+ throw ;
186+ }
87187 }
88188
89189 public async Task < TResult > ExecuteWriteOperationAsync < TResult > (
@@ -97,8 +197,41 @@ public async Task<TResult> ExecuteWriteOperationAsync<TResult>(
97197 Ensure . IsNotNull ( operation , nameof ( operation ) ) ;
98198 ThrowIfDisposed ( ) ;
99199
100- using var binding = CreateReadWriteBinding ( session , allowChannelPinning ) ;
101- return await operation . ExecuteAsync ( operationContext , binding ) . ConfigureAwait ( false ) ;
200+ using var activityScope = TransactionActivityScope . CreateIfNeeded ( session ) ;
201+ using var activity = operationContext . IsTracingEnabled
202+ ? MongoTelemetry . StartOperationActivity (
203+ operationContext . OperationName ,
204+ operationContext . DatabaseName ,
205+ operationContext . CollectionName )
206+ : null ;
207+
208+ try
209+ {
210+ using var binding = CreateReadWriteBinding ( session , allowChannelPinning ) ;
211+ var result = await operation . ExecuteAsync ( operationContext , binding ) . ConfigureAwait ( false ) ;
212+ activity ? . SetStatus ( ActivityStatusCode . Ok ) ;
213+ return result ;
214+ }
215+ catch ( Exception ex )
216+ {
217+ // Only record exceptions that originate at the operation level
218+ // Command-level exceptions (MongoCommandException, MongoWriteException, etc.)
219+ // are already recorded by CommandEventHelper on the command span
220+ if ( activity != null )
221+ {
222+ if ( ! IsCommandLevelException ( ex ) )
223+ {
224+ MongoTelemetry . RecordException ( activity , ex ) ;
225+ }
226+ else
227+ {
228+ // For command-level exceptions, only set error status without recording details
229+ activity . SetStatus ( ActivityStatusCode . Error ) ;
230+ }
231+ }
232+
233+ throw ;
234+ }
102235 }
103236
104237 public IClientSessionHandle StartImplicitSession ( )
@@ -143,5 +276,40 @@ private void ThrowIfDisposed()
143276 throw new ObjectDisposedException ( nameof ( OperationExecutor ) ) ;
144277 }
145278 }
279+
280+ private static bool IsCommandLevelException ( Exception ex )
281+ {
282+ // Command-level exceptions are those that originate from MongoDB server
283+ return ex is MongoServerException ;
284+ }
285+
286+ /// <summary>
287+ /// Manages Activity.Current scope when executing operations within a transaction.
288+ /// Temporarily sets Activity.Current to the transaction activity so operation activities nest correctly,
289+ /// then restores to the original value to prevent AsyncLocal flow issues.
290+ /// </summary>
291+ private sealed class TransactionActivityScope : IDisposable
292+ {
293+ private readonly Activity _originalActivity ;
294+
295+ private TransactionActivityScope ( Activity transactionActivity )
296+ {
297+ _originalActivity = Activity . Current ;
298+ Activity . Current = transactionActivity ;
299+ }
300+
301+ public static TransactionActivityScope CreateIfNeeded ( IClientSessionHandle session )
302+ {
303+ var transactionActivity = session ? . WrappedCoreSession ? . CurrentTransaction ? . TransactionActivity ;
304+ return transactionActivity != null
305+ ? new TransactionActivityScope ( transactionActivity )
306+ : null ;
307+ }
308+
309+ public void Dispose ( )
310+ {
311+ Activity . Current = _originalActivity ;
312+ }
313+ }
146314 }
147315}
0 commit comments