@@ -33,6 +33,7 @@ internal sealed class HostingApplicationDiagnostics
3333 private readonly HostingEventSource _eventSource ;
3434 private readonly HostingMetrics _metrics ;
3535 private readonly ILogger _logger ;
36+ private readonly bool _suppressActivityOpenTelemetryData ;
3637
3738 public HostingApplicationDiagnostics (
3839 ILogger logger ,
@@ -48,6 +49,7 @@ public HostingApplicationDiagnostics(
4849 _propagator = propagator ;
4950 _eventSource = eventSource ;
5051 _metrics = metrics ;
52+ _suppressActivityOpenTelemetryData = AppContext . TryGetSwitch ( "Microsoft.AspNetCore.Hosting.SuppressActivityOpenTelemetryData" , out var enabled ) && enabled ;
5153 }
5254
5355 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
@@ -88,9 +90,9 @@ public void BeginRequest(HttpContext httpContext, HostingApplication.Context con
8890 var diagnosticListenerActivityCreationEnabled = ( diagnosticListenerEnabled && _diagnosticListener . IsEnabled ( ActivityName , httpContext ) ) ;
8991 var loggingEnabled = _logger . IsEnabled ( LogLevel . Critical ) ;
9092
91- if ( loggingEnabled || diagnosticListenerActivityCreationEnabled || _activitySource . HasListeners ( ) )
93+ if ( ActivityCreator . IsActivityCreated ( _activitySource , loggingEnabled || diagnosticListenerActivityCreationEnabled ) )
9294 {
93- context . Activity = StartActivity ( httpContext , loggingEnabled , diagnosticListenerActivityCreationEnabled , out var hasDiagnosticListener ) ;
95+ context . Activity = StartActivity ( httpContext , loggingEnabled || diagnosticListenerActivityCreationEnabled , out var hasDiagnosticListener ) ;
9496 context . HasDiagnosticListener = hasDiagnosticListener ;
9597
9698 if ( context . Activity != null )
@@ -385,29 +387,17 @@ private void RecordRequestStartMetrics(HttpContext httpContext)
385387 }
386388
387389 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
388- private Activity ? StartActivity ( HttpContext httpContext , bool loggingEnabled , bool diagnosticListenerActivityCreationEnabled , out bool hasDiagnosticListener )
390+ private Activity ? StartActivity ( HttpContext httpContext , bool diagnosticsOrLoggingEnabled , out bool hasDiagnosticListener )
389391 {
392+ // StartActivity is only called if an Activity is already verified to be created.
393+ Debug . Assert ( ActivityCreator . IsActivityCreated ( _activitySource , diagnosticsOrLoggingEnabled ) ,
394+ "Activity should only be created if diagnostics or logging is enabled." ) ;
395+
390396 hasDiagnosticListener = false ;
391397
392- var tagsForCreation = new TagList ( ) ;
393- if ( _activitySource . HasListeners ( ) && httpContext . Request . Host . HasValue )
394- {
395- var host = httpContext . Request . Host . Host ;
396- var port = httpContext . Request . Host . Port ;
397- tagsForCreation . Add ( "server.address" , host ) ;
398- if ( port is not null )
399- {
400- tagsForCreation . Add ( "server.port" , port ) ;
401- }
402- else if ( string . Equals ( "https" , httpContext . Request . Scheme , StringComparison . OrdinalIgnoreCase ) )
403- {
404- tagsForCreation . Add ( "server.port" , 443 ) ;
405- }
406- else if ( string . Equals ( "http" , httpContext . Request . Scheme , StringComparison . OrdinalIgnoreCase ) )
407- {
408- tagsForCreation . Add ( "server.port" , 80 ) ;
409- }
410- }
398+ var initializeTags = ! _suppressActivityOpenTelemetryData
399+ ? CreateInitializeActivityTags ( httpContext )
400+ : ( TagList ? ) null ;
411401
412402 var headers = httpContext . Request . Headers ;
413403 var activity = ActivityCreator . CreateFromRemote (
@@ -422,9 +412,9 @@ private void RecordRequestStartMetrics(HttpContext httpContext)
422412 } ,
423413 ActivityName ,
424414 ActivityKind . Server ,
425- tags : tagsForCreation ,
415+ tags : initializeTags ,
426416 links : null ,
427- loggingEnabled || diagnosticListenerActivityCreationEnabled ) ;
417+ diagnosticsOrLoggingEnabled ) ;
428418 if ( activity is null )
429419 {
430420 return null ;
@@ -445,6 +435,47 @@ private void RecordRequestStartMetrics(HttpContext httpContext)
445435 return activity ;
446436 }
447437
438+ private static TagList CreateInitializeActivityTags ( HttpContext httpContext )
439+ {
440+ // The tags here are set when the activity is created. They can be used in sampling decisions.
441+ // Most values in semantic conventions that are present at creation are specified:
442+ // https://github.com/open-telemetry/semantic-conventions/blob/27735ccca3746d7bb7fa061dfb73d93bcbae2b6e/docs/http/http-spans.md#L581-L592
443+ // Missing values recommended by the spec are:
444+ // - url.query (need configuration around redaction to do properly)
445+ // - http.request.header.<key>
446+
447+ var request = httpContext . Request ;
448+ var creationTags = new TagList ( ) ;
449+
450+ if ( request . Host . HasValue )
451+ {
452+ creationTags . Add ( HostingTelemetryHelpers . AttributeServerAddress , request . Host . Host ) ;
453+
454+ if ( HostingTelemetryHelpers . TryGetServerPort ( request . Host , request . Scheme , out var port ) )
455+ {
456+ creationTags . Add ( HostingTelemetryHelpers . AttributeServerPort , port ) ;
457+ }
458+ }
459+
460+ HostingTelemetryHelpers . SetActivityHttpMethodTags ( ref creationTags , request . Method ) ;
461+
462+ if ( request . Headers . TryGetValue ( "User-Agent" , out var values ) )
463+ {
464+ var userAgent = values . Count > 0 ? values [ 0 ] : null ;
465+ if ( ! string . IsNullOrEmpty ( userAgent ) )
466+ {
467+ creationTags . Add ( HostingTelemetryHelpers . AttributeUserAgentOriginal , userAgent ) ;
468+ }
469+ }
470+
471+ creationTags . Add ( HostingTelemetryHelpers . AttributeUrlScheme , request . Scheme ) ;
472+
473+ var path = ( request . PathBase . HasValue || request . Path . HasValue ) ? ( request . PathBase + request . Path ) . ToString ( ) : "/" ;
474+ creationTags . Add ( HostingTelemetryHelpers . AttributeUrlPath , path ) ;
475+
476+ return creationTags ;
477+ }
478+
448479 [ MethodImpl ( MethodImplOptions . NoInlining ) ]
449480 private void StopActivity ( HttpContext httpContext , Activity activity , bool hasDiagnosticListener )
450481 {
0 commit comments