19
19
using Microsoft . Extensions . EnvironmentAbstractions ;
20
20
using NuGet . Frameworks ;
21
21
using OpenTelemetry ;
22
+ using OpenTelemetry . Context . Propagation ;
22
23
using OpenTelemetry . Metrics ;
23
24
using OpenTelemetry . Resources ;
24
25
using OpenTelemetry . Trace ;
@@ -56,12 +57,16 @@ public class Program
56
57
. AddOtlpExporter ( )
57
58
. Build ( ) ;
58
59
60
+ private static ActivityContext s_parentActivityContext = default ;
61
+ private static ActivityKind s_activityKind = ActivityKind . Internal ;
62
+
59
63
60
64
public static int Main ( string [ ] args )
61
65
{
62
66
// capture the time to we can compute muxer/host startup overhead
63
67
DateTime mainTimeStamp = DateTime . Now ;
64
- using var _mainActivity = Activities . s_source . StartActivity ( "main" ) ;
68
+ ( s_parentActivityContext , s_activityKind ) = DeriveParentActivityContextFromEnv ( ) ;
69
+ using var _mainActivity = Activities . s_source . StartActivity ( "main" , s_activityKind , s_parentActivityContext ) ;
65
70
using AutomaticEncodingRestorer _encodingRestorer = new ( ) ;
66
71
67
72
// Setting output encoding is not available on those platforms
@@ -119,9 +124,44 @@ public static int Main(string[] args)
119
124
}
120
125
}
121
126
127
+ /// <summary>
128
+ /// uses the OpenTelemetrySDK's Propagation API to derive the parent activity context and kind
129
+ /// from the DOTNET_CLI_TRACEPARENT and DOTNET_CLI_TRACESTATE environment variables.
130
+ /// </summary>
131
+ /// <returns></returns>
132
+ private static ( ActivityContext parentActivityContext , ActivityKind kind ) DeriveParentActivityContextFromEnv ( )
133
+ {
134
+ var traceParent = Env . GetEnvironmentVariable ( Activities . DOTNET_CLI_TRACEPARENT ) ;
135
+ var traceState = Env . GetEnvironmentVariable ( Activities . DOTNET_CLI_TRACESTATE ) ;
136
+ static IEnumerable < string > GetValueFromCarrier ( Dictionary < string , IEnumerable < string > > carrier , string key )
137
+ {
138
+ return carrier . TryGetValue ( key , out var value ) ? value : Enumerable . Empty < string > ( ) ;
139
+ }
140
+
141
+ if ( string . IsNullOrEmpty ( traceParent ) )
142
+ {
143
+ return ( default , ActivityKind . Internal ) ;
144
+ }
145
+ var carriermap = new Dictionary < string , IEnumerable < string > >
146
+ {
147
+ { "traceparent" , [ traceParent ] } ,
148
+ { "tracestate" , [ traceState ] }
149
+ } ;
150
+
151
+ // 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.
152
+ Sdk . SetDefaultTextMapPropagator ( new CompositeTextMapPropagator ( [
153
+ new TraceContextPropagator ( ) ,
154
+ new BaggagePropagator ( )
155
+ ] ) ) ;
156
+ var parentActivityContext = Propagators . DefaultTextMapPropagator . Extract ( default , carriermap , GetValueFromCarrier ) ;
157
+ var kind = parentActivityContext . ActivityContext . IsRemote ? ActivityKind . Server : ActivityKind . Internal ;
158
+
159
+ return ( parentActivityContext . ActivityContext , kind ) ;
160
+ }
161
+
122
162
private static void TrackHostStartup ( DateTime mainTimeStamp )
123
163
{
124
- var hostStartupActivity = Activities . s_source . CreateActivity ( "host-startup" , ActivityKind . Server ) ;
164
+ var hostStartupActivity = Activities . s_source . CreateActivity ( "host-startup" , s_activityKind , s_parentActivityContext ) ;
125
165
hostStartupActivity ? . SetStartTime ( Process . GetCurrentProcess ( ) . StartTime ) ;
126
166
hostStartupActivity ? . SetEndTime ( mainTimeStamp ) ;
127
167
hostStartupActivity ? . SetStatus ( ActivityStatusCode . Ok ) ;
@@ -140,20 +180,48 @@ private static void SetupMSBuildEnvironmentInvariants()
140
180
}
141
181
}
142
182
143
- internal static int ProcessArgs ( string [ ] args )
183
+ private static string GetCommandName ( ParseResult r )
184
+ {
185
+ // walk the parent command tree to find the top-level command name and get the full command name for this parseresult
186
+ List < string > parentNames = [ r . CommandResult . Command . Name ] ;
187
+ var current = r . CommandResult . Parent ;
188
+ while ( current is CommandResult parentCommandResult )
189
+ {
190
+ parentNames . Add ( parentCommandResult . Command . Name ) ;
191
+ current = parentCommandResult . Parent ;
192
+ }
193
+ parentNames . Reverse ( ) ;
194
+ return string . Join ( ' ' , parentNames ) ;
195
+ }
196
+ private static void SetDisplayName ( Activity activity , ParseResult parseResult )
197
+ {
198
+ if ( activity == null )
199
+ {
200
+ return ;
201
+ }
202
+ var name = GetCommandName ( parseResult ) ;
203
+ // Set the display name to the full command name
204
+ activity . DisplayName = name ;
205
+
206
+ // Set the command name as an attribute for better filtering in telemetry
207
+ activity . SetTag ( "command.name" , name ) ;
208
+ }
209
+
210
+ internal static int ProcessArgs ( string [ ] args , ITelemetry telemetryClient = null )
144
211
{
145
212
ParseResult parseResult ;
146
- using ( var _parseActivity = Activities . s_source . StartActivity ( "parse" ) )
213
+ using ( var _parseActivity = Activities . s_source . StartActivity ( "parse" , s_activityKind , s_parentActivityContext ) )
147
214
{
148
215
parseResult = Parser . Instance . Parse ( args ) ;
149
216
217
+ SetDisplayName ( _parseActivity , parseResult ) ;
150
218
// Avoid create temp directory with root permission and later prevent access in non sudo
151
219
// This method need to be run very early before temp folder get created
152
220
// https://github.com/dotnet/sdk/issues/20195
153
221
SudoEnvironmentDirectoryOverride . OverrideEnvironmentVariableToTmp ( parseResult ) ;
154
222
}
155
223
156
- using ( var _firstTimeUseActivity = Activities . s_source . StartActivity ( "first-time-use" ) )
224
+ using ( var _firstTimeUseActivity = Activities . s_source . StartActivity ( "first-time-use" , s_activityKind , s_parentActivityContext ) )
157
225
{
158
226
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel ( ) ;
159
227
@@ -309,7 +377,7 @@ static void InvokeBuiltInCommand(ParseResult parseResult, out int exitCode)
309
377
}
310
378
}
311
379
}
312
-
380
+
313
381
private static int AdjustExitCode ( ParseResult parseResult , int exitCode )
314
382
{
315
383
if ( parseResult . Errors . Count > 0 )
0 commit comments