Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/System.Data.SqlClient/src/System.Data.SqlClient.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<Configuration Condition="'$(Configuration)'==''">Windows_Debug</Configuration>
Expand Down Expand Up @@ -88,6 +88,7 @@
<Compile Include="System\Data\SqlClient\SqlBulkCopyColumnMappingCollection.cs" />
<Compile Include="System\Data\SqlClient\SqlBulkCopyOptions.cs" />
<Compile Include="System\Data\SqlClient\SqlCachedBuffer.cs" />
<Compile Include="System\Data\SqlClient\SqlClientDiagnosticListenerExtensions.cs" />
<Compile Include="System\Data\SqlClient\SqlClientFactory.cs" />
<Compile Include="System\Data\SqlClient\SqlCommand.cs" />
<Compile Include="System\Data\SqlClient\SqlConnection.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Collections;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace System.Data.SqlClient
{
/// <summary>
/// Extension methods on the DiagnosticListener class to log SqlCommand data
/// </summary>
internal static class SqlClientDiagnosticListenerExtensions
{
public const string DiagnosticListenerName = "SqlClientDiagnosticListener";

private const string SqlClientPrefix = "System.Data.SqlClient.";
public const string SqlBeforeExecuteCommand = SqlClientPrefix + nameof(WriteCommandBefore);
public const string SqlAfterExecuteCommand = SqlClientPrefix + nameof(WriteCommandAfter);
public const string SqlErrorExecuteCommand = SqlClientPrefix + nameof(WriteCommandError);

public static Guid WriteCommandBefore(this DiagnosticListener @this, SqlCommand sqlCommand, [CallerMemberName] string operation = "")
{
if (@this.IsEnabled(SqlBeforeExecuteCommand))
{
Guid operationId = Guid.NewGuid();

@this.Write(
SqlBeforeExecuteCommand,
new
{
OperationId = operationId,
Operation = operation,
Command = sqlCommand
});

return operationId;
}
else
return Guid.Empty;
}

public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, [CallerMemberName] string operation = "")
{
if (@this.IsEnabled(SqlAfterExecuteCommand))
{
@this.Write(
SqlAfterExecuteCommand,
new
{
OperationId = operationId,
Operation = operation,
Command = sqlCommand,
Statistics = sqlCommand.Statistics?.GetDictionary(),
Timestamp = Stopwatch.GetTimestamp()
});
}
}

public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, SqlCommand sqlCommand, Exception ex, [CallerMemberName] string operation = "")
{
if (@this.IsEnabled(SqlErrorExecuteCommand))
{
@this.Write(
SqlErrorExecuteCommand,
new
{
OperationId = operationId,
Operation = operation,
Command = sqlCommand,
Exception = ex,
Timestamp = Stopwatch.GetTimestamp()
});
}
}
}
}
110 changes: 106 additions & 4 deletions src/System.Data.SqlClient/src/System/Data/SqlClient/SqlCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public sealed class SqlCommand : DbCommand
private UpdateRowSource _updatedRowSource = UpdateRowSource.Both;
private bool _designTimeInvisible;

private readonly static DiagnosticListener _diagnosticListener = new DiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName);
private bool _parentOperationStarted = false;

