Skip to content

Commit da1f9ab

Browse files
authored
Simplify diagnostic tracing (#1193)
1 parent bc4edc6 commit da1f9ab

File tree

3 files changed

+252
-152
lines changed

3 files changed

+252
-152
lines changed

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

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,5 +346,160 @@ public static void WriteTransactionRollbackError(this SqlDiagnosticListener @thi
346346
});
347347
}
348348
}
349+
350+
public static DiagnosticScope CreateCommandScope(this SqlDiagnosticListener @this, SqlCommand command, SqlTransaction transaction, [CallerMemberName] string operationName = "")
351+
{
352+
return DiagnosticScope.CreateCommandScope(@this, command, transaction, operationName);
353+
}
354+
355+
public static DiagnosticTransactionScope CreateTransactionCommitScope(this SqlDiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operationName = "")
356+
{
357+
return DiagnosticTransactionScope.CreateTransactionCommitScope(@this, isolationLevel, connection, transaction, operationName);
358+
}
359+
360+
public static DiagnosticTransactionScope CreateTransactionRollbackScope(this SqlDiagnosticListener @this, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName, [CallerMemberName] string operationName = "")
361+
{
362+
return DiagnosticTransactionScope.CreateTransactionRollbackScope(@this, isolationLevel, connection, transaction, transactionName, operationName);
363+
}
364+
}
365+
366+
internal ref struct DiagnosticScope //: IDisposable //ref structs cannot implement interfaces but the compiler will use pattern matching
367+
{
368+
private const int CommandOperation = 1;
369+
private const int ConnectionOpenOperation = 2;
370+
371+
private readonly SqlDiagnosticListener _diagnostics;
372+
private readonly int _operation;
373+
private readonly string _operationName;
374+
private readonly Guid _operationId;
375+
private readonly object _context1;
376+
private readonly object _context2;
377+
private Exception _exception;
378+
379+
private DiagnosticScope(SqlDiagnosticListener diagnostics, int operation, Guid operationsId, string operationName, object context1, object context2)
380+
{
381+
_diagnostics = diagnostics;
382+
_operation = operation;
383+
_operationId = operationsId;
384+
_operationName = operationName;
385+
_context1 = context1;
386+
_context2 = context2;
387+
_exception = null;
388+
}
389+
390+
public void Dispose()
391+
{
392+
switch (_operation)
393+
{
394+
case CommandOperation:
395+
if (_exception != null)
396+
{
397+
_diagnostics.WriteCommandError(_operationId, (SqlCommand)_context1, (SqlTransaction)_context2, _exception, _operationName);
398+
}
399+
else
400+
{
401+
_diagnostics.WriteCommandAfter(_operationId, (SqlCommand)_context1, (SqlTransaction)_context2, _operationName);
402+
}
403+
break;
404+
405+
case ConnectionOpenOperation:
406+
if (_exception != null)
407+
{
408+
_diagnostics.WriteConnectionOpenError(_operationId, (SqlConnection)_context1, _exception, _operationName);
409+
}
410+
else
411+
{
412+
_diagnostics.WriteConnectionOpenAfter(_operationId, (SqlConnection)_context1, _operationName);
413+
}
414+
break;
415+
416+
// ConnectionCloseOperation is not implemented because it is conditionally emitted and that requires manual calls to the write apis
417+
}
418+
}
419+
420+
public void SetException(Exception ex)
421+
{
422+
_exception = ex;
423+
}
424+
425+
public static DiagnosticScope CreateCommandScope(SqlDiagnosticListener diagnostics, SqlCommand command, SqlTransaction transaction, [CallerMemberName] string operationName = "")
426+
{
427+
Guid operationId = diagnostics.WriteCommandBefore(command, transaction, operationName);
428+
return new DiagnosticScope(diagnostics, CommandOperation, operationId, operationName, command, transaction);
429+
}
430+
}
431+
432+
internal ref struct DiagnosticTransactionScope //: IDisposable //ref structs cannot implement interfaces but the compiler will use pattern matching
433+
{
434+
public const int TransactionCommit = 1;
435+
public const int TransactionRollback = 2;
436+
437+
private readonly SqlDiagnosticListener _diagnostics;
438+
private readonly int _operation;
439+
private readonly Guid _operationId;
440+
private readonly string _operationName;
441+
private readonly IsolationLevel _isolationLevel;
442+
private readonly SqlConnection _connection;
443+
private readonly SqlInternalTransaction _transaction;
444+
private readonly string _transactionName;
445+
private Exception _exception;
446+
447+
public DiagnosticTransactionScope(SqlDiagnosticListener diagnostics, int operation, Guid operationId, string operationName, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName)
448+
{
449+
_diagnostics = diagnostics;
450+
_operation = operation;
451+
_operationId = operationId;
452+
_operationName = operationName;
453+
_isolationLevel = isolationLevel;
454+
_connection = connection;
455+
_transaction = transaction;
456+
_transactionName = transactionName;
457+
_exception = null;
458+
}
459+
460+
public void Dispose()
461+
{
462+
switch (_operation)
463+
{
464+
case TransactionCommit:
465+
if (_exception != null)
466+
{
467+
_diagnostics.WriteTransactionCommitError(_operationId, _isolationLevel, _connection, _transaction, _exception, _operationName);
468+
}
469+
else
470+
{
471+
_diagnostics.WriteTransactionCommitAfter(_operationId, _isolationLevel, _connection, _transaction, _operationName);
472+
}
473+
break;
474+
475+
case TransactionRollback:
476+
if (_exception != null)
477+
{
478+
_diagnostics.WriteTransactionRollbackError(_operationId, _isolationLevel, _connection, _transaction, _exception, _transactionName, _operationName);
479+
}
480+
else
481+
{
482+
_diagnostics.WriteTransactionRollbackAfter(_operationId, _isolationLevel, _connection, _transaction, _transactionName, _operationName);
483+
}
484+
break;
485+
}
486+
}
487+
488+
public void SetException(Exception ex)
489+
{
490+
_exception = ex;
491+
}
492+
493+
public static DiagnosticTransactionScope CreateTransactionCommitScope(SqlDiagnosticListener diagnostics, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, [CallerMemberName] string operationName = "")
494+
{
495+
Guid operationId = diagnostics.WriteTransactionCommitBefore(isolationLevel, connection, transaction, operationName);
496+
return new DiagnosticTransactionScope(diagnostics, TransactionCommit, operationId, operationName, isolationLevel, connection, transaction, null);
497+
}
498+
499+
public static DiagnosticTransactionScope CreateTransactionRollbackScope(SqlDiagnosticListener diagnostics, IsolationLevel isolationLevel, SqlConnection connection, SqlInternalTransaction transaction, string transactionName, [CallerMemberName] string operationName = "")
500+
{
501+
Guid operationId = diagnostics.WriteTransactionRollbackBefore(isolationLevel, connection, transaction, transactionName, operationName);
502+
return new DiagnosticTransactionScope(diagnostics, TransactionCommit, operationId, operationName, isolationLevel, connection, transaction, transactionName);
503+
}
349504
}
350505
}

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

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,51 +1082,37 @@ public override object ExecuteScalar()
10821082
// between entry into Execute* API and the thread obtaining the stateObject.
10831083
_pendingCancel = false;
10841084

