Added instrumentation for netfx SqlClient. (#761)
* Added instrumentation for netfx SqlClient. * Code review. * Send "db.statement_type" instead of "db.statementType". * Added integration tests for SqlEventSource. * Adding .github folder to solution. * Added a couple missing files into the solution. * Added FakeSqlEventSource tests. * Fixed up GrpcClientDiagnosticListener for EventSource changes. * Removed extra ActivityListener from unit tests. Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
cdf0cbe397
commit
a48ff18b23
|
|
@ -101,6 +101,38 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentati
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests", "test\OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests\OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj", "{7C4026CA-6434-4762-8B77-D657EAEE1325}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{F1D0972B-38CF-49C2-9F4B-4C5DE02FB71D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\CODEOWNERS = .github\CODEOWNERS
|
||||
.github\PULL_REQUEST_TEMPLATE.md = .github\PULL_REQUEST_TEMPLATE.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{A533C800-3DC3-4D04-90A7-0CE7A1E6BDB3}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md
|
||||
.github\ISSUE_TEMPLATE\feature_request.md = .github\ISSUE_TEMPLATE\feature_request.md
|
||||
.github\ISSUE_TEMPLATE\question.md = .github\ISSUE_TEMPLATE\question.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{E69578EB-B456-4062-A645-877CD964528B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\workflows\dotnet-core-linux.yml = .github\workflows\dotnet-core-linux.yml
|
||||
.github\workflows\dotnet-core-win.yml = .github\workflows\dotnet-core-win.yml
|
||||
.github\workflows\dotnet-core.yml = .github\workflows\dotnet-core.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C1542297-8763-4DF4-957C-489ED771C21D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\Directory.Build.props = src\Directory.Build.props
|
||||
src\Directory.Build.targets = src\Directory.Build.targets
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D2E73927-5966-445C-94E9-EFE6F269C8D5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
test\Directory.Build.props = test\Directory.Build.props
|
||||
test\Directory.Build.targets = test\Directory.Build.targets
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.GrpcClient.Tests", "test\OpenTelemetry.Instrumentation.GrpcClient.Tests\OpenTelemetry.Instrumentation.GrpcClient.Tests.csproj", "{305E9DFD-E73B-4A28-8769-795C25551020}"
|
||||
EndProject
|
||||
Global
|
||||
|
|
@ -243,6 +275,10 @@ Global
|
|||
{47318988-CA8B-4C81-B55D-2FA11D295A49} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E}
|
||||
{25C06046-C7D0-46B4-AAAC-90C50C43DE7A} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E}
|
||||
{9A4E3A68-904B-4835-A3C8-F664B73098DB} = {E359BB2B-9AEC-497D-B321-7DF2450C3B8E}
|
||||
{A533C800-3DC3-4D04-90A7-0CE7A1E6BDB3} = {F1D0972B-38CF-49C2-9F4B-4C5DE02FB71D}
|
||||
{E69578EB-B456-4062-A645-877CD964528B} = {F1D0972B-38CF-49C2-9F4B-4C5DE02FB71D}
|
||||
{C1542297-8763-4DF4-957C-489ED771C21D} = {7CB2F02E-03FA-4FFF-89A5-C51F107623FD}
|
||||
{D2E73927-5966-445C-94E9-EFE6F269C8D5} = {7CB2F02E-03FA-4FFF-89A5-C51F107623FD}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521}
|
||||
|
|
|
|||
|
|
@ -40,9 +40,10 @@ namespace OpenTelemetry.Trace
|
|||
public const string HttpRouteKey = "http.route";
|
||||
public const string HttpFlavorKey = "http.flavor";
|
||||
|
||||
public const string DatabaseTypeKey = "db.type";
|
||||
public const string DatabaseInstanceKey = "db.instance";
|
||||
public const string DatabaseSystemKey = "db.system";
|
||||
public const string DatabaseNameKey = "db.name";
|
||||
public const string DatabaseStatementKey = "db.statement";
|
||||
public const string DatabaseStatementTypeKey = "db.statement_type";
|
||||
|
||||
public const string RpcSystem = "rpc.system";
|
||||
public const string RpcService = "rpc.service";
|
||||
|
|
|
|||
|
|
@ -207,34 +207,34 @@ namespace OpenTelemetry.Trace
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that populates database type
|
||||
/// to https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-database.md.
|
||||
/// Helper method that populates database system
|
||||
/// to https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md.
|
||||
/// </summary>
|
||||
/// <param name="span">Span to fill out.</param>
|
||||
/// <param name="type">Database type.</param>
|
||||
/// <param name="system">Database system.</param>
|
||||
/// <returns>Span with populated properties.</returns>
|
||||
public static TelemetrySpan PutDatabaseTypeAttribute(this TelemetrySpan span, string type)
|
||||
public static TelemetrySpan PutDatabaseSystemAttribute(this TelemetrySpan span, string system)
|
||||
{
|
||||
span.SetAttribute(SpanAttributeConstants.DatabaseTypeKey, type);
|
||||
span.SetAttribute(SpanAttributeConstants.DatabaseSystemKey, system);
|
||||
return span;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that populates database instance
|
||||
/// to https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-database.md.
|
||||
/// Helper method that populates database name
|
||||
/// to https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md.
|
||||
/// </summary>
|
||||
/// <param name="span">Span to fill out.</param>
|
||||
/// <param name="instance">Database instance.</param>
|
||||
/// <param name="name">Database name.</param>
|
||||
/// <returns>Span with populated properties.</returns>
|
||||
public static TelemetrySpan PutDatabaseInstanceAttribute(this TelemetrySpan span, string instance)
|
||||
public static TelemetrySpan PutDatabaseNameAttribute(this TelemetrySpan span, string name)
|
||||
{
|
||||
span.SetAttribute(SpanAttributeConstants.DatabaseInstanceKey, instance);
|
||||
span.SetAttribute(SpanAttributeConstants.DatabaseNameKey, name);
|
||||
return span;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that populates database statement
|
||||
/// to https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-database.md.
|
||||
/// to https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md.
|
||||
/// </summary>
|
||||
/// <param name="span">Span to fill out.</param>
|
||||
/// <param name="statement">Database statement.</param>
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
|
|||
PooledList<JaegerTag>.Add(ref jaegerTags.Tags, new JaegerTag("library.name", JaegerTagType.STRING, vStr: activitySource.Name));
|
||||
if (!string.IsNullOrEmpty(activitySource.Version))
|
||||
{
|
||||
PooledList<JaegerTag>.Add(ref jaegerTags.Tags, new JaegerTag("library.version", JaegerTagType.STRING, vStr: activitySource.Name));
|
||||
PooledList<JaegerTag>.Add(ref jaegerTags.Tags, new JaegerTag("library.version", JaegerTagType.STRING, vStr: activitySource.Version));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,12 +40,10 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
|
||||
public override void OnStartActivity(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnStartActivity";
|
||||
|
||||
var context = HttpContext.Current;
|
||||
if (context == null)
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStartActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -114,8 +112,6 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
|
||||
public override void OnStopActivity(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnStopActivity";
|
||||
|
||||
Activity activityToEnrich = activity;
|
||||
|
||||
if (!(this.options.TextFormat is TraceContextFormatActivity))
|
||||
|
|
@ -139,7 +135,7 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
var context = HttpContext.Current;
|
||||
if (context == null)
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,12 +48,11 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
|
||||
public override void OnStartActivity(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnStartActivity";
|
||||
var context = this.startContextFetcher.Fetch(payload) as HttpContext;
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStartActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -120,13 +119,11 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
|
||||
public override void OnStopActivity(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnStopActivity";
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
if (!(this.stopContextFetcher.Fetch(payload) is HttpContext context))
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpInListener), nameof(this.OnStopActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,10 +41,9 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
public override void OnStartActivity(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnStartActivity";
|
||||
if (!(this.startRequestFetcher.Fetch(payload) is HttpRequestMessage request))
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(GrpcClientDiagnosticListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(GrpcClientDiagnosticListener), nameof(this.OnStartActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,10 +62,9 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
public override void OnStartActivity(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnStartActivity";
|
||||
if (!(this.startRequestFetcher.Fetch(payload) is HttpRequestMessage request))
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerDiagnosticListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerDiagnosticListener), nameof(this.OnStartActivity));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -141,13 +140,11 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
public override void OnException(Activity activity, object payload)
|
||||
{
|
||||
const string EventNameSuffix = ".OnException";
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
if (!(this.stopExceptionFetcher.Fetch(payload) is Exception exc))
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerDiagnosticListener) + EventNameSuffix);
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(HttpHandlerDiagnosticListener), nameof(this.OnException));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ using System;
|
|||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Trace.Samplers;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
||||
{
|
||||
|
|
@ -32,7 +31,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
internal const string SqlDataWriteCommandError = "System.Data.SqlClient.WriteCommandError";
|
||||
internal const string SqlMicrosoftWriteCommandError = "Microsoft.Data.SqlClient.WriteCommandError";
|
||||
|
||||
private const string DatabaseStatementTypeSpanAttributeKey = "db.statementType";
|
||||
internal const string MicrosoftSqlServerDatabaseSystemName = "mssql";
|
||||
|
||||
private readonly PropertyFetcher commandFetcher = new PropertyFetcher("Command");
|
||||
private readonly PropertyFetcher connectionFetcher = new PropertyFetcher("Connection");
|
||||
|
|
@ -66,7 +65,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
|
||||
if (command == null)
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload($"{nameof(SqlClientDiagnosticListener)}-{name}");
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(SqlClientDiagnosticListener), name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -85,17 +84,16 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
var commandText = this.commandTextFetcher.Fetch(command);
|
||||
|
||||
activity.AddTag(SpanAttributeConstants.ComponentKey, "sql");
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseTypeKey, "sql");
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseSystemKey, MicrosoftSqlServerDatabaseSystemName);
|
||||
activity.AddTag(SpanAttributeConstants.PeerServiceKey, (string)dataSource);
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseInstanceKey, (string)database);
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseNameKey, (string)database);
|
||||
|
||||
if (this.commandTypeFetcher.Fetch(command) is CommandType commandType)
|
||||
{
|
||||
activity.AddTag(DatabaseStatementTypeSpanAttributeKey, commandType.ToString());
|
||||
|
||||
switch (commandType)
|
||||
{
|
||||
case CommandType.StoredProcedure:
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementTypeKey, nameof(CommandType.StoredProcedure));
|
||||
if (this.options.CaptureStoredProcedureCommandName)
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, (string)commandText);
|
||||
|
|
@ -104,12 +102,17 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
break;
|
||||
|
||||
case CommandType.Text:
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementTypeKey, nameof(CommandType.Text));
|
||||
if (this.options.CaptureTextCommandContent)
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, (string)commandText);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CommandType.TableDirect:
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementTypeKey, nameof(CommandType.TableDirect));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -136,7 +139,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
|||
}
|
||||
else
|
||||
{
|
||||
InstrumentationEventSource.Log.NullPayload($"{nameof(SqlClientDiagnosticListener)}-{name}");
|
||||
InstrumentationEventSource.Log.NullPayload(nameof(SqlClientDiagnosticListener), name);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
// <copyright file="SqlEventSourceListener.netfx.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
#if NETFRAMEWORK
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Tracing;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// .NET Framework SqlClient doesn't emit DiagnosticSource events.
|
||||
/// We hook into its EventSource if it is available:
|
||||
/// See: <a href="https://github.com/microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/System.Data/System/Data/Common/SqlEventSource.cs#L29">reference source</a>.
|
||||
/// </summary>
|
||||
internal class SqlEventSourceListener : EventListener
|
||||
{
|
||||
internal const string ActivitySourceName = "System.Data.SqlClient";
|
||||
internal const string ActivityName = ActivitySourceName + ".Execute";
|
||||
internal const string AdoNetEventSourceName = "Microsoft-AdoNet-SystemData";
|
||||
internal const int BeginExecuteEventId = 1;
|
||||
internal const int EndExecuteEventId = 2;
|
||||
|
||||
private static readonly Version Version = typeof(SqlEventSourceListener).Assembly.GetName().Version;
|
||||
private static readonly ActivitySource SqlClientActivitySource = new ActivitySource(ActivitySourceName, Version.ToString());
|
||||
|
||||
private readonly SqlClientInstrumentationOptions options;
|
||||
private EventSource eventSource;
|
||||
|
||||
public SqlEventSourceListener(SqlClientInstrumentationOptions options = null)
|
||||
{
|
||||
this.options = options ?? new SqlClientInstrumentationOptions();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (this.eventSource != null)
|
||||
{
|
||||
this.DisableEvents(this.eventSource);
|
||||
}
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
protected override void OnEventSourceCreated(EventSource eventSource)
|
||||
{
|
||||
if (eventSource?.Name.StartsWith(AdoNetEventSourceName) == true)
|
||||
{
|
||||
this.eventSource = eventSource;
|
||||
this.EnableEvents(eventSource, EventLevel.Informational, (EventKeywords)1);
|
||||
}
|
||||
|
||||
base.OnEventSourceCreated(eventSource);
|
||||
}
|
||||
|
||||
protected override void OnEventWritten(EventWrittenEventArgs eventData)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (eventData.EventId == BeginExecuteEventId)
|
||||
{
|
||||
this.OnBeginExecute(eventData);
|
||||
}
|
||||
else if (eventData.EventId == EndExecuteEventId)
|
||||
{
|
||||
this.OnEndExecute(eventData);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
InstrumentationEventSource.Log.UnknownErrorProcessingEvent(nameof(SqlEventSourceListener), nameof(this.OnEventWritten), exc);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeginExecute(EventWrittenEventArgs eventData)
|
||||
{
|
||||
/*
|
||||
Expected payload:
|
||||
[0] -> ObjectId
|
||||
[1] -> DataSource
|
||||
[2] -> Database
|
||||
[3] -> CommandText ([3] = CommandType == CommandType.StoredProcedure ? CommandText : string.Empty)
|
||||
*/
|
||||
|
||||
if ((eventData?.Payload?.Count ?? 0) < 4)
|
||||
{
|
||||
InstrumentationEventSource.Log.InvalidPayload(nameof(SqlEventSourceListener), nameof(this.OnBeginExecute));
|
||||
return;
|
||||
}
|
||||
|
||||
var activity = SqlClientActivitySource.StartActivity(ActivityName, ActivityKind.Client);
|
||||
if (activity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string databaseName = (string)eventData.Payload[2];
|
||||
|
||||
activity.DisplayName = databaseName;
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.ComponentKey, "sql");
|
||||
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseSystemKey, SqlClientDiagnosticListener.MicrosoftSqlServerDatabaseSystemName);
|
||||
activity.AddTag(SpanAttributeConstants.PeerServiceKey, (string)eventData.Payload[1]);
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseNameKey, databaseName);
|
||||
|
||||
string commandText = (string)eventData.Payload[3];
|
||||
if (string.IsNullOrEmpty(commandText))
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementTypeKey, nameof(CommandType.Text));
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementTypeKey, nameof(CommandType.StoredProcedure));
|
||||
if (this.options.CaptureStoredProcedureCommandName)
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, commandText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEndExecute(EventWrittenEventArgs eventData)
|
||||
{
|
||||
/*
|
||||
Expected payload:
|
||||
[0] -> ObjectId
|
||||
[1] -> CompositeState bitmask (0b001 -> successFlag, 0b010 -> isSqlExceptionFlag , 0b100 -> synchronousFlag)
|
||||
[2] -> SqlExceptionNumber
|
||||
*/
|
||||
|
||||
if ((eventData?.Payload?.Count ?? 0) < 3)
|
||||
{
|
||||
InstrumentationEventSource.Log.InvalidPayload(nameof(SqlEventSourceListener), nameof(this.OnEndExecute));
|
||||
return;
|
||||
}
|
||||
|
||||
var activity = Activity.Current;
|
||||
if (activity?.Source != SqlClientActivitySource)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
int compositeState = (int)eventData.Payload[1];
|
||||
if ((compositeState & 0b001) == 0b001)
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(StatusCanonicalCode.Ok));
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(StatusCanonicalCode.Unknown));
|
||||
if ((compositeState & 0b010) == 0b010)
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, $"SqlExceptionNumber {eventData.Payload[2]} thrown.");
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, $"Unknown Sql failure.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
activity.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -135,6 +135,9 @@ namespace OpenTelemetry.Trace.Configuration
|
|||
configureSqlClientInstrumentationOptions?.Invoke(sqlOptions);
|
||||
|
||||
builder.AddInstrumentation((activitySource) => new SqlClientInstrumentation(activitySource, sqlOptions));
|
||||
#if NETFRAMEWORK
|
||||
builder.AddActivitySource(SqlEventSourceListener.ActivitySourceName);
|
||||
#endif
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ namespace OpenTelemetry.Instrumentation.Dependencies
|
|||
internal const string SqlClientDiagnosticListenerName = "SqlClientDiagnosticListener";
|
||||
|
||||
private readonly DiagnosticSourceSubscriber diagnosticSourceSubscriber;
|
||||
#if NETFRAMEWORK
|
||||
private readonly SqlEventSourceListener sqlEventSourceListener;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SqlClientInstrumentation"/> class.
|
||||
|
|
@ -49,12 +52,19 @@ namespace OpenTelemetry.Instrumentation.Dependencies
|
|||
listener => listener.Name == SqlClientDiagnosticListenerName,
|
||||
null);
|
||||
this.diagnosticSourceSubscriber.Subscribe();
|
||||
|
||||
#if NETFRAMEWORK
|
||||
this.sqlEventSourceListener = new SqlEventSourceListener(options);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.diagnosticSourceSubscriber?.Dispose();
|
||||
#if NETFRAMEWORK
|
||||
this.sqlEventSourceListener?.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,16 +67,16 @@ namespace OpenTelemetry.Instrumentation
|
|||
this.UnknownErrorProcessingEvent(handlerName, eventName, ToInvariantString(ex));
|
||||
}
|
||||
|
||||
[Event(4, Message = "Unknown error processing event '{0}' from handler '{1}', Exception: {2}", Level = EventLevel.Error)]
|
||||
[Event(4, Message = "Unknown error processing event '{1}' from handler '{0}', Exception: {2}", Level = EventLevel.Error)]
|
||||
public void UnknownErrorProcessingEvent(string handlerName, string eventName, string ex)
|
||||
{
|
||||
this.WriteEvent(4, handlerName, eventName, ex);
|
||||
}
|
||||
|
||||
[Event(5, Message = "Payload is NULL in '{0}' callback. Span will not be recorded.", Level = EventLevel.Warning)]
|
||||
public void NullPayload(string eventName)
|
||||
[Event(5, Message = "Payload is NULL in event '{1}' from handler '{0}', span will not be recorded.", Level = EventLevel.Warning)]
|
||||
public void NullPayload(string handlerName, string eventName)
|
||||
{
|
||||
this.WriteEvent(5, eventName);
|
||||
this.WriteEvent(5, handlerName, eventName);
|
||||
}
|
||||
|
||||
[Event(6, Message = "Request is filtered out.", Level = EventLevel.Verbose)]
|
||||
|
|
@ -100,6 +100,12 @@ namespace OpenTelemetry.Instrumentation
|
|||
this.WriteEvent(7, instrumentationType, ex);
|
||||
}
|
||||
|
||||
[Event(8, Message = "Payload is invalid in event '{1}' from handler '{0}', span will not be recorded.", Level = EventLevel.Warning)]
|
||||
public void InvalidPayload(string handlerName, string eventName)
|
||||
{
|
||||
this.WriteEvent(8, handlerName, eventName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a culture-independent string representation of the given <paramref name="exception"/> object,
|
||||
/// appropriate for diagnostics tracing.
|
||||
|
|
|
|||
|
|
@ -165,16 +165,8 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
|||
|
||||
request.Headers.Add("traceparent", "00-0123456789abcdef0123456789abcdef-0123456789abcdef-01");
|
||||
|
||||
using (var activityListener = new ActivityListener
|
||||
{
|
||||
ShouldListenTo = (activitySource) => activitySource.Name == HttpWebRequestActivitySource.ActivitySourceName,
|
||||
})
|
||||
{
|
||||
ActivitySource.AddActivityListener(activityListener);
|
||||
|
||||
using var c = new HttpClient();
|
||||
await c.SendAsync(request);
|
||||
}
|
||||
using var c = new HttpClient();
|
||||
await c.SendAsync(request);
|
||||
|
||||
Assert.Equal(0, activityProcessor.Invocations.Count);
|
||||
}
|
||||
|
|
@ -193,16 +185,8 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
|||
arg1 is HttpWebRequest request &&
|
||||
request.RequestUri.OriginalString.Contains(this.url)));
|
||||
|
||||
using (var activityListener = new ActivityListener
|
||||
{
|
||||
ShouldListenTo = (activitySource) => activitySource.Name == HttpWebRequestActivitySource.ActivitySourceName,
|
||||
})
|
||||
{
|
||||
ActivitySource.AddActivityListener(activityListener);
|
||||
|
||||
using var c = new HttpClient();
|
||||
await c.GetAsync(this.url);
|
||||
}
|
||||
using var c = new HttpClient();
|
||||
await c.GetAsync(this.url);
|
||||
|
||||
Assert.Equal(0, spanProcessor.Invocations.Count);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// <copyright file="SkipUnlessEnvVarFoundTheoryAttribute.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
||||
{
|
||||
public class SkipUnlessEnvVarFoundTheoryAttribute : TheoryAttribute
|
||||
{
|
||||
public SkipUnlessEnvVarFoundTheoryAttribute(string environmentVariable)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(environmentVariable, EnvironmentVariableTarget.Machine)))
|
||||
{
|
||||
this.Skip = $"Skipped because {environmentVariable} environment variable was not configured.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,8 +117,8 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
|||
Assert.Null(span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.StatusDescriptionKey).Value);
|
||||
|
||||
Assert.Equal("sql", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.ComponentKey).Value);
|
||||
Assert.Equal("sql", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseTypeKey).Value);
|
||||
Assert.Equal("master", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseInstanceKey).Value);
|
||||
Assert.Equal(SqlClientDiagnosticListener.MicrosoftSqlServerDatabaseSystemName, span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseSystemKey).Value);
|
||||
Assert.Equal("master", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseNameKey).Value);
|
||||
|
||||
switch (commandType)
|
||||
{
|
||||
|
|
@ -205,8 +205,8 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
|||
Assert.Equal("Unknown", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.StatusCodeKey).Value);
|
||||
Assert.Equal("Boom!", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.StatusDescriptionKey).Value);
|
||||
Assert.Equal("sql", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.ComponentKey).Value);
|
||||
Assert.Equal("sql", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseTypeKey).Value);
|
||||
Assert.Equal("master", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseInstanceKey).Value);
|
||||
Assert.Equal(SqlClientDiagnosticListener.MicrosoftSqlServerDatabaseSystemName, span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseSystemKey).Value);
|
||||
Assert.Equal("master", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseNameKey).Value);
|
||||
Assert.Equal("SP_GetOrders", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.DatabaseStatementKey).Value);
|
||||
Assert.Equal("(localdb)\\MSSQLLocalDB", span.Tags.FirstOrDefault(i => i.Key == SpanAttributeConstants.PeerServiceKey).Value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,244 @@
|
|||
// <copyright file="SqlEventSourceTests.netfx.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
#if NETFRAMEWORK
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Tracing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using OpenTelemetry.Instrumentation.Dependencies.Implementation;
|
||||
using OpenTelemetry.Trace;
|
||||
using OpenTelemetry.Trace.Configuration;
|
||||
using OpenTelemetry.Trace.Export;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.Dependencies.Tests
|
||||
{
|
||||
public class SqlEventSourceTests
|
||||
{
|
||||
/*
|
||||
To run the integration tests, set the ot.SqlConnectionString machine-level environment variable to a valid Sql Server connection string.
|
||||
|
||||
To use Docker...
|
||||
1) Run: docker run -d --name sql2019 -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Pass@word" -p 5433:1433 mcr.microsoft.com/mssql/server:2019-latest
|
||||
2) Set ot.SqlConnectionString as: Data Source=127.0.0.1,5433; User ID=sa; Password=Pass@word
|
||||
*/
|
||||
|
||||
private const string SqlConnectionStringEnvVarName = "ot.SqlConnectionString";
|
||||
private static readonly string SqlConnectionString = Environment.GetEnvironmentVariable(SqlConnectionStringEnvVarName, EnvironmentVariableTarget.Machine);
|
||||
|
||||
[Trait("CategoryName", "SqlIntegrationTests")]
|
||||
[SkipUnlessEnvVarFoundTheory(SqlConnectionStringEnvVarName)]
|
||||
[InlineData(CommandType.Text, "select 1/1", false)]
|
||||
[InlineData(CommandType.Text, "select 1/0", false, true)]
|
||||
[InlineData(CommandType.StoredProcedure, "sp_who", false)]
|
||||
[InlineData(CommandType.StoredProcedure, "sp_who", true)]
|
||||
public async Task SuccessfulCommandTest(CommandType commandType, string commandText, bool captureText, bool isFailure = false)
|
||||
{
|
||||
var activityProcessor = new Mock<ActivityProcessor>();
|
||||
using var shutdownSignal = OpenTelemetrySdk.EnableOpenTelemetry(b =>
|
||||
{
|
||||
b.AddProcessorPipeline(c => c.AddProcessor(ap => activityProcessor.Object));
|
||||
b.AddSqlClientDependencyInstrumentation(options =>
|
||||
{
|
||||
options.CaptureStoredProcedureCommandName = captureText;
|
||||
});
|
||||
});
|
||||
|
||||
using SqlConnection sqlConnection = new SqlConnection(SqlConnectionString);
|
||||
|
||||
await sqlConnection.OpenAsync().ConfigureAwait(false);
|
||||
|
||||
string dataSource = sqlConnection.DataSource;
|
||||
|
||||
sqlConnection.ChangeDatabase("master");
|
||||
|
||||
using SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection)
|
||||
{
|
||||
CommandType = commandType,
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await sqlCommand.ExecuteNonQueryAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
Assert.Equal(2, activityProcessor.Invocations.Count);
|
||||
|
||||
var activity = (Activity)activityProcessor.Invocations[1].Arguments[0];
|
||||
|
||||
VerifyActivityData(commandType, commandText, captureText, isFailure, dataSource, activity);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(CommandType.Text, "select 1/1", false)]
|
||||
[InlineData(CommandType.Text, "select 1/0", false, true)]
|
||||
[InlineData(CommandType.StoredProcedure, "sp_who", false)]
|
||||
[InlineData(CommandType.StoredProcedure, "sp_who", true)]
|
||||
public void EventSourceFakeTests(CommandType commandType, string commandText, bool captureText, bool isFailure = false, int sqlExceptionNumber = 0)
|
||||
{
|
||||
using FakeBehavingSqlEventSource fakeSqlEventSource = new FakeBehavingSqlEventSource();
|
||||
|
||||
var activityProcessor = new Mock<ActivityProcessor>();
|
||||
using var shutdownSignal = OpenTelemetrySdk.EnableOpenTelemetry(b =>
|
||||
{
|
||||
b.AddProcessorPipeline(c => c.AddProcessor(ap => activityProcessor.Object));
|
||||
b.AddSqlClientDependencyInstrumentation(options =>
|
||||
{
|
||||
options.CaptureStoredProcedureCommandName = captureText;
|
||||
});
|
||||
});
|
||||
|
||||
int objectId = Guid.NewGuid().GetHashCode();
|
||||
|
||||
fakeSqlEventSource.WriteBeginExecuteEvent(objectId, "127.0.0.1", "master", commandType == CommandType.StoredProcedure ? commandText : string.Empty);
|
||||
|
||||
// success is stored in the first bit in compositeState 0b001
|
||||
int successFlag = !isFailure ? 1 : 0;
|
||||
|
||||
// isSqlException is stored in the second bit in compositeState 0b010
|
||||
int isSqlExceptionFlag = sqlExceptionNumber > 0 ? 2 : 0;
|
||||
|
||||
// synchronous state is stored in the third bit in compositeState 0b100
|
||||
int synchronousFlag = false ? 4 : 0;
|
||||
|
||||
int compositeState = successFlag | isSqlExceptionFlag | synchronousFlag;
|
||||
|
||||
fakeSqlEventSource.WriteEndExecuteEvent(objectId, compositeState, sqlExceptionNumber);
|
||||
|
||||
Assert.Equal(2, activityProcessor.Invocations.Count);
|
||||
|
||||
var activity = (Activity)activityProcessor.Invocations[1].Arguments[0];
|
||||
|
||||
VerifyActivityData(commandType, commandText, captureText, isFailure, "127.0.0.1", activity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EventSourceFakeUnknownEventWithNullPayloadTest()
|
||||
{
|
||||
using FakeMisbehavingSqlEventSource fakeSqlEventSource = new FakeMisbehavingSqlEventSource();
|
||||
|
||||
var activityProcessor = new Mock<ActivityProcessor>();
|
||||
using var shutdownSignal = OpenTelemetrySdk.EnableOpenTelemetry(b =>
|
||||
{
|
||||
b.AddProcessorPipeline(c => c.AddProcessor(ap => activityProcessor.Object));
|
||||
b.AddSqlClientDependencyInstrumentation();
|
||||
});
|
||||
|
||||
fakeSqlEventSource.WriteUnknownEventWithNullPayload();
|
||||
|
||||
Assert.Equal(0, activityProcessor.Invocations.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EventSourceFakeInvalidPayloadTest()
|
||||
{
|
||||
using FakeMisbehavingSqlEventSource fakeSqlEventSource = new FakeMisbehavingSqlEventSource();
|
||||
|
||||
var activityProcessor = new Mock<ActivityProcessor>();
|
||||
using var shutdownSignal = OpenTelemetrySdk.EnableOpenTelemetry(b =>
|
||||
{
|
||||
b.AddProcessorPipeline(c => c.AddProcessor(ap => activityProcessor.Object));
|
||||
b.AddSqlClientDependencyInstrumentation();
|
||||
});
|
||||
|
||||
fakeSqlEventSource.WriteBeginExecuteEvent("arg1");
|
||||
|
||||
fakeSqlEventSource.WriteEndExecuteEvent("arg1", "arg2", "arg3", "arg4");
|
||||
|
||||
Assert.Equal(0, activityProcessor.Invocations.Count);
|
||||
}
|
||||
|
||||
private static void VerifyActivityData(CommandType commandType, string commandText, bool captureText, bool isFailure, string dataSource, Activity activity)
|
||||
{
|
||||
Assert.Equal("master", activity.DisplayName);
|
||||
Assert.Equal(ActivityKind.Client, activity.Kind);
|
||||
Assert.Equal("sql", activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.ComponentKey).Value);
|
||||
Assert.Equal(SqlClientDiagnosticListener.MicrosoftSqlServerDatabaseSystemName, activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.DatabaseSystemKey).Value);
|
||||
Assert.Equal(dataSource, activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.PeerServiceKey).Value);
|
||||
Assert.Equal("master", activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.DatabaseNameKey).Value);
|
||||
Assert.Equal(commandType.ToString(), activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.DatabaseStatementTypeKey).Value);
|
||||
if (commandType == CommandType.StoredProcedure)
|
||||
{
|
||||
if (captureText)
|
||||
{
|
||||
Assert.Equal(commandText, activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.DatabaseStatementKey).Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.DoesNotContain(activity.Tags, t => t.Key == SpanAttributeConstants.DatabaseStatementKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFailure)
|
||||
{
|
||||
Assert.Equal("Ok", activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal("Unknown", activity.Tags.FirstOrDefault(t => t.Key == SpanAttributeConstants.StatusCodeKey).Value);
|
||||
Assert.Contains(activity.Tags, t => t.Key == SpanAttributeConstants.StatusDescriptionKey);
|
||||
}
|
||||
}
|
||||
|
||||
[EventSource(Name = SqlEventSourceListener.AdoNetEventSourceName + "-FakeFriendly")]
|
||||
private class FakeBehavingSqlEventSource : EventSource
|
||||
{
|
||||
[Event(SqlEventSourceListener.BeginExecuteEventId)]
|
||||
public void WriteBeginExecuteEvent(int objectId, string dataSource, string databaseName, string commandText)
|
||||
{
|
||||
this.WriteEvent(SqlEventSourceListener.BeginExecuteEventId, objectId, dataSource, databaseName, commandText);
|
||||
}
|
||||
|
||||
[Event(SqlEventSourceListener.EndExecuteEventId)]
|
||||
public void WriteEndExecuteEvent(int objectId, int compositeState, int sqlExceptionNumber)
|
||||
{
|
||||
this.WriteEvent(SqlEventSourceListener.EndExecuteEventId, objectId, compositeState, sqlExceptionNumber);
|
||||
}
|
||||
}
|
||||
|
||||
[EventSource(Name = SqlEventSourceListener.AdoNetEventSourceName + "-FakeEvil")]
|
||||
private class FakeMisbehavingSqlEventSource : EventSource
|
||||
{
|
||||
[Event(SqlEventSourceListener.BeginExecuteEventId)]
|
||||
public void WriteBeginExecuteEvent(string arg1)
|
||||
{
|
||||
this.WriteEvent(SqlEventSourceListener.BeginExecuteEventId, arg1);
|
||||
}
|
||||
|
||||
[Event(SqlEventSourceListener.EndExecuteEventId)]
|
||||
public void WriteEndExecuteEvent(string arg1, string arg2, string arg3, string arg4)
|
||||
{
|
||||
this.WriteEvent(SqlEventSourceListener.EndExecuteEventId, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
[Event(3)]
|
||||
public void WriteUnknownEventWithNullPayload()
|
||||
{
|
||||
object[] args = null;
|
||||
|
||||
this.WriteEvent(3, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Loading…
Reference in New Issue