Skip to content
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

SqlClient instrumentation to support Microsoft.Data.SqlClient EventSource #1599

Merged
merged 17 commits into from
Nov 24, 2020

Conversation

mbakalov
Copy link
Contributor

@mbakalov mbakalov commented Nov 20, 2020

Fixes #1597.

Changes

  • SqlClientInstrumentationOptions now has different public API on .NET Core and .NET Framework
  • Made SqlClientDiagnosticListener .NET Core-only (it was referencing the options properties no longer available on .NET Framework)
    • For that had to refactor some common properties into a new SqlActivitySourceHelper
    • DiagnosticListener is no longer created/used under .NET Framework
  • On .NET Framework the new SetStatementText is false by default
  • SqlEventSourceListener is now listening to both event sources ("Microsoft-AdoNet-SystemData" and "Microsoft.Data.SqlClient.EventSource")
  • Updated Microsoft.Data.SqlClient NuGet in the Test project from 1.1.1 to 2.1.0

Background

Recently, Microsoft.Data.SqlClient renamed their EventSource from "Microsoft-AdoNet-SystemData" to "Microsoft.Data.SqlClient.EventSource". The System.Data.SqlClient still uses the old event source, so the instrumentation needs to listen to both event sources.

TODO:

  • unit-tests, including "fake event source" ones to have a 2nd fake source
  • behavior change: new EventSource reports CommandText not only for StoredProcedures now but for Text statements as well
  • comments and README updates
  • CHANGELOG.md updated for non-trivial changes
    * [ ] Changes in public API reviewed - shouldn't be any public API changes
  • Changes in public API reviewed - there are public API changes for SetStatementText

@codecov
Copy link

codecov bot commented Nov 20, 2020

Codecov Report

Merging #1599 (6511d3b) into master (04f35ef) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #1599   +/-   ##
=======================================
  Coverage   80.56%   80.56%           
=======================================
  Files         242      243    +1     
  Lines        6582     6582           
=======================================
  Hits         5303     5303           
  Misses       1279     1279           
Impacted Files Coverage Δ
...ation.SqlClient/SqlClientInstrumentationOptions.cs 97.95% <ø> (ø)
...qlClient/Implementation/SqlActivitySourceHelper.cs 100.00% <100.00%> (ø)
...ient/Implementation/SqlClientDiagnosticListener.cs 75.75% <100.00%> (-0.72%) ⬇️
...trumentation.SqlClient/SqlClientInstrumentation.cs 100.00% <100.00%> (ø)
...ation.SqlClient/TracerProviderBuilderExtensions.cs 100.00% <100.00%> (ø)

Copy link
Member

@cijothomas cijothomas left a comment

Choose a reason for hiding this comment

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

LGTM. Please add an entry in chagelog.md.
We need to update tests to use mds as well rigth?

@mbakalov
Copy link
Contributor Author

LGTM. Please add an entry in chagelog.md.
We need to update tests to use mds as well rigth?

@cijothomas - thanks, yes, working on that still, will push later today. The semantics of the new EventSource are different a bit too (it now captures CommandText always, not just for SPs) so I want to make sure that is supported properly as well.

@mbakalov
Copy link
Contributor Author

Actually, annoyingly that behavior is different enough that it is going to result in confusing API.

System.Data.SqlClient basically does this:

private void WriteBeginExecuteEvent()
{
    if (SqlEventSource.Log.IsEnabled() && Connection != null)
    {
        string commandText = CommandType == CommandType.StoredProcedure ? CommandText : string.Empty;
        SqlEventSource.Log.BeginExecute(GetHashCode(), Connection.DataSource, Connection.Database, commandText);
    }
}

While MDS does this:

private void WriteBeginExecuteEvent()
{
    if (Connection != null)
    {
        SqlClientEventSource.Log.BeginExecute(GetHashCode(), Connection.DataSource, Connection.Database, CommandText);
    }
}

So MDS always reports CommandText which is great, but now in our listener we cannot tell if we are dealing with an SP or a Text statement. Previously we could say "CommandText is non-empty so it has to be an SP", now we can't.

This becomes confusing because the "options" has two separate settings for those: SetTextCommandContent and SetStoredProcedureCommandName (currently SetTextCommandContent does nothing on System.Data.SqlClient netfx).

To clean this up, I am suggesting the following in the SqlClientInstrumentationOptions API, basically have only one toggle that does both (but only on netfx). Any thoughts?

        // .NET Framework implementation uses SqlEventSource from which we can't reliably distinguish
        // StoredProcedures from regular Text sql commands.
#if NETFRAMEWORK

        /// <summary>
        /// Gets or sets a value indicating whether or not the <see cref="SqlClientInstrumentation"/> should
        /// add the text of the executed Sql commands as the <see cref="SemanticConventions.AttributeDbStatement"/> tag.
        /// Default value: False.
        /// </summary>
        public bool SetStatementText { get; set; }
#else
        /// <summary>
        /// Gets or sets a value indicating whether or not the <see cref="SqlClientInstrumentation"/> should add the names of <see cref="CommandType.StoredProcedure"/> commands as the <see cref="SemanticConventions.AttributeDbStatement"/> tag. Default value: True.
        /// </summary>
        public bool SetStoredProcedureCommandName { get; set; } = true;

        /// <summary>
        /// Gets or sets a value indicating whether or not the <see cref="SqlClientInstrumentation"/> should add the text of <see cref="CommandType.Text"/> commands as the <see cref="SemanticConventions.AttributeDbStatement"/> tag. Default value: False.
        /// </summary>
        public bool SetTextCommandContent { get; set; }
#endif

@mbakalov
Copy link
Contributor Author

