88using System . Diagnostics . CodeAnalysis ;
99#endif
1010using System . Globalization ;
11+ using System . Text ;
1112using OpenTelemetry . Trace ;
1213
1314namespace 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
@@ -125,6 +155,26 @@ public override void OnEventWritten(string name, object? payload)
125155 break ;
126156
127157 case CommandType . Text :
158+ // This section is not for production use. DEMO only
159+ if ( options . ContextPropagationLevel == SqlClientTraceInstrumentationOptions
160+ . ContextPropagationLevelTrace &&
161+ command is IDbCommand and not
162+ {
163+ CommandText : SetContextSql
164+ } )
165+ {
166+ var idbCommand = ( IDbCommand ) command ;
167+
168+ // TODO: this service name is not correct, to fix it with correct one.
169+ var serviceName = options . ServiceName ?? "UnknownService" ;
170+ Dictionary < string , string > params4 = new Dictionary < string , string > { { "service.name" , serviceName } } ;
171+ string encoded = SqlCommenter . EncodeParams ( params4 ) ;
172+ string comment = SqlCommenter . CreateComment ( encoded ) ;
173+
174+ // TODO: we need to check if the command text is already set with a comment.
175+ idbCommand . CommandText = $ "{ comment } { idbCommand . CommandText } ";
176+ }
177+
128178 if ( options . SetDbStatementForText )
129179 {
130180 var sqlStatementInfo = SqlProcessor . GetSanitizedSql ( commandText ) ;
@@ -168,6 +218,15 @@ public override void OnEventWritten(string name, object? payload)
168218 case SqlDataAfterExecuteCommand :
169219 case SqlMicrosoftAfterExecuteCommand :
170220 {
221+ _ = this . commandFetcher . TryFetch ( payload , out var command ) ;
222+
223+ // skip if this is an injected query
224+ if ( options . ContextPropagationLevel == SqlClientTraceInstrumentationOptions . ContextPropagationLevelTrace &&
225+ command is IDbCommand { CommandType : CommandType . Text , CommandText : SetContextSql } )
226+ {
227+ return ;
228+ }
229+
171230 if ( activity == null )
172231 {
173232 SqlClientInstrumentationEventSource . Log . NullActivity ( name ) ;
@@ -189,6 +248,15 @@ public override void OnEventWritten(string name, object? payload)
189248 case SqlDataWriteCommandError :
190249 case SqlMicrosoftWriteCommandError :
191250 {
251+ _ = this . commandFetcher . TryFetch ( payload , out var command ) ;
252+
253+ // skip if this is an injected query
254+ if ( options . ContextPropagationLevel == SqlClientTraceInstrumentationOptions . ContextPropagationLevelTrace &&
255+ command is IDbCommand { CommandType : CommandType . Text , CommandText : SetContextSql } )
256+ {
257+ return ;
258+ }
259+
192260 if ( activity == null )
193261 {
194262 SqlClientInstrumentationEventSource . Log . NullActivity ( name ) ;
0 commit comments