/// <summary> /// Diagnostic event handler method for 'Microsoft.AspNetCore.Hosting.BeginRequest' event. This is from 1.XX runtime. /// </summary> public void OnBeginRequest(HttpContext httpContext, long timestamp) { if (this.client.IsEnabled() && this.aspNetCoreMajorVersion == AspNetCoreMajorVersion.One) { // It's possible to host multiple apps (ASP.NET Core or generic hosts) in the same process // Each of this apps has it's own HostingDiagnosticListener and corresponding Http listener. // We should ignore events for all of them except one if (!SubscriptionManager.IsActive(this)) { // AspNetCoreEventSource.Instance.NotActiveListenerNoTracking("Microsoft.AspNetCore.Hosting.BeginRequest", Activity.Current?.Id); return; } // 1.XX does not create Activity and SDK is responsible for creating Activity. var activity = new ActivityShim(ActivityCreatedByHostingDiagnosticListener); IHeaderDictionary requestHeaders = httpContext.Request.Headers; string originalParentId = null; string legacyRootId = null; // W3C-TraceParent if (ActivityShim.DefaultIdFormat == ActivityIdFormat.W3C && requestHeaders.TryGetValue(W3CConstants.TraceParentHeader, out StringValues traceParentValues) && traceParentValues != StringValues.Empty) { var parentTraceParent = StringUtilities.EnforceMaxLength(traceParentValues.First(), InjectionGuardConstants.TraceParentHeaderMaxLength); activity.SetParentId(parentTraceParent); originalParentId = parentTraceParent; ReadTraceState(requestHeaders, activity); } // Request-Id else if (requestHeaders.TryGetValue(RequestResponseHeaders.RequestIdHeader, out StringValues requestIdValues) && requestIdValues != StringValues.Empty) { originalParentId = StringUtilities.EnforceMaxLength(requestIdValues.First(), InjectionGuardConstants.RequestHeaderMaxLength); if (ActivityShim.DefaultIdFormat == ActivityIdFormat.W3C) { if (TryGetW3CCompatibleTraceId(originalParentId, out var traceId)) { activity.SetParentId(ActivityTraceId.CreateFromString(traceId), default(ActivitySpanId), ActivityTraceFlags.None); } else { // store rootIdFromOriginalParentId in custom Property legacyRootId = ExtractOperationIdFromRequestId(originalParentId); } } else { activity.SetParentId(originalParentId); } } // no headers else { // No need of doing anything. When Activity starts, it'll generate IDs in W3C or Hierarchical format as configured, } activity.Start(); ReadCorrelationContext(requestHeaders, activity); var requestTelemetry = this.InitializeRequestTelemetry(httpContext, activity, timestamp, legacyRootId); requestTelemetry.Context.Operation.ParentId = GetParentId(activity, originalParentId, requestTelemetry.Context.Operation.Id); this.AddAppIdToResponseIfRequired(httpContext, requestTelemetry); } }
/// <summary> /// Diagnostic event handler method for 'Microsoft.AspNetCore.Hosting.HttpRequestIn.Start' event. This is from 2.XX runtime. /// </summary> public void OnHttpRequestInStart(HttpContext httpContext) { if (this.client.IsEnabled()) { // It's possible to host multiple apps (ASP.NET Core or generic hosts) in the same process // Each of this apps has it's own HostingDiagnosticListener and corresponding Http listener. // We should ignore events for all of them except one if (!SubscriptionManager.IsActive(this)) { // AspNetCoreEventSource.Instance.NotActiveListenerNoTracking("Microsoft.AspNetCore.Hosting.HttpRequestIn.Start", Activity.Current?.Id); return; } if (Activity.Current == null) { // AspNetCoreEventSource.Instance.LogHostingDiagnosticListenerOnHttpRequestInStartActivityNull(); return; } var currentActivity = ActivityShim.Current; ActivityShim newActivity = default; string originalParentId = currentActivity.ParentId; string legacyRootId = null; bool traceParentPresent = false; var headers = httpContext.Request.Headers; // 3 possibilities when TelemetryConfiguration.EnableW3CCorrelation = true // 1. No incoming headers. originalParentId will be null. Simply use the Activity as such. // 2. Incoming Request-ID Headers. originalParentId will be request-id, but Activity ignores this for ID calculations. // If incoming ID is W3C compatible, ignore current Activity. Create new one with parent set to incoming W3C compatible rootid. // If incoming ID is not W3C compatible, we can use Activity as such, but need to store originalParentID in custom property 'legacyRootId' // 3. Incoming TraceParent header. // 3a - 2.XX Need to ignore current Activity, and create new from incoming W3C TraceParent header. // 3b - 3.XX Use Activity as such because 3.XX is W3C Aware. // Another 3 possibilities when TelemetryConfiguration.EnableW3CCorrelation = false // 1. No incoming headers. originalParentId will be null. Simply use the Activity as such. // 2. Incoming Request-ID Headers. originalParentId will be request-id, Activity uses this for ID calculations. // 3. Incoming TraceParent header. Will simply Ignore W3C headers, and Current Activity used as such. // Attempt to find parent from incoming W3C Headers which 2.XX Hosting is unaware of. if (this.aspNetCoreMajorVersion != AspNetCoreMajorVersion.Three && currentActivity.IdFormat == ActivityIdFormat.W3C && headers.TryGetValue(W3CConstants.TraceParentHeader, out StringValues traceParentValues) && traceParentValues != StringValues.Empty) { var parentTraceParent = StringUtilities.EnforceMaxLength( traceParentValues.First(), InjectionGuardConstants.TraceParentHeaderMaxLength); originalParentId = parentTraceParent; traceParentPresent = true; // AspNetCoreEventSource.Instance.HostingListenerInformational(this.aspNetCoreMajorVersion, "Retrieved trace parent from headers."); } // Scenario #1. No incoming correlation headers. if (originalParentId == null) { // Nothing to do here. // AspNetCoreEventSource.Instance.HostingListenerInformational(this.aspNetCoreMajorVersion, "OriginalParentId is null."); } else if (traceParentPresent) { // Scenario #3. W3C-TraceParent // We need to ignore the Activity created by Hosting, as it did not take W3CTraceParent into consideration. newActivity = new ActivityShim(ActivityCreatedByHostingDiagnosticListener); newActivity.SetParentId(originalParentId); // AspNetCoreEventSource.Instance.HostingListenerInformational(this.aspNetCoreMajorVersion, "Ignoring original Activity from Hosting to create new one using traceparent header retrieved by sdk."); // read and populate tracestate ReadTraceState(httpContext.Request.Headers, newActivity); } else if (this.aspNetCoreMajorVersion == AspNetCoreMajorVersion.Three && headers.ContainsKey(W3CConstants.TraceParentHeader)) { // AspNetCoreEventSource.Instance.HostingListenerInformational(this.aspNetCoreMajorVersion, "Incoming request has traceparent. Using Activity created from Hosting."); // scenario #3b Use Activity created by Hosting layer when W3C Headers Present. // but ignore parent if user disabled w3c. if (currentActivity.IdFormat != ActivityIdFormat.W3C) { originalParentId = null; } } else { // Scenario #2. RequestID if (currentActivity.IdFormat == ActivityIdFormat.W3C) { if (TryGetW3CCompatibleTraceId(originalParentId, out var traceId)) { newActivity = new ActivityShim(ActivityCreatedByHostingDiagnosticListener); newActivity.SetParentId(ActivityTraceId.CreateFromString(traceId), default(ActivitySpanId), ActivityTraceFlags.None); // AspNetCoreEventSource.Instance.HostingListenerInformational(this.aspNetCoreMajorVersion, "Ignoring original Activity from Hosting to create new one using w3c compatible request-id."); foreach (var bag in currentActivity.Baggage) { newActivity.AddBaggage(bag.Key, bag.Value); } } else { // store rootIdFromOriginalParentId in custom Property legacyRootId = ExtractOperationIdFromRequestId(originalParentId); // AspNetCoreEventSource.Instance.HostingListenerInformational(this.aspNetCoreMajorVersion, "Incoming Request-ID is not W3C Compatible, and hence will be ignored for ID generation, but stored in custom property legacy_rootID."); } } } if (!newActivity.IsDefault) { newActivity.Start(); currentActivity = newActivity; } // Read Correlation-Context is all scenarios irrespective of presence of either request-id or traceparent headers. ReadCorrelationContext(httpContext.Request.Headers, currentActivity); var requestTelemetry = this.InitializeRequestTelemetry(httpContext, currentActivity, Stopwatch.GetTimestamp(), legacyRootId); requestTelemetry.Context.Operation.ParentId = GetParentId(currentActivity, originalParentId, requestTelemetry.Context.Operation.Id); this.AddAppIdToResponseIfRequired(httpContext, requestTelemetry); } }