1085-
SqlStatistics statistics = null;
1086-
Exception e = null;
1087-
bool success = false;
1088-
int? sqlExceptionNumber = null;
1089-
Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction);
1090-
1085+
using (DiagnosticScope diagnosticScope = _diagnosticListener.CreateCommandScope(this, _transaction))
10911086
using (TryEventScope.Create("SqlCommand.ExecuteScalar | API | ObjectId {0}", ObjectID))
10921087
{
1088+
SqlStatistics statistics = null;
1089+
bool success = false;
1090+
int? sqlExceptionNumber = null;
10931091
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteScalar | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
10941092

10951093
try
10961094
{
10971095
statistics = SqlStatistics.StartTimer(Statistics);
10981096
WriteBeginExecuteEvent();
1099-
SqlDataReader ds;
1100-
ds = IsProviderRetriable ?
1097+
SqlDataReader ds = IsProviderRetriable ?
11011098
RunExecuteReaderWithRetry(0, RunBehavior.ReturnImmediately, returnStream: true) :
11021099
RunExecuteReader(0, RunBehavior.ReturnImmediately, returnStream: true, method: nameof(ExecuteScalar));
11031100
success = true;
1104-
11051101
return CompleteExecuteScalar(ds, false);
11061102
}
11071103
catch (Exception ex)
11081104
{
1109-
if (ex is SqlException)
1105+
diagnosticScope.SetException(ex);
1106+
if (ex is SqlException sqlException)
11101107
{
1111-
SqlException exception = (SqlException)ex;
1112-
sqlExceptionNumber = exception.Number;
1108+
sqlExceptionNumber = sqlException.Number;
11131109
}
1114-
1115-
e = ex;
11161110
throw;
11171111
}
11181112
finally
11191113
{
11201114
SqlStatistics.StopTimer(statistics);
11211115
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
1122-
if (e != null)
1123-
{
1124-
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
1125-
}
1126-
else
1127-
{
1128-
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
1129-
}
11301116
}
11311117
}
11321118
}
@@ -1176,12 +1162,12 @@ public override int ExecuteNonQuery()
11761162
// between entry into Execute* API and the thread obtaining the stateObject.
11771163
_pendingCancel = false;
11781164

