1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
- #nullable disable
5
-
6
4
using System . CommandLine ;
7
5
using System . Diagnostics ;
8
6
using Microsoft . DotNet . Cli . CommandFactory ;
@@ -32,39 +30,55 @@ public class Program
32
30
public static ITelemetry TelemetryClient ;
33
31
// Create a new OpenTelemetry tracer provider and add the Azure Monitor trace exporter and the OTLP trace exporter.
34
32
// It is important to keep the TracerProvider instance active throughout the process lifetime.
35
- private static TracerProvider tracerProvider = Sdk . CreateTracerProviderBuilder ( )
36
- . ConfigureResource ( r =>
37
- {
38
- r . AddService ( "dotnet-cli" , serviceVersion : Product . Version ) ;
39
- } )
40
- . AddSource ( Activities . s_source . Name )
41
- . AddHttpClientInstrumentation ( )
42
- . AddOtlpExporter ( )
43
- . Build ( ) ;
33
+ private static TracerProvider tracerProvider ;
44
34
45
35
// Create a new OpenTelemetry meter provider and add the Azure Monitor metric exporter and the OTLP metric exporter.
46
36
// It is important to keep the MetricsProvider instance active throughout the process lifetime.
47
- private static MeterProvider metricsProvider = Sdk . CreateMeterProviderBuilder ( )
48
- . ConfigureResource ( r =>
49
- {
50
- r . AddService ( "dotnet-cli" , serviceVersion : Product . Version ) ;
51
- } )
52
- . AddMeter ( Activities . s_source . Name )
53
- . AddHttpClientInstrumentation ( )
54
- . AddRuntimeInstrumentation ( )
55
- . AddOtlpExporter ( )
56
- . Build ( ) ;
37
+ private static MeterProvider metricsProvider ;
57
38
58
- private static ActivityContext s_parentActivityContext = default ;
59
- private static ActivityKind s_activityKind = ActivityKind . Internal ;
39
+ static Activity ? s_mainActivity ;
40
+ private static DateTime s_mainTimeStamp ;
41
+ private static PosixSignalRegistration s_sigIntRegistration ;
42
+ private static PosixSignalRegistration s_sigQuitRegistration ;
43
+ private static PosixSignalRegistration s_sigTermRegistration ;
60
44
45
+ static Program ( )
46
+ {
47
+ s_mainTimeStamp = DateTime . Now ;
48
+ s_sigIntRegistration = PosixSignalRegistration . Create ( PosixSignal . SIGINT , Shutdown ) ;
49
+ s_sigQuitRegistration = PosixSignalRegistration . Create ( PosixSignal . SIGQUIT , Shutdown ) ;
50
+ s_sigTermRegistration = PosixSignalRegistration . Create ( PosixSignal . SIGTERM , Shutdown ) ;
51
+ metricsProvider = Sdk . CreateMeterProviderBuilder ( )
52
+ . ConfigureResource ( r =>
53
+ {
54
+ r . AddService ( "dotnet-cli" , serviceVersion : Product . Version ) ;
55
+ } )
56
+ . AddMeter ( Activities . s_source . Name )
57
+ . AddHttpClientInstrumentation ( )
58
+ . AddRuntimeInstrumentation ( )
59
+ . AddOtlpExporter ( )
60
+ . Build ( ) ;
61
+ tracerProvider = Sdk . CreateTracerProviderBuilder ( )
62
+ . ConfigureResource ( r =>
63
+ {
64
+ r . AddService ( "dotnet-cli" , serviceVersion : Product . Version ) ;
65
+ } )
66
+ . AddSource ( Activities . s_source . Name )
67
+ . AddHttpClientInstrumentation ( )
68
+ . AddOtlpExporter ( )
69
+ . SetSampler ( new AlwaysOnSampler ( ) )
70
+ . Build ( ) ;
71
+ ( var s_parentActivityContext , var s_activityKind ) = DeriveParentActivityContextFromEnv ( ) ;
72
+ s_mainActivity = Activities . s_source . CreateActivity ( "main" , s_activityKind , s_parentActivityContext ) ;
73
+ s_mainActivity ? . Start ( ) ;
74
+ s_mainActivity ? . SetStartTime ( Process . GetCurrentProcess ( ) . StartTime ) ;
75
+ TrackHostStartup ( s_mainTimeStamp ) ;
76
+ SetupMSBuildEnvironmentInvariants ( ) ;
77
+ TelemetryClient = InitializeTelemetry ( ) ;
78
+ }
61
79
62
80
public static int Main ( string [ ] args )
63
81
{
64
- // capture the time to we can compute muxer/host startup overhead
65
- DateTime mainTimeStamp = DateTime . Now ;
66
- ( s_parentActivityContext , s_activityKind ) = DeriveParentActivityContextFromEnv ( ) ;
67
- using var _mainActivity = Activities . s_source . StartActivity ( "main" , s_activityKind , s_parentActivityContext ) ;
68
82
using AutomaticEncodingRestorer _encodingRestorer = new ( ) ;
69
83
70
84
// Setting output encoding is not available on those platforms
@@ -75,51 +89,50 @@ public static int Main(string[] args)
75
89
76
90
DebugHelper . HandleDebugSwitch ( ref args ) ;
77
91
78
- TrackHostStartup ( mainTimeStamp ) ;
79
-
80
- SetupMSBuildEnvironmentInvariants ( ) ;
92
+ InitializeProcess ( ) ;
81
93
82
94
try
83
95
{
84
- InitializeProcess ( ) ;
96
+ return ProcessArgs ( args ) ;
97
+ }
98
+ catch ( Exception e ) when ( e . ShouldBeDisplayedAsError ( ) )
99
+ {
100
+ Reporter . Error . WriteLine ( CommandLoggingContext . IsVerbose
101
+ ? e . ToString ( ) . Red ( ) . Bold ( )
102
+ : e . Message . Red ( ) . Bold ( ) ) ;
85
103
86
- try
104
+ var commandParsingException = e as CommandParsingException ;
105
+ if ( commandParsingException != null && commandParsingException . ParseResult != null )
87
106
{
88
- return ProcessArgs ( args ) ;
107
+ commandParsingException . ParseResult . ShowHelp ( ) ;
89
108
}
90
- catch ( Exception e ) when ( e . ShouldBeDisplayedAsError ( ) )
91
- {
92
- Reporter . Error . WriteLine ( CommandLoggingContext . IsVerbose
93
- ? e . ToString ( ) . Red ( ) . Bold ( )
94
- : e . Message . Red ( ) . Bold ( ) ) ;
95
-
96
- var commandParsingException = e as CommandParsingException ;
97
- if ( commandParsingException != null && commandParsingException . ParseResult != null )
98
- {
99
- commandParsingException . ParseResult . ShowHelp ( ) ;
100
- }
101
109
102
- return 1 ;
103
- }
104
- catch ( Exception e ) when ( ! e . ShouldBeDisplayedAsError ( ) )
105
- {
106
- // If telemetry object has not been initialized yet. It cannot be collected
107
- TelemetryEventEntry . SendFiltered ( e ) ;
108
- Reporter . Error . WriteLine ( e . ToString ( ) . Red ( ) . Bold ( ) ) ;
110
+ return 1 ;
111
+ }
112
+ catch ( Exception e ) when ( ! e . ShouldBeDisplayedAsError ( ) )
113
+ {
114
+ // If telemetry object has not been initialized yet. It cannot be collected
115
+ TelemetryEventEntry . SendFiltered ( e ) ;
116
+ Reporter . Error . WriteLine ( e . ToString ( ) . Red ( ) . Bold ( ) ) ;
109
117
110
- return 1 ;
111
- }
112
- finally
113
- {
114
- PerformanceLogEventSource . Log . CLIStop ( ) ;
115
- }
118
+ return 1 ;
116
119
}
117
120
finally
118
121
{
119
- tracerProvider ? . ForceFlush ( ) ;
120
- metricsProvider ? . ForceFlush ( ) ;
121
- Activities . s_source . Dispose ( ) ;
122
+ Shutdown ( default ! ) ;
122
123
}
124
+
125
+ }
126
+
127
+ public static void Shutdown ( PosixSignalContext context )
128
+ {
129
+ s_sigIntRegistration . Dispose ( ) ;
130
+ s_sigQuitRegistration . Dispose ( ) ;
131
+ s_sigTermRegistration . Dispose ( ) ;
132
+ s_mainActivity ? . Stop ( ) ;
133
+ tracerProvider ? . ForceFlush ( ) ;
134
+ metricsProvider ? . ForceFlush ( ) ;
135
+ Activities . s_source . Dispose ( ) ;
123
136
}
124
137
125
138
/// <summary>
@@ -131,20 +144,23 @@ private static (ActivityContext parentActivityContext, ActivityKind kind) Derive
131
144
{
132
145
var traceParent = Env . GetEnvironmentVariable ( Activities . DOTNET_CLI_TRACEPARENT ) ;
133
146
var traceState = Env . GetEnvironmentVariable ( Activities . DOTNET_CLI_TRACESTATE ) ;
134
- static IEnumerable < string > GetValueFromCarrier ( Dictionary < string , IEnumerable < string > > carrier , string key )
147
+ static IEnumerable < string > ? GetValueFromCarrier ( Dictionary < string , IEnumerable < string > ? > carrier , string key )
135
148
{
136
- return carrier . TryGetValue ( key , out var value ) ? value : Enumerable . Empty < string > ( ) ;
149
+ return carrier . TryGetValue ( key , out var value ) ? value : null ;
137
150
}
138
151
139
152
if ( string . IsNullOrEmpty ( traceParent ) )
140
153
{
141
154
return ( default , ActivityKind . Internal ) ;
142
155
}
143
- var carriermap = new Dictionary < string , IEnumerable < string > >
156
+ var carriermap = new Dictionary < string , IEnumerable < string > ? >
144
157
{
145
158
{ "traceparent" , [ traceParent ] } ,
146
- { "tracestate" , [ traceState ] }
147
159
} ;
160
+ if ( ! string . IsNullOrEmpty ( traceState ) )
161
+ {
162
+ carriermap . Add ( "tracestate" , [ traceState ] ) ;
163
+ }
148
164
149
165
// 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
166
Sdk . SetDefaultTextMapPropagator ( new CompositeTextMapPropagator ( [
@@ -159,7 +175,7 @@ static IEnumerable<string> GetValueFromCarrier(Dictionary<string, IEnumerable<st
159
175
160
176
private static void TrackHostStartup ( DateTime mainTimeStamp )
161
177
{
162
- var hostStartupActivity = Activities . s_source . CreateActivity ( "host-startup" , s_activityKind , s_parentActivityContext ) ;
178
+ var hostStartupActivity = Activities . s_source . StartActivity ( "host-startup" ) ;
163
179
hostStartupActivity ? . SetStartTime ( Process . GetCurrentProcess ( ) . StartTime ) ;
164
180
hostStartupActivity ? . SetEndTime ( mainTimeStamp ) ;
165
181
hostStartupActivity ? . SetStatus ( ActivityStatusCode . Ok ) ;
@@ -191,38 +207,113 @@ private static string GetCommandName(ParseResult r)
191
207
parentNames . Reverse ( ) ;
192
208
return string . Join ( ' ' , parentNames ) ;
193
209
}
194
- private static void SetDisplayName ( Activity activity , ParseResult parseResult )
210
+ private static void SetDisplayName ( Activity ? activity , ParseResult parseResult )
195
211
{
196
212
if ( activity == null )
197
213
{
198
214
return ;
199
215
}
200
216
var name = GetCommandName ( parseResult ) ;
217
+
201
218
// Set the display name to the full command name
202
219
activity . DisplayName = name ;
203
220
204
221
// Set the command name as an attribute for better filtering in telemetry
205
222
activity . SetTag ( "command.name" , name ) ;
206
223
}
207
224
208
- internal static int ProcessArgs ( string [ ] args , ITelemetry telemetryClient = null )
225
+ internal static int ProcessArgs ( string [ ] args )
226
+ {
227
+ ParseResult parseResult = ParseArgs ( args ) ;
228
+ SetupDotnetFirstRun ( parseResult ) ;
229
+
230
+ if ( parseResult . CanBeInvoked ( ) )
231
+ {
232
+ return Invoke ( parseResult ) ;
233
+ }
234
+ else
235
+ {
236
+ try
237
+ {
238
+ return LookupAndExecuteCommand ( args , parseResult ) ;
239
+ }
240
+ catch ( CommandUnknownException e )
241
+ {
242
+ Reporter . Error . WriteLine ( e . Message . Red ( ) ) ;
243
+ Reporter . Output . WriteLine ( e . InstructionMessage ) ;
244
+ return 1 ;
245
+ }
246
+ }
247
+ }
248
+
249
+ private static int LookupAndExecuteCommand ( string [ ] args , ParseResult parseResult )
250
+ {
251
+ var _lookupExternalCommandActivity = Activities . s_source . StartActivity ( "lookup-external-command" ) ;
252
+ var resolvedCommand = CommandFactoryUsingResolver . Create (
253
+ "dotnet-" + parseResult . GetValue ( Parser . DotnetSubCommand ) ,
254
+ args . GetSubArguments ( ) ,
255
+ FrameworkConstants . CommonFrameworks . NetStandardApp15 ) ;
256
+ _lookupExternalCommandActivity ? . Dispose ( ) ;
257
+
258
+ var _executionActivity = Activities . s_source . StartActivity ( "execute-extensible-command" ) ;
259
+ var result = resolvedCommand . Execute ( ) ;
260
+ _executionActivity ? . Dispose ( ) ;
261
+
262
+ return result . ExitCode ;
263
+ }
264
+
265
+ private static int Invoke ( ParseResult parseResult )
266
+ {
267
+ using var _invocationActivity = Activities . s_source . StartActivity ( "invocation" ) ;
268
+ try
269
+ {
270
+ var exitCode = parseResult . Invoke ( ) ;
271
+ return AdjustExitCode ( parseResult , exitCode ) ;
272
+ }
273
+ catch ( Exception exception )
274
+ {
275
+ return Parser . ExceptionHandler ( exception , parseResult ) ;
276
+ }
277
+ }
278
+
279
+ private static ITelemetry InitializeTelemetry ( )
280
+ {
281
+ var telemetryClient = new Telemetry . Telemetry ( ) ;
282
+ TelemetryEventEntry . Subscribe ( telemetryClient . TrackEvent ) ;
283
+ TelemetryEventEntry . TelemetryFilter = new TelemetryFilter ( Sha256Hasher . HashWithNormalizedCasing ) ;
284
+
285
+ if ( CommandLoggingContext . IsVerbose )
286
+ {
287
+ Console . WriteLine ( $ "Telemetry is: { ( telemetryClient . Enabled ? "Enabled" : "Disabled" ) } ") ;
288
+ }
289
+
290
+ return telemetryClient ;
291
+ }
292
+
293
+ private static ParseResult ParseArgs ( string [ ] args )
209
294
{
210
295
ParseResult parseResult ;
211
- using ( var _parseActivity = Activities . s_source . StartActivity ( "parse" , s_activityKind , s_parentActivityContext ) )
296
+ using ( var _parseActivity = Activities . s_source . StartActivity ( "parse" ) )
212
297
{
213
298
// If we get C# file path as the first argument, parse as `dotnet run file.cs`.
214
299
parseResult = args is [ { } filePath , ..] && VirtualProjectBuildingCommand . IsValidEntryPointPath ( filePath )
215
300
? Parser . Instance . Parse ( [ "run" , .. args ] )
216
301
: Parser . Instance . Parse ( args ) ;
217
302
218
- SetDisplayName ( _parseActivity , parseResult ) ;
219
303
// Avoid create temp directory with root permission and later prevent access in non sudo
220
304
// This method need to be run very early before temp folder get created
221
305
// https://github.com/dotnet/sdk/issues/20195
222
306
SudoEnvironmentDirectoryOverride . OverrideEnvironmentVariableToTmp ( parseResult ) ;
223
307
}
224
308
225
- using ( var _firstTimeUseActivity = Activities . s_source . StartActivity ( "first-time-use" , s_activityKind , s_parentActivityContext ) )
309
+ SetDisplayName ( s_mainActivity , parseResult ) ;
310
+
311
+ return parseResult ;
312
+ }
313
+
314
+ private static void SetupDotnetFirstRun ( ParseResult parseResult )
315
+ {
316
+ using ( var _firstTimeUseActivity = Activities . s_source . StartActivity ( "first-time-use" ) )
226
317
{
227
318
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel ( ) ;
228
319
@@ -276,57 +367,6 @@ internal static int ProcessArgs(string[] args, ITelemetry telemetryClient = null
276
367
environmentProvider ,
277
368
skipFirstTimeUseCheck : getStarOptionPassed ) ;
278
369
}
279
-
280
- TelemetryClient = new Telemetry . Telemetry ( ) ;
281
- TelemetryEventEntry . Subscribe ( TelemetryClient . TrackEvent ) ;
282
- TelemetryEventEntry . TelemetryFilter = new TelemetryFilter ( Sha256Hasher . HashWithNormalizedCasing ) ;
283
-
284
- if ( CommandLoggingContext . IsVerbose )
285
- {
286
- Console . WriteLine ( $ "Telemetry is: { ( TelemetryClient . Enabled ? "Enabled" : "Disabled" ) } ") ;
287
- }
288
-
289
- int exitCode ;
290
- if ( parseResult . CanBeInvoked ( ) )
291
- {
292
- using var _invocationActivity = Activities . s_source . StartActivity ( "invocation" , s_activityKind , s_parentActivityContext ) ;
293
- try
294
- {
295
- exitCode = parseResult . Invoke ( ) ;
296
- exitCode = AdjustExitCode ( parseResult , exitCode ) ;
297
- }
298
- catch ( Exception exception )
299
- {
300
- exitCode = Parser . ExceptionHandler ( exception , parseResult ) ;
301
- }
302
- }
303
- else
304
- {
305
- try
306
- {
307
- var _lookupExternalCommandActivity = Activities . s_source . StartActivity ( "lookup-external-command" , s_activityKind , s_parentActivityContext ) ;
308
- var resolvedCommand = CommandFactoryUsingResolver . Create (
309
- "dotnet-" + parseResult . GetValue ( Parser . DotnetSubCommand ) ,
310
- args . GetSubArguments ( ) ,
311
- FrameworkConstants . CommonFrameworks . NetStandardApp15 ) ;
312
- _lookupExternalCommandActivity ? . Dispose ( ) ;
313
-
314
- var _executionActivity = Activities . s_source . StartActivity ( "execute-extensible-command" , s_activityKind , s_parentActivityContext ) ;
315
- var result = resolvedCommand . Execute ( ) ;
316
- _executionActivity ? . Dispose ( ) ;
317
-
318
- exitCode = result . ExitCode ;
319
- }
320
- catch ( CommandUnknownException e )
321
- {
322
- Reporter . Error . WriteLine ( e . Message . Red ( ) ) ;
323
- Reporter . Output . WriteLine ( e . InstructionMessage ) ;
324
- exitCode = 1 ;
325
- }
326
- }
327
-
328
- TelemetryClient . Dispose ( ) ;
329
- return exitCode ;
330
370
}
331
371
332
372
private static int AdjustExitCode ( ParseResult parseResult , int exitCode )
0 commit comments