// Prepare
// Against 7.0 Serve a prepare/unprepare requires an extra roundtrip to the server.
//
Expand Down Expand Up @@ -290,7 +293,8 @@ internal SqlStatistics Statistics
{
if (null != _activeConnection)
{
if (_activeConnection.StatisticsEnabled)
if (_activeConnection.StatisticsEnabled ||
_diagnosticListener.IsEnabled(SqlClientDiagnosticListenerExtensions.SqlAfterExecuteCommand))
Copy link
Contributor

@saurabh500 saurabh500 May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What "enables" the diagnosticListener? How can this be disabled?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A listener is enabled when an observer is registered and self-reports as listening to the diagnosticListener. So in practice it will be disabled by default and then enabled once an interested observer shows up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbilling How is the observer registered? Any samples on the usage of this capability?
What code / class should I look at, to see what the observer is going to look like?

Copy link
Contributor

@nbilling nbilling May 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the tests there is a toy observer called FakeDiagnosticListenerObserver. It gets hooked up to the SqlClient DiagnosticListener on line DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver); (where diagnosticListenerObserver is a FakeDiagnosticListenerObserver).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @nbilling, for answering these questions! @saurabh500 let me know if you have any more questions about this.

{
return _activeConnection.Statistics;
}
Expand Down Expand Up @@ -738,19 +742,35 @@ override public object ExecuteScalar()
// between entry into Execute* API and the thread obtaining the stateObject.
_pendingCancel = false;

SqlStatistics statistics = null;

Guid operationId = _diagnosticListener.WriteCommandBefore(this);

SqlStatistics statistics = null;

Exception e = null;
try
{
statistics = SqlStatistics.StartTimer(Statistics);
SqlDataReader ds;
ds = RunExecuteReader(0, RunBehavior.ReturnImmediately, returnStream: true);
return CompleteExecuteScalar(ds, false);
}
catch (Exception ex)
{
e = ex;
throw;
}
finally
{
SqlStatistics.StopTimer(statistics);

if (e != null)
{
_diagnosticListener.WriteCommandError(operationId, this, e);
}
else
{
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}
}

Expand Down Expand Up @@ -790,16 +810,34 @@ override public int ExecuteNonQuery()
// between entry into Execute* API and the thread obtaining the stateObject.
_pendingCancel = false;

Guid operationId = _diagnosticListener.WriteCommandBefore(this);

SqlStatistics statistics = null;

Exception e = null;
try
{
statistics = SqlStatistics.StartTimer(Statistics);
InternalExecuteNonQuery(completion: null, sendToPipe: false, timeout: CommandTimeout);
return _rowsAffected;
}
catch (Exception ex)
{
e = ex;
throw;
}
finally
{
SqlStatistics.StopTimer(statistics);

if (e != null)
{
_diagnosticListener.WriteCommandError(operationId, this, e);
}
else
{
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}
}

Expand Down Expand Up @@ -1091,7 +1129,11 @@ public XmlReader ExecuteXmlReader()
// between entry into Execute* API and the thread obtaining the stateObject.
_pendingCancel = false;

Guid operationId = _diagnosticListener.WriteCommandBefore(this);

SqlStatistics statistics = null;

Exception e = null;
try
{
statistics = SqlStatistics.StartTimer(Statistics);
Expand All @@ -1101,9 +1143,23 @@ public XmlReader ExecuteXmlReader()
ds = RunExecuteReader(CommandBehavior.SequentialAccess, RunBehavior.ReturnImmediately, returnStream: true);
return CompleteXmlReader(ds);
}
catch (Exception ex)
{
e = ex;
throw;
}
finally
{
SqlStatistics.StopTimer(statistics);

if (e != null)
{
_diagnosticListener.WriteCommandError(operationId, this, e);
}
else
{
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}
}

Expand Down Expand Up @@ -1286,16 +1342,33 @@ override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
// between entry into Execute* API and the thread obtaining the stateObject.
_pendingCancel = false;

SqlStatistics statistics = null;
Guid operationId = _diagnosticListener.WriteCommandBefore(this);

SqlStatistics statistics = null;

Exception e = null;
try
{
statistics = SqlStatistics.StartTimer(Statistics);
return RunExecuteReader(behavior, RunBehavior.ReturnImmediately, returnStream: true);
}
catch (Exception ex)
{
e = ex;
throw;
}
finally
{
SqlStatistics.StopTimer(statistics);

if (e != null)
{
_diagnosticListener.WriteCommandError(operationId, this, e);
}
else
{
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}
}

Expand Down Expand Up @@ -1438,6 +1511,8 @@ private SqlDataReader InternalEndExecuteReader(IAsyncResult asyncResult, string

public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
{
Guid operationId = _diagnosticListener.WriteCommandBefore(this);

TaskCompletionSource<int> source = new TaskCompletionSource<int>();

CancellationTokenRegistration registration = new CancellationTokenRegistration();
Expand All @@ -1462,6 +1537,7 @@ public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationTok
if (t.IsFaulted)
{
Exception e = t.Exception.InnerException;
_diagnosticListener.WriteCommandError(operationId, this, e);
source.SetException(e);
}
else
Expand All @@ -1474,11 +1550,13 @@ public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationTok
{
source.SetResult(t.Result);
}
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}, TaskScheduler.Default);
}
catch (Exception e)
{
_diagnosticListener.WriteCommandError(operationId, this, e);
source.SetException(e);
}