1179-
SqlStatistics statistics = null;
1180-
Exception e = null;
1181-
Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction);
1182-
1165+
using (var diagnosticScope = _diagnosticListener.CreateCommandScope(this, _transaction))
11831166
using (TryEventScope.Create("SqlCommand.ExecuteNonQuery | API | Object Id {0}", ObjectID))
11841167
{
1168+
SqlStatistics statistics = null;
1169+
bool success = false;
1170+
int? sqlExceptionNumber = null;
11851171
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteNonQuery | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}, Command Text {3}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
11861172

11871173
try
@@ -1196,24 +1182,22 @@ public override int ExecuteNonQuery()
11961182
{
11971183
InternalExecuteNonQuery(completion: null, sendToPipe: false, timeout: CommandTimeout, out _);
11981184
}
1185+
success = true;
11991186
return _rowsAffected;
12001187
}
12011188
catch (Exception ex)
12021189
{
1203-
e = ex;
1190+
diagnosticScope.SetException(ex);
1191+
if (ex is SqlException sqlException)
1192+
{
1193+
sqlExceptionNumber = sqlException.Number;
1194+
}
12041195
throw;
12051196
}
12061197
finally
12071198
{
12081199
SqlStatistics.StopTimer(statistics);
1209-
if (e != null)
1210-
{
1211-
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
1212-
}
1213-
else
1214-
{
1215-
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
1216-
}
1200+
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
12171201
}
12181202
}
12191203
}
@@ -1680,51 +1664,38 @@ public XmlReader ExecuteXmlReader()
16801664
// between entry into Execute* API and the thread obtaining the stateObject.
16811665
_pendingCancel = false;
16821666

1683-
SqlStatistics statistics = null;
1684-
bool success = false;
1685-
int? sqlExceptionNumber = null;
1686-
Exception e = null;
1687-
Guid operationId = _diagnosticListener.WriteCommandBefore(this, _transaction);
1688-
1667+
using (DiagnosticScope diagnosticScope = _diagnosticListener.CreateCommandScope(this, _transaction))
16891668
using (TryEventScope.Create("SqlCommand.ExecuteXmlReader | API | Object Id {0}", ObjectID))
16901669
{
1670+
SqlStatistics statistics = null;
1671+
bool success = false;
1672+
int? sqlExceptionNumber = null;
16911673
SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlCommand.ExecuteXmlReader | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId, CommandText);
16921674

16931675
try
16941676
{
16951677
statistics = SqlStatistics.StartTimer(Statistics);
16961678
WriteBeginExecuteEvent();
16971679
// use the reader to consume metadata
1698-
SqlDataReader ds;
1699-
ds = IsProviderRetriable ?
1680+
SqlDataReader ds = IsProviderRetriable ?
17001681
RunExecuteReaderWithRetry(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true) :
17011682
RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true);
17021683
success = true;
17031684
return CompleteXmlReader(ds);
17041685
}
17051686
catch (Exception ex)
17061687
{
1707-
e = ex;
1708-
if (ex is SqlException)
1688+
diagnosticScope.SetException(ex);
1689+
if (ex is SqlException sqlException)
17091690
{
1710-
SqlException exception = (SqlException)ex;
1711-
sqlExceptionNumber = exception.Number;
1691+
sqlExceptionNumber = sqlException.Number;
17121692
}
1713-
17141693
throw;
17151694
}
17161695
finally
17171696
{
17181697
SqlStatistics.StopTimer(statistics);
17191698
WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
1720-
if (e != null)
1721-
{
1722-
_diagnosticListener.WriteCommandError(operationId, this, _transaction, e);
1723-
}
1724-
else
1725-
{
1726-
_diagnosticListener.WriteCommandAfter(operationId, this, _transaction);
1727-
}
17281699
}
17291700
}
17301701
}

0 commit comments

Comments
 (0)