Skip to content

Commit 215add0

Browse files
author
Chao
committed
[Instrumentation.SqlClient] Introducing ContextPropagationLevel opinion to propagate trace info to SQL Server
1 parent ae68958 commit 215add0

File tree

5 files changed

+81
-0
lines changed

5 files changed

+81
-0
lines changed

src/OpenTelemetry.Instrumentation.SqlClient/.publicApi/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.Rec
99
OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SetDbStatementForText.get -> bool
1010
OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SetDbStatementForText.set -> void
1111
OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.SqlClientTraceInstrumentationOptions() -> void
12+
OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.ContextPropagationLevel.get -> string!
13+
OpenTelemetry.Instrumentation.SqlClient.SqlClientTraceInstrumentationOptions.ContextPropagationLevel.set -> void
1214
OpenTelemetry.Metrics.SqlClientMeterProviderBuilderExtensions
1315
OpenTelemetry.Trace.TracerProviderBuilderExtensions
1416
static OpenTelemetry.Metrics.SqlClientMeterProviderBuilderExtensions.AddSqlClientInstrumentation(this OpenTelemetry.Metrics.MeterProviderBuilder! builder) -> OpenTelemetry.Metrics.MeterProviderBuilder!

src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
the new conventions.
1111
([#2811](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2811))
1212

13+
* Added `ContextPropagationLevel` option to propagate trace context to
14+
SQL Server database.
15+
([#2709](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2709))
16+
17+
Valid options:
18+
* `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))
19+
* `disabled`: Default value
20+
1321
## 1.12.0-beta.1
1422

1523
Released 2025-May-06

src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Diagnostics.CodeAnalysis;
99
#endif
1010
using System.Globalization;
11+
using System.Text;
1112
using OpenTelemetry.Trace;
1213

1314
namespace OpenTelemetry.Instrumentation.SqlClient.Implementation;
@@ -26,6 +27,9 @@ internal sealed class SqlClientDiagnosticListener : ListenerHandler
2627
public const string SqlDataWriteCommandError = "System.Data.SqlClient.WriteCommandError";
2728
public const string SqlMicrosoftWriteCommandError = "Microsoft.Data.SqlClient.WriteCommandError";
2829

30+
private const string ContextInfoParameterName = "@opentelemetry_traceparent";
31+
private const string SetContextSql = $"set context_info {ContextInfoParameterName}";
32+
2933
private readonly PropertyFetcher<object> commandFetcher = new("Command");
3034
private readonly PropertyFetcher<object> connectionFetcher = new("Connection");
3135
private readonly PropertyFetcher<string> dataSourceFetcher = new("DataSource");
@@ -64,6 +68,13 @@ public override void OnEventWritten(string name, object? payload)
6468
return;
6569
}
6670

71+
// skip if this is an injected query
72+
if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace &&
73+
command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql })
74+
{
75+
return;
76+
}
77+
6778
_ = this.connectionFetcher.TryFetch(command, out var connection);
6879
_ = this.databaseFetcher.TryFetch(connection, out var databaseName);
6980
_ = this.dataSourceFetcher.TryFetch(connection, out var dataSource);
@@ -82,6 +93,25 @@ public override void OnEventWritten(string name, object? payload)
8293
return;
8394
}
8495

96+
if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace &&
97+
command is IDbCommand { CommandType: CommandType.Text, Connection.State: ConnectionState.Open } iDbCommand)
98+
{
99+
var setContextCommand = iDbCommand.Connection.CreateCommand();
100+
setContextCommand.CommandText = SetContextSql;
101+
setContextCommand.CommandType = CommandType.Text;
102+
var parameter = setContextCommand.CreateParameter();
103+
parameter.ParameterName = ContextInfoParameterName;
104+
105+
var tracedflags = (activity.ActivityTraceFlags & ActivityTraceFlags.Recorded) != 0 ? "01" : "00";
106+
var traceparent = $"00-{activity.TraceId.ToHexString()}-{activity.SpanId.ToHexString()}-{tracedflags}";
107+
108+
parameter.DbType = DbType.Binary;
109+
parameter.Value = Encoding.UTF8.GetBytes(traceparent);
110+
setContextCommand.Parameters.Add(parameter);
111+
112+
setContextCommand.ExecuteNonQuery();
113+
}
114+
85115
if (activity.IsAllDataRequested)
86116
{
87117
try
@@ -168,6 +198,15 @@ public override void OnEventWritten(string name, object? payload)
168198
case SqlDataAfterExecuteCommand:
169199
case SqlMicrosoftAfterExecuteCommand:
170200
{
201+
_ = this.commandFetcher.TryFetch(payload, out var command);
202+
203+
// skip if this is an injected query
204+
if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace &&
205+
command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql })
206+
{
207+
return;
208+
}
209+
171210
if (activity == null)
172211
{
173212
SqlClientInstrumentationEventSource.Log.NullActivity(name);
@@ -189,6 +228,15 @@ public override void OnEventWritten(string name, object? payload)
189228
case SqlDataWriteCommandError:
190229
case SqlMicrosoftWriteCommandError:
191230
{
231+
_ = this.commandFetcher.TryFetch(payload, out var command);
232+
233+
// skip if this is an injected query
234+
if (options.ContextPropagationLevel == SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace &&
235+
command is IDbCommand { CommandType: CommandType.Text, CommandText: SetContextSql })
236+
{
237+
return;
238+
}
239+
192240
if (activity == null)
193241
{
194242
SqlClientInstrumentationEventSource.Log.NullActivity(name);

src/OpenTelemetry.Instrumentation.SqlClient/SqlClientTraceInstrumentationOptions.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ namespace OpenTelemetry.Instrumentation.SqlClient;
1717
/// </remarks>
1818
public class SqlClientTraceInstrumentationOptions
1919
{
20+
/// <summary>
21+
/// Flag to send traceparent information to SQL Server.
22+
/// </summary>
23+
internal const string ContextPropagationLevelTrace = "trace";
24+
25+
/// <summary>
26+
/// Flag to disable sending trace information to SQL Server.
27+
/// </summary>
28+
internal const string ContextPropagationDisabled = "disabled";
29+
2030
/// <summary>
2131
/// Initializes a new instance of the <see cref="SqlClientTraceInstrumentationOptions"/> class.
2232
/// </summary>
@@ -30,6 +40,7 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration)
3040
var databaseSemanticConvention = GetSemanticConventionOptIn(configuration);
3141
this.EmitOldAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.Old);
3242
this.EmitNewAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.New);
43+
this.ContextPropagationLevel = ContextPropagationDisabled;
3344
}
3445

3546
/// <summary>
@@ -63,6 +74,17 @@ internal SqlClientTraceInstrumentationOptions(IConfiguration configuration)
6374
/// </remarks>
6475
public bool SetDbStatementForText { get; set; }
6576

77+
/// <summary>
78+
/// Gets or sets a value indicating whether to send trace information to SQL Server database.
79+
/// Optional values:
80+
/// <see langword="trace"/>:
81+
/// Send traceparent information to SQL Server.
82+
/// <see langword="disabled"/>:
83+
/// Disable sending trace information to SQL Server.
84+
/// Default value: <see landword="disabled"/>.
85+
/// </summary>
86+
public string ContextPropagationLevel { get; set; }
87+
6688
/// <summary>
6789
/// Gets or sets an action to enrich an <see cref="Activity"/> with the
6890
/// raw <c>SqlCommand</c> object.

test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public void SuccessfulCommandTest(
5656
{
5757
options.SetDbStatementForText = captureTextCommandContent;
5858
options.RecordException = recordException;
59+
options.ContextPropagationLevel = SqlClientTraceInstrumentationOptions.ContextPropagationLevelTrace;
5960
})
6061
.Build();
6162

0 commit comments

Comments
 (0)