Expand Down Expand Up @@ -1514,6 +1592,10 @@ protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior b

new public Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
Guid operationId;
if (!_parentOperationStarted)
operationId = _diagnosticListener.WriteCommandBefore(this);

TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>();

CancellationTokenRegistration registration = new CancellationTokenRegistration();
Expand All @@ -1538,6 +1620,8 @@ protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior b
if (t.IsFaulted)
{
Exception e = t.Exception.InnerException;
if (!_parentOperationStarted)
_diagnosticListener.WriteCommandError(operationId, this, e);
source.SetException(e);
}
else
Expand All @@ -1550,11 +1634,16 @@ protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior b
{
source.SetResult(t.Result);
}
if (!_parentOperationStarted)
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}, TaskScheduler.Default);
}
catch (Exception e)
{
if (!_parentOperationStarted)
_diagnosticListener.WriteCommandError(operationId, this, e);

source.SetException(e);
}

Expand All @@ -1563,6 +1652,9 @@ protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior b

public override Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)
{
_parentOperationStarted = true;
Guid operationId = _diagnosticListener.WriteCommandBefore(this);

return ExecuteReaderAsync(cancellationToken).ContinueWith((executeTask) =>
{
TaskCompletionSource<object> source = new TaskCompletionSource<object>();
Expand All @@ -1572,6 +1664,7 @@ public override Task<object> ExecuteScalarAsync(CancellationToken cancellationTo
}
else if (executeTask.IsFaulted)
{
_diagnosticListener.WriteCommandError(operationId, this, executeTask.Exception.InnerException);
source.SetException(executeTask.Exception.InnerException);
}
else
Expand All @@ -1589,6 +1682,7 @@ public override Task<object> ExecuteScalarAsync(CancellationToken cancellationTo
else if (readTask.IsFaulted)
{
reader.Dispose();
_diagnosticListener.WriteCommandError(operationId, this, readTask.Exception.InnerException);
source.SetException(readTask.Exception.InnerException);
}
else
Expand Down Expand Up @@ -1616,10 +1710,12 @@ public override Task<object> ExecuteScalarAsync(CancellationToken cancellationTo
}
if (exception != null)
{
_diagnosticListener.WriteCommandError(operationId, this, exception);
source.SetException(exception);
}
else
{
_diagnosticListener.WriteCommandAfter(operationId, this);
source.SetResult(result);
}
}
Expand All @@ -1631,6 +1727,7 @@ public override Task<object> ExecuteScalarAsync(CancellationToken cancellationTo
}
}, TaskScheduler.Default);
}
_parentOperationStarted = false;
return source.Task;
}, TaskScheduler.Default).Unwrap();
}
Expand All @@ -1642,6 +1739,8 @@ public Task<XmlReader> ExecuteXmlReaderAsync()

public Task<XmlReader> ExecuteXmlReaderAsync(CancellationToken cancellationToken)
{
Guid operationId = _diagnosticListener.WriteCommandBefore(this);

TaskCompletionSource<XmlReader> source = new TaskCompletionSource<XmlReader>();

CancellationTokenRegistration registration = new CancellationTokenRegistration();
Expand All @@ -1666,6 +1765,7 @@ public Task<XmlReader> ExecuteXmlReaderAsync(CancellationToken cancellationToken
if (t.IsFaulted)
{
Exception e = t.Exception.InnerException;
_diagnosticListener.WriteCommandError(operationId, this, e);
source.SetException(e);
}
else
Expand All @@ -1678,11 +1778,13 @@ public Task<XmlReader> ExecuteXmlReaderAsync(CancellationToken cancellationToken
{
source.SetResult(t.Result);
}
_diagnosticListener.WriteCommandAfter(operationId, this);
}
}, TaskScheduler.Default);
}
catch (Exception e)
{
_diagnosticListener.WriteCommandError(operationId, this, e);
source.SetException(e);
}

Expand Down
Loading