Alternatively, the instrumentation can set db.statement on netfx MDS only if both SetStoredProcedureCommandName and SetTextCommandContent are set to true. Less code and less ifdefs in the codebase (especially tests), but might be more confusing to users?

@cijothomas
Copy link
Member

Alternatively, the instrumentation can set db.statement on netfx MDS only if both SetStoredProcedureCommandName and SetTextCommandContent are set to true. Less code and less ifdefs in the codebase (especially tests), but might be more confusing to users?

Can we instead use separate listeners for MDS and SDS? Then listener know exactly if its getting sp or sqltext.
Sorry haven't fully read the code. but an initial thought.

@mbakalov
Copy link
Contributor Author

Alternatively, the instrumentation can set db.statement on netfx MDS only if both SetStoredProcedureCommandName and SetTextCommandContent are set to true. Less code and less ifdefs in the codebase (especially tests), but might be more confusing to users?

Can we instead use separate listeners for MDS and SDS? Then listener know exactly if its getting sp or sqltext.
Sorry haven't fully read the code. but an initial thought.

Yeah that is the issue I think. Even if MDS was a separate listener, it just gets a CommandText and at that point has no way of telling if it is sp or sqltext.

@CodeBlanch
Copy link
Member

@mbakalov Bummer about the change to command text by MS! I like your idea to change the options.

Separate SqlClientInstrumentationOptions for netcore and netfx.
Upgrade MDS nuget to the newer version that actually uses the new
EventSource name.
Don't reference the diagnostic listener from the sql even source code.
@mbakalov
Copy link
Contributor Author

mbakalov commented Nov 21, 2020

Introduced a separate SetStatementText property just on .NET Framework (had to refactor code just a bit to get there) - please take a look.

It turned out that the reason net461 tests were failing was only somewhat related to the renaming of the EventSource. The test project was referencing MDS nuget version 1.1.1, which actually still uses the old EventSource name :), so the tests should have worked. And they mostly did, but were flaky for a reason similar to what was being discussed in elastic/apm-agent-dotnet#704 (comment).

When running both System.Data.SqlClient and Microsoft.Data.SqlClient tests in the same session, the OnEventSourceCreated was firing multiple types during a single test, overwriting the eventSource we already enabled and causing issues. Running just System.Data.SqlClient or just Microsoft.Data.SqlClient tests under net461 has always worked ok.

I upgraded the MDS nuget to the latest version (2.1.0) and now with the support of the new EventSource all tests pass.

I did a CHANGELOG.md update and a few minor comment clarifications and fixes. Still need to add a few more new tests and update README.

@mbakalov mbakalov marked this pull request as ready for review November 21, 2020 18:32
@mbakalov mbakalov requested a review from a team November 21, 2020 18:32
@mbakalov
Copy link
Contributor Author

mbakalov commented Nov 21, 2020

Highlights of changes:

  • SqlClientInstrumentationOptions now has different public API on .NET Core and .NET Framework
  • Made SqlClientDiagnosticListener .NET Core-only (it was referencing the options properties no longer available on .NET Framework)
    • For that had to refactor some common properties into a new SqlActivitySourceHelper
    • DiagnosticListener is no longer created/used under .NET Framework
  • On .NET Framework the new SetStatementText is true by default and will capture both sp and sqltext if available. Not sure if good idea or not. now false by default (EDIT).
  • SqlEventSourceListener is now listening to both event sources
  • Updated Microsoft.Data.SqlClient NuGet in the Test project from 1.1.1 to 2.1.0

@cijothomas
Copy link
Member

@mbakalov Thanks a lot!
LGTM overall. except we need to keep default as false. Given this bug fix requires API change, its okay to be merged before 1.0.0. We need 2 approvals as we are close to 1.0.0. @CodeBlanch - please share your review.

ALso, open to any other suggestion for the names for these:
.NET Fx
SetStatementText

.NET core
SetStoredProcedureCommandName
SetTextCommandContent

@mbakalov
Copy link
Contributor Author

Thanks @cijothomas, I wasn't sure about the default, will update to false!

@mbakalov
Copy link
Contributor Author

As for the property names, maybe:

  • netfx
    • SetCommandText
  • netcore
    • SetStoredProcedureCommandText
    • SetTextCommandText

@mbakalov
Copy link
Contributor Author

Changed the default to false. Added more comments and warnings just in case. Let me know if need to update/reword anything there to make more clear.

@cijothomas
Copy link
Member

For namings, alternate proposal:

netfx
SetDbStatement
netcore
SetDbStatementForStoredProcedure
SetDbStatementForText

"Sets DBStatement semantic convention" can be the general comment for all, as its coming directly from spec. Then specifically clarify SetDbStatement could be SPName or Text. SetDbStatementForStoredProcedure and SetDbStatementForText can be similarly clarified.

public const string ActivitySourceName = "OpenTelemetry.SqlClient";
public const string ActivityName = ActivitySourceName + ".Execute";

public const string MicrosoftSqlServerDatabaseSystemName = "mssql";
Copy link
Member

Choose a reason for hiding this comment

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

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, I'll update this! Other things ("db.statement") are being pulled from semantic conventions so better to be consistent.

Copy link
Member

@CodeBlanch CodeBlanch left a comment

Choose a reason for hiding this comment

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

Left a couple of comments, but LGTM.

@cijothomas
Copy link
Member

Good to merge as has enough approvals, and no blocking comments. We just need to see if any better names are possible for the options.

@cijothomas cijothomas merged commit 9e26699 into open-telemetry:master Nov 24, 2020
@mbakalov mbakalov deleted the mds-netfx branch February 26, 2021 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SqlClient instrumentation doesn't work for Microsoft.Data.SqlClient under .NET Framework
3 participants