Skip to content

Commit d14a561

Browse files
committed
Enable SqlCommand telemetry
1 parent a436aab commit d14a561

File tree

2 files changed

+86
-19
lines changed

2 files changed

+86
-19
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1233,7 +1233,7 @@ public override int ExecuteNonQuery()
12331233
// between entry into Execute* API and the thread obtaining the stateObject.
12341234
_pendingCancel = false;
12351235

1236-
using (var diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction))
1236+
using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction))
12371237
using (TryEventScope.Create("SqlCommand.ExecuteNonQuery | API | Object Id {0}", ObjectID))
12381238
{
12391239
SqlStatistics statistics = null;

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using System.Buffers;
2222
using Microsoft.Data.Common;
2323
using Microsoft.Data.Sql;
24+
using Microsoft.Data.SqlClient.Diagnostics;
2425
using Microsoft.Data.SqlClient.Server;
2526
using System.Transactions;
2627
using System.Collections.Concurrent;
@@ -162,6 +163,10 @@ protected override void AfterCleared(SqlCommand owner)
162163
/// </summary>
163164
private static bool _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false;
164165
#endif
166+
167+
private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener();
168+
private bool _parentOperationStarted = false;
169+
165170
internal static readonly Action<object> s_cancelIgnoreFailure = CancelIgnoreFailureCallback;
166171

167172
// Prepare
@@ -632,7 +637,8 @@ internal SqlStatistics Statistics
632637
{
633638
if (_activeConnection != null)
634639
{
635-
if (_activeConnection.StatisticsEnabled)
640+
if (_activeConnection.StatisticsEnabled ||
641+
s_diagnosticListener.IsEnabled(SqlClientCommandAfter.Name))
636642
{
637643
return _activeConnection.Statistics;
638644
}
@@ -1228,6 +1234,7 @@ public override object ExecuteScalar()
12281234
_pendingCancel = false;
12291235
SqlStatistics statistics = null;
12301236

1237+
using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction))
12311238
using (TryEventScope.Create("SqlCommand.ExecuteScalar | API | ObjectId {0}", ObjectID))
12321239
{
12331240
bool success = false;
@@ -1245,9 +1252,13 @@ public override object ExecuteScalar()
12451252
success = true;
12461253
return result;
12471254
}
1248-
catch (SqlException ex)
1255+
catch (Exception ex)
12491256
{
1250-
sqlExceptionNumber = ex.Number;
1257+
diagnosticScope.SetException(ex);
1258+
if (ex is SqlException sqlException)
1259+
{
1260+
sqlExceptionNumber = sqlException.Number;
1261+
}
12511262
throw;
12521263
}
12531264
finally
@@ -1318,6 +1329,7 @@ public override int ExecuteNonQuery()
13181329

13191330
SqlStatistics statistics = null;
13201331

1332+
using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction))
13211333
using (TryEventScope.Create("<sc.SqlCommand.ExecuteNonQuery|API> {0}", ObjectID))
13221334
{
13231335
bool success = false;
@@ -1344,9 +1356,13 @@ public override int ExecuteNonQuery()
13441356
success = true;
13451357
return _rowsAffected;
13461358
}
1347-
catch (SqlException ex)
1359+
catch (Exception ex)
13481360
{
1349-
sqlExceptionNumber = ex.Number;
1361+
diagnosticScope.SetException(ex);
1362+
if (ex is SqlException sqlException)
1363+
{
1364+
sqlExceptionNumber = sqlException.Number;
1365+
}
13501366
throw;
13511367
}
13521368
finally
@@ -1916,6 +1932,7 @@ public XmlReader ExecuteXmlReader()
19161932

19171933
SqlStatistics statistics = null;
19181934

