예제 #1
0
        /// <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);
            }
        }
예제 #2
0
        /// <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);
            }
        }