From 215add0ce8d995c5afc3cbc38305ce7d49bf6a8f Mon Sep 17 00:00:00 2001 From: Chao Date: Tue, 8 Jul 2025 19:49:39 +0800 Subject: [PATCH 01/12] [Instrumentation.SqlClient] Introducing ContextPropagationLevel opinion to propagate trace info to SQL Server --- .../.publicApi/PublicAPI.Unshipped.txt | 2 + .../CHANGELOG.md | 8 ++++ .../SqlClientDiagnosticListener.cs | 48 +++++++++++++++++++ .../SqlClientTraceInstrumentationOptions.cs | 22 +++++++++ .../SqlClientIntegrationTests.cs | 1 + 5 files changed, 81 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt index 3cd16fd49a..ee732f91fc 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt @@ -9,6 +9,8 @@ OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.Rec OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SetDbStatementForText.get -> bool OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SetDbStatementForText.set -> void OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SqlClientTraceInstrumentationOptions() -> void +OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.ContextPropagationLevel.get -> string! +OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.ContextPropagationLevel.set -> void OpenTelemetry.Metrics.SqlClientMeterProviderBuilderExtensions OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Metrics.SqlClientMeterProviderBuilderExtensions.AddSqlClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder! diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 6405ed6b83..76680d0e44 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -10,6 +10,14 @@ the new conventions. ([#2811](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2811)) +* Added `ContextPropagationLevel` option to propagate trace context to + SQL Server database. + ([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709)) + + Valid options: + * `trace`: Propagate traceparent info to SQL Server database (see [SET CONTEXT_INFO](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-context-info-transact-sql?view=sql-server-ver16)) + * `disabled`: Default value + ## 1.12.0-beta.1 Released 2025-May-06 diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs index 5907810dff..dade9b0214 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; #endif using System.Globalization; +using System.Text; using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.SqlClient.Implementation; @@ -26,6 +27,9 @@ internal sealed class SqlClientDiagnosticListener : ListenerHandler public const string SqlDataWriteCommandError = "System.Data.SqlClient.WriteCommandError"; public const string SqlMicrosoftWriteCommandError = "Microsoft.Data.SqlClient.WriteCommandError"; + private const string ContextInfoParameterName = "@opentelemetry_traceparent"; + private const string SetContextSql = $"set context_info {ContextInfoParameterName}"; + private readonly PropertyFetcher commandFetcher = new("Command"); private readonly PropertyFetcher connectionFetcher = new("Connection"); private readonly PropertyFetcher dataSourceFetcher = new("DataSource"); @@ -64,6 +68,13 @@ public override void OnEventWritten(string name, object? payload) return; } + // skip if this is an injected query + if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) + { + return; + } + _ = this.connectionFetcher.TryFetch(command, out var connection); _ = this.databaseFetcher.TryFetch(connection, out var databaseName); _ = this.dataSourceFetcher.TryFetch(connection, out var dataSource); @@ -82,6 +93,25 @@ public override void OnEventWritten(string name, object? payload) return; } + if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + command is IDbCommand { CommandType: CommandType.Text, Connection.State: ConnectionState.Open } iDbCommand) + { + var setContextCommand = iDbCommand.Connection.CreateCommand(); + setContextCommand.CommandText = SetContextSql; + setContextCommand.CommandType = CommandType.Text; + var parameter = setContextCommand.CreateParameter(); + parameter.ParameterName = ContextInfoParameterName; + + var tracedflags = (activity.ActivityTraceFlags & ActivityTraceFlags.Recorded) != 0 ? "01" : "00"; + var traceparent = $"00-{activity.TraceId.ToHexString()}-{activity.SpanId.ToHexString()}-{tracedflags}"; + + parameter.DbType = DbType.Binary; + parameter.Value = Encoding.UTF8.GetBytes(traceparent); + setContextCommand.Parameters.Add(parameter); + + setContextCommand.ExecuteNonQuery(); + } + if (activity.IsAllDataRequested) { try @@ -168,6 +198,15 @@ public override void OnEventWritten(string name, object? payload) case SqlDataAfterExecuteCommand: case SqlMicrosoftAfterExecuteCommand: { + _ = this.commandFetcher.TryFetch(payload, out var command); + + // skip if this is an injected query + if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) + { + return; + } + if (activity == null) { SqlClientInstrumentationEventSource.Log.NullActivity(name); @@ -189,6 +228,15 @@ public override void OnEventWritten(string name, object? payload) case SqlDataWriteCommandError: case SqlMicrosoftWriteCommandError: { + _ = this.commandFetcher.TryFetch(payload, out var command); + + // skip if this is an injected query + if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) + { + return; + } + if (activity == null) { SqlClientInstrumentationEventSource.Log.NullActivity(name); diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs index 06bee628eb..705ebd1e01 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs @@ -17,6 +17,16 @@ namespace OpenTelemetry.Instrumentation.SqlClient; /// public class SqlClientTraceInstrumentationOptions { + /// + /// Flag to send traceparent information to SQL Server. + /// + internal const string ContextPropagationLevelTrace = "trace"; + + /// + /// Flag to disable sending trace information to SQL Server. + /// + internal const string ContextPropagationDisabled = "disabled"; + /// /// Initializes a new instance of the class. /// @@ -30,6 +40,7 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) var databaseSemanticConvention = GetSemanticConventionOptIn(configuration); this.EmitOldAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.Old); this.EmitNewAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.New); + this.ContextPropagationLevel = ContextPropagationDisabled; } /// @@ -63,6 +74,17 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) /// public bool SetDbStatementForText { get; set; } + /// + /// Gets or sets a value indicating whether to send trace information to SQL Server database. + /// Optional values: + /// : + /// Send traceparent information to SQL Server. + /// : + /// Disable sending trace information to SQL Server. + /// Default value: . + /// + public string ContextPropagationLevel { get; set; } + /// /// Gets or sets an action to enrich an with the /// raw SqlCommand object. diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index ca7af4d7f3..caf121871d 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -56,6 +56,7 @@ public void SuccessfulCommandTest( { options.SetDbStatementForText = captureTextCommandContent; options.RecordException = recordException; + options.ContextPropagationLevel = SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace; }) .Build(); From 235f1aafe2fc5e7ee1ad5be6c2c5d19e0505abf7 Mon Sep 17 00:00:00 2001 From: Chao Date: Tue, 8 Jul 2025 20:57:02 +0800 Subject: [PATCH 02/12] Add integration test --- .../SqlClientIntegrationTests.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index caf121871d..923afac791 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -3,6 +3,7 @@ using System.Data; using System.Diagnostics; +using System.Text; using Microsoft.Data.SqlClient; using OpenTelemetry.Instrumentation.SqlClient.Implementation; using OpenTelemetry.Tests; @@ -28,6 +29,7 @@ public SqlClientIntegrationTests(SqlClientIntegrationTestsFixture fixture) [InlineData(CommandType.Text, "select 1/1", true, "select ?/?")] [InlineData(CommandType.Text, "select 1/0", false, null, true)] [InlineData(CommandType.Text, "select 1/0", false, null, true, true)] + [InlineData(CommandType.Text, "SELECT CONTEXT_INFO()")] #if NETFRAMEWORK [InlineData(CommandType.StoredProcedure, "sp_who", false, null)] #else @@ -73,10 +75,10 @@ public void SuccessfulCommandTest( { CommandType = commandType, }; - + object commandResult = DBNull.Value; try { - sqlCommand.ExecuteNonQuery(); + commandResult = sqlCommand.ExecuteScalar(); } catch { @@ -85,6 +87,7 @@ public void SuccessfulCommandTest( Assert.Single(activities); var activity = activities[0]; + VerifyContextInfo(commandText, commandResult, activity); VerifyActivityData(commandType, sanitizedCommandText, captureTextCommandContent, isFailure, recordException, activity); VerifySamplingParameters(sampler.LatestSamplingParameters); @@ -104,6 +107,20 @@ public void SuccessfulCommandTest( } } + private static void VerifyContextInfo( + string? commandText, + object commandResult, + Activity activity) + { + if (commandText == "SELECT CONTEXT_INFO()") + { + Assert.NotEqual(commandResult, DBNull.Value); + Assert.True(commandResult is byte[]); + var contextInfo = Encoding.ASCII.GetString((byte[])commandResult).TrimEnd('\0'); + Assert.Equal(contextInfo, activity.Id); + } + } + private static void VerifyActivityData( CommandType commandType, string? commandText, From 63da26a9385f35bad5c476b5d05aaae2c1c0c0ad Mon Sep 17 00:00:00 2001 From: Chao Date: Wed, 9 Jul 2025 13:10:05 +0800 Subject: [PATCH 03/12] feedback --- .../.publicApi/PublicAPI.Unshipped.txt | 2 -- ...Telemetry.Instrumentation.SqlClient.csproj | 1 + .../SqlClientTraceInstrumentationOptions.cs | 32 ++++++++++++------- .../SqlClientIntegrationTests.cs | 12 +++++-- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt index ee732f91fc..3cd16fd49a 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt @@ -9,8 +9,6 @@ OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.Rec OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SetDbStatementForText.get -> bool OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SetDbStatementForText.set -> void OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SqlClientTraceInstrumentationOptions() -> void -OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.ContextPropagationLevel.get -> string! -OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.ContextPropagationLevel.set -> void OpenTelemetry.Metrics.SqlClientMeterProviderBuilderExtensions OpenTelemetry.Trace.TracerProviderBuilderExtensions static OpenTelemetry.Metrics.SqlClientMeterProviderBuilderExtensions.AddSqlClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder! diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/OpenTelemetry.Instrumentation.SqlClient.csproj b/src/OpenTelemetry.Instrumentation.SqlClient/OpenTelemetry.Instrumentation.SqlClient.csproj index 945e11b8c5..21057224e1 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/OpenTelemetry.Instrumentation.SqlClient.csproj +++ b/src/OpenTelemetry.Instrumentation.SqlClient/OpenTelemetry.Instrumentation.SqlClient.csproj @@ -16,6 +16,7 @@ + diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs index 705ebd1e01..c628715d17 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs @@ -17,6 +17,8 @@ namespace OpenTelemetry.Instrumentation.SqlClient; /// public class SqlClientTraceInstrumentationOptions { + internal const string ContextPropagationLevelEnvVar = "OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_CONTEXT_PROPAGATION_LEVEL"; + /// /// Flag to send traceparent information to SQL Server. /// @@ -41,6 +43,14 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) this.EmitOldAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.Old); this.EmitNewAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.New); this.ContextPropagationLevel = ContextPropagationDisabled; + + Debug.Assert(configuration != null, "configuration was null"); + + if (configuration!.TryGetStringValue(ContextPropagationLevelEnvVar, out var contextPropagationLevel) + && contextPropagationLevel == ContextPropagationLevelTrace) + { + this.ContextPropagationLevel = contextPropagationLevel; + } } /// @@ -74,17 +84,6 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) /// public bool SetDbStatementForText { get; set; } - /// - /// Gets or sets a value indicating whether to send trace information to SQL Server database. - /// Optional values: - /// : - /// Send traceparent information to SQL Server. - /// : - /// Disable sending trace information to SQL Server. - /// Default value: . - /// - public string ContextPropagationLevel { get; set; } - /// /// Gets or sets an action to enrich an with the /// raw SqlCommand object. @@ -148,4 +147,15 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) /// Gets or sets a value indicating whether the new database attributes should be emitted. /// internal bool EmitNewAttributes { get; set; } + + /// + /// Gets or sets a value indicating whether to send trace information to SQL Server database. + /// Optional values: + /// : + /// Send traceparent information to SQL Server. + /// : + /// Disable sending trace information to SQL Server. + /// Default value: . + /// + internal string ContextPropagationLevel { get; set; } } diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index 923afac791..51d7d1a22a 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -17,6 +17,8 @@ namespace OpenTelemetry.Instrumentation.SqlClient.Tests; [Trait("CategoryName", "SqlIntegrationTests")] public sealed class SqlClientIntegrationTests : IClassFixture { + private const string GetContextInfoQuery = "SELECT CONTEXT_INFO()"; + private readonly SqlClientIntegrationTestsFixture fixture; public SqlClientIntegrationTests(SqlClientIntegrationTestsFixture fixture) @@ -29,7 +31,7 @@ public SqlClientIntegrationTests(SqlClientIntegrationTestsFixture fixture) [InlineData(CommandType.Text, "select 1/1", true, "select ?/?")] [InlineData(CommandType.Text, "select 1/0", false, null, true)] [InlineData(CommandType.Text, "select 1/0", false, null, true, true)] - [InlineData(CommandType.Text, "SELECT CONTEXT_INFO()")] + [InlineData(CommandType.Text, GetContextInfoQuery)] #if NETFRAMEWORK [InlineData(CommandType.StoredProcedure, "sp_who", false, null)] #else @@ -44,6 +46,11 @@ public void SuccessfulCommandTest( bool isFailure = false, bool recordException = false) { + if (commandText == GetContextInfoQuery) + { + Environment.SetEnvironmentVariable(SqlClientTraceInstrumentationOptions.ContextPropagationLevelEnvVar, SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace); + } + #if NETFRAMEWORK // Disable things not available on netfx recordException = false; @@ -58,7 +65,6 @@ public void SuccessfulCommandTest( { options.SetDbStatementForText = captureTextCommandContent; options.RecordException = recordException; - options.ContextPropagationLevel = SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace; }) .Build(); @@ -112,7 +118,7 @@ private static void VerifyContextInfo( object commandResult, Activity activity) { - if (commandText == "SELECT CONTEXT_INFO()") + if (commandText == GetContextInfoQuery) { Assert.NotEqual(commandResult, DBNull.Value); Assert.True(commandResult is byte[]); From 8ce3f0212d1ba31ea6a9a5985a02357690468ec6 Mon Sep 17 00:00:00 2001 From: Chao Date: Wed, 9 Jul 2025 19:23:45 +0800 Subject: [PATCH 04/12] doc --- .../CHANGELOG.md | 12 +++++++----- .../README.md | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 76680d0e44..5ba46860c4 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -10,13 +10,15 @@ the new conventions. ([#2811](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2811)) -* Added `ContextPropagationLevel` option to propagate trace context to - SQL Server database. +* Added `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_CONTEXT_PROPAGATION_LEVEL` + environment variable to propagate trace context to SQL Server databases. ([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709)) - Valid options: - * `trace`: Propagate traceparent info to SQL Server database (see [SET CONTEXT_INFO](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-context-info-transact-sql?view=sql-server-ver16)) - * `disabled`: Default value + Valid options for this environment variable are: + * `trace`: Propagate `traceparent` information to SQL Server databases + (see [SET CONTEXT_INFO](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-context-info-transact-sql?view=sql-server-ver16)). + Note that this option incurs an additional round-trip to the database. + * `disabled`: No context propagation performed, default value. ## 1.12.0-beta.1 diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/README.md b/src/OpenTelemetry.Instrumentation.SqlClient/README.md index 616f85a5d0..54cd078601 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/README.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/README.md @@ -281,6 +281,21 @@ using var traceProvider = Sdk.CreateTracerProviderBuilder() { ``` +### Trace Context Propagation + +> [!NOTE] +> Only `CommandType.Text` commands are supported for context propagation. + +Database context propagation can be enabled by setting +`OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_CONTEXT_PROPAGATION_LEVEL` environment variable. +The valid values are: + +* `disabled` - No context propagation is performed. This is the default value. +* `trace` - Enables sending trace information to databases + in [W3C traceparent text format](https://www.w3.org/TR/trace-context/#traceparent-header). + This uses the `SET CONTEXT_INFO` command to set trace context in SQL Server, + which results in **an additional round-trip to the database**. + ## References * [OpenTelemetry Project](https://opentelemetry.io/) From 1033417fad51e86e381ec9b8f564c1c1b8adf4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Thu, 10 Jul 2025 13:04:09 +0200 Subject: [PATCH 05/12] Update src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs Co-authored-by: Zach Montoya --- .../Implementation/SqlClientDiagnosticListener.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs index dade9b0214..c9e87db090 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs @@ -97,6 +97,7 @@ public override void OnEventWritten(string name, object? payload) command is IDbCommand { CommandType: CommandType.Text, Connection.State: ConnectionState.Open } iDbCommand) { var setContextCommand = iDbCommand.Connection.CreateCommand(); + setContextCommand.Transaction = iDbCommand.Transaction; setContextCommand.CommandText = SetContextSql; setContextCommand.CommandType = CommandType.Text; var parameter = setContextCommand.CreateParameter(); From 909cc3634c03e88f3eabd23a564027a9c7115f3c Mon Sep 17 00:00:00 2001 From: Chao Date: Thu, 10 Jul 2025 20:05:05 +0800 Subject: [PATCH 06/12] feedback --- .../CHANGELOG.md | 6 +-- .../SqlClientDiagnosticListener.cs | 8 ++-- .../SqlClientInstrumentationEventSource.cs | 14 ++++++- .../README.md | 20 +++++----- .../SqlClientTraceInstrumentationOptions.cs | 39 ++++++++----------- .../SqlClientIntegrationTests.cs | 18 +++++++-- 6 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 5ba46860c4..3cf7373a01 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -10,15 +10,13 @@ the new conventions. ([#2811](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2811)) -* Added `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_CONTEXT_PROPAGATION_LEVEL` +* Added `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` environment variable to propagate trace context to SQL Server databases. ([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709)) - Valid options for this environment variable are: - * `trace`: Propagate `traceparent` information to SQL Server databases + > Propagate `traceparent` information to SQL Server databases (see [SET CONTEXT_INFO](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-context-info-transact-sql?view=sql-server-ver16)). Note that this option incurs an additional round-trip to the database. - * `disabled`: No context propagation performed, default value. ## 1.12.0-beta.1 diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs index c9e87db090..d0e644e45b 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs @@ -69,7 +69,7 @@ public override void OnEventWritten(string name, object? payload) } // skip if this is an injected query - if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) { return; @@ -93,7 +93,7 @@ public override void OnEventWritten(string name, object? payload) return; } - if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, Connection.State: ConnectionState.Open } iDbCommand) { var setContextCommand = iDbCommand.Connection.CreateCommand(); @@ -202,7 +202,7 @@ public override void OnEventWritten(string name, object? payload) _ = this.commandFetcher.TryFetch(payload, out var command); // skip if this is an injected query - if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) { return; @@ -232,7 +232,7 @@ public override void OnEventWritten(string name, object? payload) _ = this.commandFetcher.TryFetch(payload, out var command); // skip if this is an injected query - if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace && + if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) { return; diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientInstrumentationEventSource.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientInstrumentationEventSource.cs index ccd86471d7..086e2103f1 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientInstrumentationEventSource.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientInstrumentationEventSource.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics.Tracing; +using Microsoft.Extensions.Configuration; using OpenTelemetry.Internal; namespace OpenTelemetry.Instrumentation.SqlClient.Implementation; @@ -10,7 +11,7 @@ namespace OpenTelemetry.Instrumentation.SqlClient.Implementation; /// EventSource events emitted from the project. /// [EventSource(Name = "OpenTelemetry-Instrumentation-SqlClient")] -internal sealed class SqlClientInstrumentationEventSource : EventSource +internal sealed class SqlClientInstrumentationEventSource : EventSource, IConfigurationExtensionsLogger { public static SqlClientInstrumentationEventSource Log = new(); @@ -82,4 +83,15 @@ public void CommandFilterException(string exception) { this.WriteEvent(7, exception); } + + [Event(8, Message = "Configuration key '{0}' has an invalid value: '{1}'", Level = EventLevel.Warning)] + public void InvalidConfigurationValue(string key, string value) + { + this.WriteEvent(8, key, value); + } + + void IConfigurationExtensionsLogger.LogInvalidConfigurationValue(string key, string value) + { + this.InvalidConfigurationValue(key, value); + } } diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/README.md b/src/OpenTelemetry.Instrumentation.SqlClient/README.md index 54cd078601..59527b69c3 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/README.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/README.md @@ -284,17 +284,15 @@ using var traceProvider = Sdk.CreateTracerProviderBuilder() ### Trace Context Propagation > [!NOTE] -> Only `CommandType.Text` commands are supported for context propagation. - -Database context propagation can be enabled by setting -`OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_CONTEXT_PROPAGATION_LEVEL` environment variable. -The valid values are: - -* `disabled` - No context propagation is performed. This is the default value. -* `trace` - Enables sending trace information to databases - in [W3C traceparent text format](https://www.w3.org/TR/trace-context/#traceparent-header). - This uses the `SET CONTEXT_INFO` command to set trace context in SQL Server, - which results in **an additional round-trip to the database**. +> Only `CommandType.Text` commands are supported for trace context propagation. + +Database trace context propagation can be enabled by setting +`OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` +environment variable to `true`. +This uses the [SET CONTEXT_INFO](https://learn.microsoft.com/en-us/sql/t-sql/statements/set-context-info-transact-sql?view=sql-server-ver16) +command to set [traceparent](https://www.w3.org/TR/trace-context/#traceparent-header)information +for the current connection, which results in +**an additional round-trip to the database**. ## References diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs index c628715d17..98c4c0a295 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs @@ -4,6 +4,7 @@ using System.Data; using System.Diagnostics; using Microsoft.Extensions.Configuration; +using OpenTelemetry.Instrumentation.SqlClient.Implementation; using OpenTelemetry.Trace; using static OpenTelemetry.Internal.DatabaseSemanticConventionHelper; @@ -17,17 +18,7 @@ namespace OpenTelemetry.Instrumentation.SqlClient; /// public class SqlClientTraceInstrumentationOptions { - internal const string ContextPropagationLevelEnvVar = "OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_CONTEXT_PROPAGATION_LEVEL"; - - /// - /// Flag to send traceparent information to SQL Server. - /// - internal const string ContextPropagationLevelTrace = "trace"; - - /// - /// Flag to disable sending trace information to SQL Server. - /// - internal const string ContextPropagationDisabled = "disabled"; + internal const string ContextPropagationLevelEnvVar = "OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION"; /// /// Initializes a new instance of the class. @@ -42,14 +33,15 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) var databaseSemanticConvention = GetSemanticConventionOptIn(configuration); this.EmitOldAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.Old); this.EmitNewAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.New); - this.ContextPropagationLevel = ContextPropagationDisabled; Debug.Assert(configuration != null, "configuration was null"); - if (configuration!.TryGetStringValue(ContextPropagationLevelEnvVar, out var contextPropagationLevel) - && contextPropagationLevel == ContextPropagationLevelTrace) + if (configuration!.TryGetBoolValue( + SqlClientInstrumentationEventSource.Log, + ContextPropagationLevelEnvVar, + out var enableTraceContextPropagation)) { - this.ContextPropagationLevel = contextPropagationLevel; + this.EnableTraceContextPropagation = enableTraceContextPropagation; } } @@ -149,13 +141,14 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) internal bool EmitNewAttributes { get; set; } /// - /// Gets or sets a value indicating whether to send trace information to SQL Server database. - /// Optional values: - /// : - /// Send traceparent information to SQL Server. - /// : - /// Disable sending trace information to SQL Server. - /// Default value: . + /// Gets or sets a value indicating whether to send traceparent information to SQL Server database. /// - internal string ContextPropagationLevel { get; set; } + /// + /// + /// Only `CommandType.Text` commands are supported for trace context propagation. + /// Note: This uses the SET CONTEXT_INFO command to set traceparent information + /// for the current connection, which results in an additional round-trip to the database. + /// + /// + internal bool EnableTraceContextPropagation { get; set; } } diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index 51d7d1a22a..c1c503197b 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -31,7 +31,8 @@ public SqlClientIntegrationTests(SqlClientIntegrationTestsFixture fixture) [InlineData(CommandType.Text, "select 1/1", true, "select ?/?")] [InlineData(CommandType.Text, "select 1/0", false, null, true)] [InlineData(CommandType.Text, "select 1/0", false, null, true, true)] - [InlineData(CommandType.Text, GetContextInfoQuery)] + [InlineData(CommandType.Text, GetContextInfoQuery, false, null, false, false, false)] + [InlineData(CommandType.Text, GetContextInfoQuery, false, null, false, false, true)] #if NETFRAMEWORK [InlineData(CommandType.StoredProcedure, "sp_who", false, null)] #else @@ -44,11 +45,12 @@ public void SuccessfulCommandTest( bool captureTextCommandContent = false, string? sanitizedCommandText = null, bool isFailure = false, - bool recordException = false) + bool recordException = false, + bool enableTransaction = false) { if (commandText == GetContextInfoQuery) { - Environment.SetEnvironmentVariable(SqlClientTraceInstrumentationOptions.ContextPropagationLevelEnvVar, SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace); + Environment.SetEnvironmentVariable(SqlClientTraceInstrumentationOptions.ContextPropagationLevelEnvVar, "true"); } #if NETFRAMEWORK @@ -75,12 +77,20 @@ public void SuccessfulCommandTest( var dataSource = sqlConnection.DataSource; sqlConnection.ChangeDatabase("master"); + SqlTransaction transaction = null; #pragma warning disable CA2100 using var sqlCommand = new SqlCommand(commandText, sqlConnection) #pragma warning restore CA2100 { CommandType = commandType, }; + + if (enableTransaction) + { + transaction = sqlConnection.BeginTransaction(); + sqlCommand.Transaction = transaction; + } + object commandResult = DBNull.Value; try { @@ -90,6 +100,8 @@ public void SuccessfulCommandTest( { } + transaction?.Commit(); + Assert.Single(activities); var activity = activities[0]; From 03f3e0e995a8449d9bbe0bc3f34574f35ed2aa85 Mon Sep 17 00:00:00 2001 From: Chao Date: Thu, 10 Jul 2025 20:08:53 +0800 Subject: [PATCH 07/12] fix --- .../SqlClientIntegrationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index c1c503197b..c738e7bec6 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -77,7 +77,7 @@ public void SuccessfulCommandTest( var dataSource = sqlConnection.DataSource; sqlConnection.ChangeDatabase("master"); - SqlTransaction transaction = null; + SqlTransaction? transaction = null; #pragma warning disable CA2100 using var sqlCommand = new SqlCommand(commandText, sqlConnection) #pragma warning restore CA2100 From 56615db39051731694fb83008070fe681ceaa898 Mon Sep 17 00:00:00 2001 From: Will Li Date: Sat, 12 Jul 2025 04:12:24 +0000 Subject: [PATCH 08/12] disable context info check in netframework --- .../SqlClientIntegrationTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index c738e7bec6..1b8ec9cfd6 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -105,7 +105,9 @@ public void SuccessfulCommandTest( Assert.Single(activities); var activity = activities[0]; +#if !NETFRAMEWORK VerifyContextInfo(commandText, commandResult, activity); +#endif VerifyActivityData(commandType, sanitizedCommandText, captureTextCommandContent, isFailure, recordException, activity); VerifySamplingParameters(sampler.LatestSamplingParameters); From bab319e714862d8c7dda63b13455a4d56d4fa8b0 Mon Sep 17 00:00:00 2001 From: Will Li Date: Mon, 14 Jul 2025 07:22:42 +0000 Subject: [PATCH 09/12] update changelog and test --- src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md | 1 + .../SqlClientIntegrationTests.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 3cf7373a01..77b0bdddff 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -12,6 +12,7 @@ * Added `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` environment variable to propagate trace context to SQL Server databases. + It is now only available on .NET Core. ([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709)) > Propagate `traceparent` information to SQL Server databases diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index 1b8ec9cfd6..e2fa30c53d 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -31,8 +31,10 @@ public SqlClientIntegrationTests(SqlClientIntegrationTestsFixture fixture) [InlineData(CommandType.Text, "select 1/1", true, "select ?/?")] [InlineData(CommandType.Text, "select 1/0", false, null, true)] [InlineData(CommandType.Text, "select 1/0", false, null, true, true)] +#if NET [InlineData(CommandType.Text, GetContextInfoQuery, false, null, false, false, false)] [InlineData(CommandType.Text, GetContextInfoQuery, false, null, false, false, true)] +#endif #if NETFRAMEWORK [InlineData(CommandType.StoredProcedure, "sp_who", false, null)] #else @@ -105,9 +107,7 @@ public void SuccessfulCommandTest( Assert.Single(activities); var activity = activities[0]; -#if !NETFRAMEWORK VerifyContextInfo(commandText, commandResult, activity); -#endif VerifyActivityData(commandType, sanitizedCommandText, captureTextCommandContent, isFailure, recordException, activity); VerifySamplingParameters(sampler.LatestSamplingParameters); From 98696b3ac4f8c1db2754481d048d78a71fd69b6c Mon Sep 17 00:00:00 2001 From: Will Li Date: Mon, 14 Jul 2025 07:32:19 +0000 Subject: [PATCH 10/12] update readme --- src/OpenTelemetry.Instrumentation.SqlClient/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/README.md b/src/OpenTelemetry.Instrumentation.SqlClient/README.md index 59527b69c3..a276eca685 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/README.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/README.md @@ -285,6 +285,7 @@ using var traceProvider = Sdk.CreateTracerProviderBuilder() > [!NOTE] > Only `CommandType.Text` commands are supported for trace context propagation. +> Only .NET and .NET Core runtimes are supported. Database trace context propagation can be enabled by setting `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` From 2c78274ae388d4b004c0282dee0bf99b90f527ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= Date: Mon, 14 Jul 2025 10:19:25 +0200 Subject: [PATCH 11/12] direct feedback to PR --- .../CHANGELOG.md | 2 +- .../Implementation/SqlClientDiagnosticListener.cs | 12 ++++++++++++ .../README.md | 2 +- .../SqlClientTraceInstrumentationOptions.cs | 6 ++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 77b0bdddff..d1cd80ad9a 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -12,7 +12,7 @@ * Added `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` environment variable to propagate trace context to SQL Server databases. - It is now only available on .NET Core. + It is now only available on .NET 8 and newer. ([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709)) > Propagate `traceparent` information to SQL Server databases diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs index d0e644e45b..1b0f69753c 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs @@ -8,7 +8,9 @@ using System.Diagnostics.CodeAnalysis; #endif using System.Globalization; +#if NET using System.Text; +#endif using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.SqlClient.Implementation; @@ -27,8 +29,10 @@ internal sealed class SqlClientDiagnosticListener : ListenerHandler public const string SqlDataWriteCommandError = "System.Data.SqlClient.WriteCommandError"; public const string SqlMicrosoftWriteCommandError = "Microsoft.Data.SqlClient.WriteCommandError"; +#if NET private const string ContextInfoParameterName = "@opentelemetry_traceparent"; private const string SetContextSql = $"set context_info {ContextInfoParameterName}"; +#endif private readonly PropertyFetcher commandFetcher = new("Command"); private readonly PropertyFetcher connectionFetcher = new("Connection"); @@ -68,12 +72,14 @@ public override void OnEventWritten(string name, object? payload) return; } +#if NET // skip if this is an injected query if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) { return; } +#endif _ = this.connectionFetcher.TryFetch(command, out var connection); _ = this.databaseFetcher.TryFetch(connection, out var databaseName); @@ -93,6 +99,7 @@ public override void OnEventWritten(string name, object? payload) return; } +#if NET if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, Connection.State: ConnectionState.Open } iDbCommand) { @@ -112,6 +119,7 @@ public override void OnEventWritten(string name, object? payload) setContextCommand.ExecuteNonQuery(); } +#endif if (activity.IsAllDataRequested) { @@ -201,12 +209,14 @@ public override void OnEventWritten(string name, object? payload) { _ = this.commandFetcher.TryFetch(payload, out var command); +#if NET // skip if this is an injected query if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) { return; } +#endif if (activity == null) { @@ -231,12 +241,14 @@ public override void OnEventWritten(string name, object? payload) { _ = this.commandFetcher.TryFetch(payload, out var command); +#if NET // skip if this is an injected query if (options.EnableTraceContextPropagation && command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql }) { return; } +#endif if (activity == null) { diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/README.md b/src/OpenTelemetry.Instrumentation.SqlClient/README.md index a276eca685..63219f3491 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/README.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/README.md @@ -285,7 +285,7 @@ using var traceProvider = Sdk.CreateTracerProviderBuilder() > [!NOTE] > Only `CommandType.Text` commands are supported for trace context propagation. -> Only .NET and .NET Core runtimes are supported. +> Only .NET runtimes are supported. Database trace context propagation can be enabled by setting `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs index 98c4c0a295..eed484d1bf 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs @@ -4,7 +4,9 @@ using System.Data; using System.Diagnostics; using Microsoft.Extensions.Configuration; +#if NET using OpenTelemetry.Instrumentation.SqlClient.Implementation; +#endif using OpenTelemetry.Trace; using static OpenTelemetry.Internal.DatabaseSemanticConventionHelper; @@ -34,6 +36,7 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) this.EmitOldAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.Old); this.EmitNewAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.New); +#if NET Debug.Assert(configuration != null, "configuration was null"); if (configuration!.TryGetBoolValue( @@ -43,6 +46,7 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) { this.EnableTraceContextPropagation = enableTraceContextPropagation; } +#endif } /// @@ -140,6 +144,7 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) /// internal bool EmitNewAttributes { get; set; } +#if NET /// /// Gets or sets a value indicating whether to send traceparent information to SQL Server database. /// @@ -151,4 +156,5 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration) /// /// internal bool EnableTraceContextPropagation { get; set; } +#endif } From 197a99ccd53b79e39daedd3757f8bc726132d476 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:32:41 -0700 Subject: [PATCH 12/12] Update src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md --- src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index d1cd80ad9a..858ae1cab7 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -12,6 +12,8 @@ * Added `OTEL_DOTNET_EXPERIMENTAL_SQLCLIENT_ENABLE_TRACE_CONTEXT_PROPAGATION` environment variable to propagate trace context to SQL Server databases. + This will remain experimental while the [specification](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/sql-server.md#context-propagation) + remains in development. It is now only available on .NET 8 and newer. ([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709))