Skip to content

Latest commit

 

History

History
276 lines (220 loc) · 10.9 KB

File metadata and controls

276 lines (220 loc) · 10.9 KB

SqlClient Instrumentation for OpenTelemetry

NuGet NuGet

This is an Instrumentation Library, which instruments Microsoft.Data.SqlClient and System.Data.SqlClient and collects traces about database operations.

Warning Instrumentation is not working with Microsoft.Data.SqlClient v3.* due to the issue. It was fixed in 4.0 and later.

Note This component is based on the OpenTelemetry semantic conventions for traces. These conventions are Experimental, and hence, this package is a pre-release. Until a stable version is released, there can be breaking changes. You can track the progress from milestones.

Steps to enable OpenTelemetry.Instrumentation.SqlClient

Step 1: Install Package

Add a reference to the OpenTelemetry.Instrumentation.SqlClient package. Also, add any other instrumentations & exporters you will need.

dotnet add package --prerelease OpenTelemetry.Instrumentation.SqlClient

Step 2: Enable SqlClient Instrumentation at application startup

SqlClient instrumentation must be enabled at application startup.

The following example demonstrates adding SqlClient instrumentation to a console application. This example also sets up the OpenTelemetry Console exporter, which requires adding the package OpenTelemetry.Exporter.Console to the application.

using OpenTelemetry.Trace;

public class Program
{
    public static void Main(string[] args)
    {
        using var tracerProvider = Sdk.CreateTracerProviderBuilder()
            .AddSqlClientInstrumentation()
            .AddConsoleExporter()
            .Build();
    }
}

For an ASP.NET Core application, adding instrumentation is typically done in the ConfigureServices of your Startup class. Refer to documentation for OpenTelemetry.Instrumentation.AspNetCore.

For an ASP.NET application, adding instrumentation is typically done in the Global.asax.cs. Refer to the documentation for OpenTelemetry.Instrumentation.AspNet.

Advanced configuration

This instrumentation can be configured to change the default behavior by using SqlClientInstrumentationOptions.

Capturing database statements

The SqlClientInstrumentationOptions class exposes two properties that can be used to configure how the db.statement attribute is captured upon execution of a query but the behavior depends on the runtime used.

.NET and .NET Core

On .NET and .NET Core, two properties are available: SetDbStatementForStoredProcedure and SetDbStatementForText. These properties control capturing of CommandType.StoredProcedure and CommandType.Text respectively.

SetDbStatementForStoredProcedure is true by default and will set db.statement attribute to the stored procedure command name.

SetDbStatementForText is false by default (to prevent accidental capture of sensitive data that might be part of the SQL statement text). When set to true, the instrumentation will set db.statement attribute to the text of the SQL command being executed.

To disable capturing stored procedure commands use configuration like below.

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSqlClientInstrumentation(
        options => options.SetDbStatementForStoredProcedure = false)
    .AddConsoleExporter()
    .Build();

To enable capturing of sqlCommand.CommandText for CommandType.Text use the following configuration.

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSqlClientInstrumentation(
        options => options.SetDbStatementForText = true)
    .AddConsoleExporter()
    .Build();

.NET Framework

On .NET Framework, the SetDbStatementForText property controls whether or not this instrumentation will set the db.statement attribute to the text of the SqlCommand being executed. This could either be the name of a stored procedure (when CommandType.StoredProcedure is used) or the full text of a CommandType.Text query. SetDbStatementForStoredProcedure is ignored because on .NET Framework there is no way to determine the type of command being executed.

Since CommandType.Text might contain sensitive data, all SQL capturing is disabled by default to protect against accidentally sending full query text to a telemetry backend. If you are only using stored procedures or have no sensitive data in your sqlCommand.CommandText, you can enable SQL capturing using the options like below:

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSqlClientInstrumentation(
        options => options.SetDbStatementForText = true)
    .AddConsoleExporter()
    .Build();

Note When using the built-in System.Data.SqlClient only stored procedure command names will ever be captured. When using the Microsoft.Data.SqlClient NuGet package (v1.1+) stored procedure command names, full query text, and other command text will be captured.

EnableConnectionLevelAttributes

Note EnableConnectionLevelAttributes is supported on all runtimes.

By default, EnabledConnectionLevelAttributes is disabled and this instrumentation sets the peer.service attribute to the DataSource property of the connection. If EnabledConnectionLevelAttributes is enabled, the DataSource will be parsed and the server name will be sent as the net.peer.name or net.peer.ip attribute, the instance name will be sent as the db.mssql.instance_name attribute, and the port will be sent as the net.peer.port attribute if it is not 1433 (the default port).

The following example shows how to use EnableConnectionLevelAttributes.

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSqlClientInstrumentation(
        options => options.EnableConnectionLevelAttributes = true)
    .AddConsoleExporter()
    .Build();

Enrich

Note Enrich is supported on .NET and .NET Core runtimes only.

This option can be used to enrich the activity with additional information from the raw SqlCommand object. The Enrich action is called only when activity.IsAllDataRequested is true. It contains the activity itself (which can be enriched), the name of the event, and the actual raw object.

Currently there is only one event name reported, "OnCustom". The actual object is Microsoft.Data.SqlClient.SqlCommand for Microsoft.Data.SqlClient and System.Data.SqlClient.SqlCommand for System.Data.SqlClient.

The following code snippet shows how to add additional tags using Enrich.

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSqlClientInstrumentation(opt => opt.Enrich
        = (activity, eventName, rawObject) =>
    {
        if (eventName.Equals("OnCustom"))
        {
            if (rawObject is SqlCommand cmd)
            {
                activity.SetTag("db.commandTimeout", cmd.CommandTimeout);
            }
        };
    })
    .Build();

Processor, is the general extensibility point to add additional properties to any activity. The Enrich option is specific to this instrumentation, and is provided to get access to SqlCommand object.

RecordException

Note RecordException is supported on .NET and .NET Core runtimes only.

This option can be set to instruct the instrumentation to record SqlExceptions as Activity events.

The default value is false and can be changed by the code like below.

using var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSqlClientInstrumentation(
        options => options.RecordException = true)
    .AddConsoleExporter()
    .Build();

Filter

Note Filter is supported on .NET and .NET Core runtimes only.

This option can be used to filter out activities based on the properties of the SqlCommand object being instrumented using a Func<object, bool>. The function receives an instance of the raw SqlCommand and should return true if the telemetry is to be collected, and false if it should not. The parameter of the Func delegate is of type object and needs to be cast to the appropriate type of SqlCommand, either Microsoft.Data.SqlClient.SqlCommand or System.Data.SqlClient.SqlCommand. The example below filters out all commands that are not stored procedures.

using var traceProvider = Sdk.CreateTracerProviderBuilder()
   .AddSqlClientInstrumentation(
       opt =>
       {
           opt.Filter = cmd =>
           {
               if (cmd is SqlCommand command)
               {
                   return command.CommandType == CommandType.StoredProcedure;
               }

               return false;
           };
       })
   .AddConsoleExporter()
   .Build();
{

References