17
17
using Microsoft . Extensions . EnvironmentAbstractions ;
18
18
using NuGet . Frameworks ;
19
19
using OpenTelemetry ;
20
+ using OpenTelemetry . Context . Propagation ;
20
21
using OpenTelemetry . Metrics ;
21
22
using OpenTelemetry . Resources ;
22
23
using OpenTelemetry . Trace ;
@@ -54,12 +55,16 @@ public class Program
54
55
. AddOtlpExporter ( )
55
56
. Build ( ) ;
56
57
58
+ private static ActivityContext s_parentActivityContext = default ;
59
+ private static ActivityKind s_activityKind = ActivityKind . Internal ;
60
+
57
61
58
62
public static int Main ( string [ ] args )
59
63
{
60
64
// capture the time to we can compute muxer/host startup overhead
61
65
DateTime mainTimeStamp = DateTime . Now ;
62
- using var _mainActivity = Activities . s_source . StartActivity ( "main" ) ;
66
+ ( s_parentActivityContext , s_activityKind ) = DeriveParentActivityContextFromEnv ( ) ;
67
+ using var _mainActivity = Activities . s_source . StartActivity ( "main" , s_activityKind , s_parentActivityContext ) ;
63
68
using AutomaticEncodingRestorer _encodingRestorer = new ( ) ;
64
69
65
70
// Setting output encoding is not available on those platforms
@@ -117,9 +122,44 @@ public static int Main(string[] args)
117
122
}
118
123
}
119
124
125
+ /// <summary>
126
+ /// uses the OpenTelemetrySDK's Propagation API to derive the parent activity context and kind
127
+ /// from the DOTNET_CLI_TRACEPARENT and DOTNET_CLI_TRACESTATE environment variables.
128
+ /// </summary>
129
+ /// <returns></returns>
130
+ private static ( ActivityContext parentActivityContext , ActivityKind kind ) DeriveParentActivityContextFromEnv ( )
131
+ {
132
+ var traceParent = Env . GetEnvironmentVariable ( Activities . DOTNET_CLI_TRACEPARENT ) ;
133
+ var traceState = Env . GetEnvironmentVariable ( Activities . DOTNET_CLI_TRACESTATE ) ;
134
+ static IEnumerable < string > GetValueFromCarrier ( Dictionary < string , IEnumerable < string > > carrier , string key )
135
+ {
136
+ return carrier . TryGetValue ( key , out var value ) ? value : Enumerable . Empty < string > ( ) ;
137
+ }
138
+
139
+ if ( string . IsNullOrEmpty ( traceParent ) )
140
+ {
141
+ return ( default , ActivityKind . Internal ) ;
142
+ }
143
+ var carriermap = new Dictionary < string , IEnumerable < string > >
144
+ {
145
+ { "traceparent" , [ traceParent ] } ,
146
+ { "tracestate" , [ traceState ] }
147
+ } ;
148
+
149
+ // Use the OpenTelemetry Propagator to extract the parent activity context and kind. For some reason this isn't set by the OTel SDK like docs say it should be.
150
+ Sdk . SetDefaultTextMapPropagator ( new CompositeTextMapPropagator ( [
151
+ new TraceContextPropagator ( ) ,
152
+ new BaggagePropagator ( )
153
+ ] ) ) ;
154
+ var parentActivityContext = Propagators . DefaultTextMapPropagator . Extract ( default , carriermap , GetValueFromCarrier ) ;
155
+ var kind = parentActivityContext . ActivityContext . IsRemote ? ActivityKind . Server : ActivityKind . Internal ;
156
+
157
+ return ( parentActivityContext . ActivityContext , kind ) ;
158
+ }
159
+
120
160
private static void TrackHostStartup ( DateTime mainTimeStamp )
121
161
{
122
- var hostStartupActivity = Activities . s_source . CreateActivity ( "host-startup" , ActivityKind . Server ) ;
162
+ var hostStartupActivity = Activities . s_source . CreateActivity ( "host-startup" , s_activityKind , s_parentActivityContext ) ;
123
163
hostStartupActivity ? . SetStartTime ( Process . GetCurrentProcess ( ) . StartTime ) ;
124
164
hostStartupActivity ? . SetEndTime ( mainTimeStamp ) ;
125
165
hostStartupActivity ? . SetStatus ( ActivityStatusCode . Ok ) ;
@@ -138,23 +178,51 @@ private static void SetupMSBuildEnvironmentInvariants()
138
178
}
139
179
}
140
180
141
- internal static int ProcessArgs ( string [ ] args )
181
+ private static string GetCommandName ( ParseResult r )
182
+ {
183
+ // walk the parent command tree to find the top-level command name and get the full command name for this parseresult
184
+ List < string > parentNames = [ r . CommandResult . Command . Name ] ;
185
+ var current = r . CommandResult . Parent ;
186
+ while ( current is CommandResult parentCommandResult )
187
+ {
188
+ parentNames . Add ( parentCommandResult . Command . Name ) ;
189
+ current = parentCommandResult . Parent ;
190
+ }
191
+ parentNames . Reverse ( ) ;
192
+ return string . Join ( ' ' , parentNames ) ;
193
+ }
194
+ private static void SetDisplayName ( Activity activity , ParseResult parseResult )
195
+ {
196
+ if ( activity == null )
197
+ {
198
+ return ;
199
+ }
200
+ var name = GetCommandName ( parseResult ) ;
201
+ // Set the display name to the full command name
202
+ activity . DisplayName = name ;
203
+
204
+ // Set the command name as an attribute for better filtering in telemetry
205
+ activity . SetTag ( "command.name" , name ) ;
206
+ }
207
+
208
+ internal static int ProcessArgs ( string [ ] args , ITelemetry telemetryClient = null )
142
209
{
143
210
ParseResult parseResult ;
144
- using ( var _parseActivity = Activities . s_source . StartActivity ( "parse" ) )
211
+ using ( var _parseActivity = Activities . s_source . StartActivity ( "parse" , s_activityKind , s_parentActivityContext ) )
145
212
{
146
213
// If we get C# file path as the first argument, parse as `dotnet run file.cs`.
147
214
parseResult = args is [ { } filePath , ..] && VirtualProjectBuildingCommand . IsValidEntryPointPath ( filePath )
148
215
? Parser . Instance . Parse ( [ "run" , .. args ] )
149
216
: Parser . Instance . Parse ( args ) ;
150
217
218
+ SetDisplayName ( _parseActivity , parseResult ) ;
151
219
// Avoid create temp directory with root permission and later prevent access in non sudo
152
220
// This method need to be run very early before temp folder get created
153
221
// https://github.com/dotnet/sdk/issues/20195
154
222
SudoEnvironmentDirectoryOverride . OverrideEnvironmentVariableToTmp ( parseResult ) ;
155
223
}
156
224
157
- using ( var _firstTimeUseActivity = Activities . s_source . StartActivity ( "first-time-use" ) )
225
+ using ( var _firstTimeUseActivity = Activities . s_source . StartActivity ( "first-time-use" , s_activityKind , s_parentActivityContext ) )
158
226
{
159
227
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel ( ) ;
160
228
@@ -221,17 +289,7 @@ internal static int ProcessArgs(string[] args)
221
289
int exitCode ;
222
290
if ( parseResult . CanBeInvoked ( ) )
223
291
{
224
- using var _invocationActivity = Activities . s_source . StartActivity ( "invocation" ) ;
225
- // walk the parent command tree to find the top-level command name and get the full command name for this parseresult
226
- List < string > parentNames = [ parseResult . CommandResult . Command . Name ] ;
227
- var current = parseResult . CommandResult . Parent ;
228
- while ( current is CommandResult parentCommandResult )
229
- {
230
- parentNames . Add ( parentCommandResult . Command . Name ) ;
231
- current = parentCommandResult . Parent ;
232
- }
233
- parentNames . Reverse ( ) ;
234
- _invocationActivity ? . DisplayName = string . Join ( ' ' , parentNames ) ;
292
+ using var _invocationActivity = Activities . s_source . StartActivity ( "invocation" , s_activityKind , s_parentActivityContext ) ;
235
293
try
236
294
{
237
295
exitCode = parseResult . Invoke ( ) ;
@@ -246,14 +304,14 @@ internal static int ProcessArgs(string[] args)
246
304
{
247
305
try
248
306
{
249
- var _lookupExternalCommandActivity = Activities . s_source . StartActivity ( "lookup-external-command" ) ;
307
+ var _lookupExternalCommandActivity = Activities . s_source . StartActivity ( "lookup-external-command" , s_activityKind , s_parentActivityContext ) ;
250
308
var resolvedCommand = CommandFactoryUsingResolver . Create (
251
309
"dotnet-" + parseResult . GetValue ( Parser . DotnetSubCommand ) ,
252
310
args . GetSubArguments ( ) ,
253
311
FrameworkConstants . CommonFrameworks . NetStandardApp15 ) ;
254
312
_lookupExternalCommandActivity ? . Dispose ( ) ;
255
313
256
- var _executionActivity = Activities . s_source . StartActivity ( "execute-extensible-command" ) ;
314
+ var _executionActivity = Activities . s_source . StartActivity ( "execute-extensible-command" , s_activityKind , s_parentActivityContext ) ;
257
315
var result = resolvedCommand . Execute ( ) ;
258
316
_executionActivity ? . Dispose ( ) ;
259
317
0 commit comments