1935+
using (DiagnosticScope diagnosticScope = s_diagnosticListener.CreateCommandScope(this, _transaction))
19191936
using (TryEventScope.Create("SqlCommand.ExecuteXmlReader | API | Object Id {0}", ObjectID))
19201937
{
19211938
bool success = false;
@@ -1935,9 +1952,13 @@ public XmlReader ExecuteXmlReader()
19351952
success = true;
19361953
return result;
19371954
}
1938-
catch (SqlException ex)
1955+
catch (Exception ex)
19391956
{
1940-
sqlExceptionNumber = ex.Number;
1957+
diagnosticScope.SetException(ex);
1958+
if (ex is SqlException sqlException)
1959+
{
1960+
sqlExceptionNumber = sqlException.Number;
1961+
}
19411962
throw;
19421963
}
19431964
finally
@@ -2293,6 +2314,8 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
22932314
RuntimeHelpers.PrepareConstrainedRegions();
22942315
bool success = false;
22952316
int? sqlExceptionNumber = null;
2317+
Exception e = null;
2318+
Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
22962319

22972320
using (TryEventScope.Create("SqlCommand.ExecuteReader | API | Object Id {0}", ObjectID))
22982321
{
@@ -2307,31 +2330,44 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
23072330
success = true;
23082331
return result;
23092332
}
2310-
catch (SqlException e)
2333+
catch (System.OutOfMemoryException ex)
23112334
{
2312-
sqlExceptionNumber = e.Number;
2335+
_activeConnection.Abort(ex);
23132336
throw;
23142337
}
2315-
catch (System.OutOfMemoryException e)
2338+
catch (System.StackOverflowException ex)
23162339
{
2317-
_activeConnection.Abort(e);
2340+
_activeConnection.Abort(ex);
23182341
throw;
23192342
}
2320-
catch (System.StackOverflowException e)
2343+
catch (System.Threading.ThreadAbortException ex)
23212344
{
2322-
_activeConnection.Abort(e);
2345+
_activeConnection.Abort(ex);
2346+
SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
23232347
throw;
23242348
}
2325-
catch (System.Threading.ThreadAbortException e)
2349+
catch (Exception ex)
23262350
{
2327-
_activeConnection.Abort(e);
2328-
SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
2351+
if (ex is SqlException sqlException)
2352+
{
2353+
sqlExceptionNumber = sqlException.Number;
2354+
}
2355+
2356+
e = ex;
23292357
throw;
23302358
}
23312359
finally
23322360
{
23332361
SqlStatistics.StopTimer(statistics);
23342362
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
2363+
if (e != null)
2364+
{
2365+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
2366+
}
2367+
else
2368+
{
2369+
s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
2370+
}
23352371
}
23362372
}
23372373
}
@@ -2433,10 +2469,18 @@ private void CleanupExecuteReaderAsync(Task<SqlDataReader> task, TaskCompletionS
24332469
if (task.IsFaulted)
24342470
{
24352471
Exception e = task.Exception.InnerException;
2472+
if (!_parentOperationStarted)
2473+
{
2474+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
2475+
}
24362476
source.SetException(e);
24372477
}
24382478
else
24392479
{
2480+
if (!_parentOperationStarted)
2481+
{
2482+
s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
2483+
}
24402484
if (task.IsCanceled)
24412485
{
24422486
source.SetCanceled();
@@ -2832,7 +2876,7 @@ private Task<int> InternalExecuteNonQueryAsync(CancellationToken cancellationTok
28322876
{
28332877
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteNonQueryAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
28342878
SqlConnection.ExecutePermission.Demand();
2835-
Guid operationId = Guid.Empty;
2879+
Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
28362880

28372881
// connection can be used as state in RegisterForConnectionCloseNotification continuation
28382882
// to avoid an allocation so use it as the state value if possible but it can be changed if
@@ -2885,6 +2929,7 @@ private Task<int> InternalExecuteNonQueryAsync(CancellationToken cancellationTok
28852929
}
28862930
catch (Exception e)
28872931
{
2932+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
28882933
source.SetException(e);
28892934
context.Dispose();
28902935
}
@@ -2897,6 +2942,7 @@ private void CleanupAfterExecuteNonQueryAsync(Task<int> task, TaskCompletionSour
28972942
if (task.IsFaulted)
28982943
{
28992944
Exception e = task.Exception.InnerException;
2945+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
29002946
source.SetException(e);
29012947
}
29022948
else
@@ -2909,6 +2955,7 @@ private void CleanupAfterExecuteNonQueryAsync(Task<int> task, TaskCompletionSour
29092955
{
29102956
source.SetResult(task.Result);
29112957
}
2958+
s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
29122959
}
29132960
}
29142961

@@ -2960,6 +3007,10 @@ private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior,
29603007
SqlClientEventSource.Log.TryTraceEvent("SqlCommand.InternalExecuteReaderAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'", ObjectID, Connection?.ClientConnectionId, CommandText);
29613008
SqlConnection.ExecutePermission.Demand();
29623009
Guid operationId = default(Guid);
3010+
if (!_parentOperationStarted)
3011+
{
3012+
operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
3013+
}
29633014

29643015
// connection can be used as state in RegisterForConnectionCloseNotification continuation
29653016
// to avoid an allocation so use it as the state value if possible but it can be changed if
@@ -3021,6 +3072,11 @@ private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior,
30213072
}
30223073
catch (Exception e)
30233074
{
3075+
if (!_parentOperationStarted)
3076+
{
3077+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
3078+
}
3079+
30243080
source.SetException(e);
30253081
context?.Dispose();
30263082
}
@@ -3059,6 +3115,9 @@ public override Task<object> ExecuteScalarAsync(CancellationToken cancellationTo
30593115

30603116
private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationToken)
30613117
{
3118+
_parentOperationStarted = true;
3119+
Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
3120+
30623121
return ExecuteReaderAsync(cancellationToken).ContinueWith((executeTask) =>
30633122
{
30643123
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
@@ -3068,6 +3127,7 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
30683127
}
30693128
else if (executeTask.IsFaulted)
30703129
{
3130+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, executeTask.Exception.InnerException);
30713131
source.SetException(executeTask.Exception.InnerException);
30723132
}
30733133
else
@@ -3086,6 +3146,7 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
30863146
else if (readTask.IsFaulted)
30873147
{
30883148
reader.Dispose();
3149+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, readTask.Exception.InnerException);
30893150
source.SetException(readTask.Exception.InnerException);
30903151
}
30913152
else
@@ -3113,10 +3174,12 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
31133174
}
31143175
if (exception != null)
31153176
{
3177+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, exception);
31163178
source.SetException(exception);
31173179
}
31183180
else
31193181
{
3182+
s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
31203183
source.SetResult(result);
31213184
}
31223185
}
@@ -3130,6 +3193,7 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
31303193
TaskScheduler.Default
31313194
);
31323195
}
3196+
_parentOperationStarted = false;
31333197
return source.Task;
31343198
}, TaskScheduler.Default).Unwrap();
31353199
}
@@ -3154,7 +3218,7 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
31543218
{
31553219
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.InternalExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
31563220
SqlConnection.ExecutePermission.Demand();
3157-
Guid operationId = Guid.Empty;
3221+
Guid operationId = s_diagnosticListener.WriteCommandBefore(this, _transaction);
31583222

31593223
// connection can be used as state in RegisterForConnectionCloseNotification continuation
31603224
// to avoid an allocation so use it as the state value if possible but it can be changed if
@@ -3214,6 +3278,7 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
32143278
}
32153279
catch (Exception e)
32163280
{
3281+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
32173282
source.SetException(e);
32183283
}
32193284

@@ -3225,6 +3290,7 @@ private void CleanupAfterExecuteXmlReaderAsync(Task<XmlReader> task, TaskComplet
32253290
if (task.IsFaulted)
32263291
{
32273292
Exception e = task.Exception.InnerException;
3293+
s_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
32283294
source.SetException(e);
32293295
}
32303296
else
@@ -3237,6 +3303,7 @@ private void CleanupAfterExecuteXmlReaderAsync(Task<XmlReader> task, TaskComplet
32373303
{
32383304
source.SetResult(task.Result);
32393305
}
3306+
s_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
32403307
}
32413308
}
32423309

0 commit comments

Comments
 (0)