private void ValidateTelemetry <T>(T telemetry, Activity activity, bool isW3C = true) where T : OperationTelemetry
        {
            Assert.AreEqual(activity.OperationName, telemetry.Name);
            if (isW3C)
            {
                Assert.AreEqual(W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString()), telemetry.Id);
            }
            else
            {
                Assert.AreEqual(activity.Id, telemetry.Id);
            }

            Assert.AreEqual(activity.ParentId, telemetry.Context.Operation.ParentId);
            Assert.AreEqual(activity.RootId, telemetry.Context.Operation.Id);

            foreach (var baggage in activity.Baggage)
            {
                Assert.IsTrue(telemetry.Properties.ContainsKey(baggage.Key));
                Assert.AreEqual(baggage.Value, telemetry.Properties[baggage.Key]);
            }

            foreach (var tag in activity.Tags)
            {
                Assert.IsTrue(telemetry.Properties.ContainsKey(tag.Key));
                Assert.AreEqual(tag.Value, telemetry.Properties[tag.Key]);
            }
        }
        protected void SetCommonProperties(string eventName, object eventPayload, Activity activity, OperationTelemetry telemetry)
        {
            telemetry.Name      = this.GetOperationName(eventName, eventPayload, activity);
            telemetry.Duration  = activity.Duration;
            telemetry.Timestamp = activity.StartTimeUtc;

            if (activity.IdFormat == ActivityIdFormat.W3C)
            {
                var traceId = activity.TraceId.ToHexString();
                telemetry.Context.Operation.Id = traceId;

                if (string.IsNullOrEmpty(telemetry.Context.Operation.ParentId))
                {
                    if (activity.ParentSpanId != default)
                    {
                        telemetry.Context.Operation.ParentId = W3CUtilities.FormatTelemetryId(traceId, activity.ParentSpanId.ToHexString());
                    }
                    else if (!string.IsNullOrEmpty(activity.ParentId))
                    {
                        // W3C activity with non-W3C parent must keep parentId
                        telemetry.Context.Operation.ParentId = activity.ParentId;
                    }
                }

                telemetry.Id = W3CUtilities.FormatTelemetryId(traceId, activity.SpanId.ToHexString());

                // TODO[tracestate]: remove, this is done in base SDK
                if (!string.IsNullOrEmpty(activity.TraceStateString) && !telemetry.Properties.ContainsKey(W3CConstants.TracestatePropertyKey))
                {
                    telemetry.Properties.Add(W3CConstants.TracestatePropertyKey, activity.TraceStateString);
                }
            }
            else
            {
                telemetry.Id = activity.Id;
                telemetry.Context.Operation.Id       = activity.RootId;
                telemetry.Context.Operation.ParentId = activity.ParentId;
            }

            foreach (var item in activity.Tags)
            {
                if (!telemetry.Properties.ContainsKey(item.Key))
                {
                    telemetry.Properties[item.Key] = item.Value;
                }
            }

            foreach (var item in activity.Baggage)
            {
                if (!telemetry.Properties.ContainsKey(item.Key))
                {
                    telemetry.Properties[item.Key] = item.Value;
                }
            }

            telemetry.Success = this.IsOperationSuccessful(eventName, eventPayload, activity);
        }
Beispiel #3
0
 private static void InjectBackCompatibleRequestId(Activity currentActivity, HttpRequestHeaders requestHeaders)
 {
     if (!requestHeaders.Contains(RequestResponseHeaders.RequestIdHeader))
     {
         requestHeaders.Add(RequestResponseHeaders.RequestIdHeader,
                            W3CUtilities.FormatTelemetryId(currentActivity.TraceId.ToHexString(),
                                                           currentActivity.SpanId.ToHexString()));
     }
 }
        private void ValidateTelemetry <T>(T telemetry, Activity activity, bool isW3C = true, string legacyParentId = null) where T : OperationTelemetry
        {
            Assert.AreEqual(activity.OperationName, telemetry.Name);
            Assert.AreEqual(
                isW3C
                    ? W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString())
                    : activity.Id, telemetry.Id);

            if (isW3C)
            {
                if (activity.ParentSpanId != default && activity.ParentSpanId.ToHexString() != "0000000000000000")
                {
                    Assert.AreEqual(
                        W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(),
                                                       activity.ParentSpanId.ToHexString()), telemetry.Context.Operation.ParentId);
                }
                else
                {
                    Assert.AreEqual(legacyParentId, telemetry.Context.Operation.ParentId);
                }

                Assert.AreEqual(activity.TraceId.ToHexString(), telemetry.Context.Operation.Id);
            }
            else
            {
                Assert.AreEqual(activity.ParentId, telemetry.Context.Operation.ParentId);
                Assert.AreEqual(activity.RootId, telemetry.Context.Operation.Id);
            }

            foreach (var baggage in activity.Baggage)
            {
                Assert.IsTrue(telemetry.Properties.ContainsKey(baggage.Key));
                Assert.AreEqual(baggage.Value, telemetry.Properties[baggage.Key]);
            }

            foreach (var tag in activity.Tags)
            {
                Assert.IsTrue(telemetry.Properties.ContainsKey(tag.Key));
                Assert.AreEqual(tag.Value, telemetry.Properties[tag.Key]);
            }

            if (activity.TraceStateString != null)
            {
                Assert.IsTrue(telemetry.Properties.TryGetValue("tracestate", out var tracestate));
                Assert.AreEqual(activity.TraceStateString, tracestate);
            }
            else
            {
                Assert.IsFalse(telemetry.Properties.ContainsKey("tracestate"));
            }

            Assert.AreEqual(activity.Recorded ? SamplingDecision.SampledIn : SamplingDecision.None, (telemetry as ISupportAdvancedSampling).ProactiveSamplingDecision);
        }
        private static T ActivityToTelemetry <T>(Activity activity) where T : OperationTelemetry, new()
        {
            Debug.Assert(activity.Id != null, "Activity must be started prior calling this method");

            var telemetry = new T {
                Name = activity.OperationName
            };

            OperationContext operationContext = telemetry.Context.Operation;

            operationContext.Name = activity.GetOperationName();

            if (activity.IdFormat == ActivityIdFormat.W3C)
            {
                operationContext.Id = activity.TraceId.ToHexString();
                telemetry.Id        = W3CUtilities.FormatTelemetryId(operationContext.Id, activity.SpanId.ToHexString());

                if (string.IsNullOrEmpty(operationContext.ParentId) && activity.ParentSpanId != default)
                {
                    operationContext.ParentId =
                        W3CUtilities.FormatTelemetryId(operationContext.Id, activity.ParentSpanId.ToHexString());
                }
            }
            else
            {
                operationContext.Id       = activity.RootId;
                operationContext.ParentId = activity.ParentId;
                telemetry.Id = activity.Id;
            }

            foreach (var item in activity.Baggage)
            {
                if (!telemetry.Properties.ContainsKey(item.Key))
                {
                    telemetry.Properties.Add(item);
                }
            }

            foreach (var item in activity.Tags)
            {
                if (!telemetry.Properties.ContainsKey(item.Key))
                {
                    telemetry.Properties.Add(item);
                }
            }

            return(telemetry);
        }
Beispiel #6
0
        public void InitializesTelemetryFromParentActivityW3C(string beforeEventName, string afterEventName)
        {
            var activity = new Activity("Current").AddBaggage("Stuff", "123");

            activity.Start();

            var operationId   = Guid.NewGuid();
            var sqlConnection = new SqlConnection(TestConnectionString);
            var sqlCommand    = sqlConnection.CreateCommand();

            sqlCommand.CommandText = "select * from orders";

            var beforeExecuteEventData = new
            {
                OperationId = operationId,
                Command     = sqlCommand,
                Timestamp   = (long?)1000000L
            };

            this.fakeSqlClientDiagnosticSource.Write(
                beforeEventName,
                beforeExecuteEventData);

            var afterExecuteEventData = new
            {
                OperationId = operationId,
                Command     = sqlCommand,
                Timestamp   = 2000000L
            };

            this.fakeSqlClientDiagnosticSource.Write(
                afterEventName,
                afterExecuteEventData);

            var dependencyTelemetry = (DependencyTelemetry)this.sendItems.Single();

            Assert.Equal(activity.TraceId.ToHexString(), dependencyTelemetry.Context.Operation.Id);
            Assert.Equal(W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString()), dependencyTelemetry.Context.Operation.ParentId);
            Assert.Equal("123", dependencyTelemetry.Properties["Stuff"]);
        }
Beispiel #7
0
        public void InitializePopulatesOperationContextFromActivity()
        {
            // Arrange
            Activity activity = new Activity("somename");

            activity.Start();
            var telemetry           = new DependencyTelemetry();
            var originalTelemetryId = telemetry.Id;

            // Act
            var initializer = new OperationCorrelationTelemetryInitializer();

            initializer.Initialize(telemetry);

            // Validate
            Assert.AreEqual(activity.TraceId.ToHexString(), telemetry.Context.Operation.Id, "OperationCorrelationTelemetryInitializer is expected to populate OperationID from Activity");
            Assert.AreEqual(W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString()),
                            telemetry.Context.Operation.ParentId,
                            "OperationCorrelationTelemetryInitializer is expected to populate Operation ParentID as |traceID.SpanId. from Activity");
            Assert.AreEqual(originalTelemetryId, telemetry.Id, "OperationCorrelationTelemetryInitializer is not expected to modify Telemetry ID");
            activity.Stop();
        }
Beispiel #8
0
        public void StartDependencyTrackingHandlesMultipleContextStoresInCurrentActivityW3C()
        {
            var operation       = this.telemetryClient.StartOperation <DependencyTelemetry>("OperationName") as OperationHolder <DependencyTelemetry>;
            var currentActivity = Activity.Current;

            Assert.AreEqual(operation.Telemetry.Id, W3CUtilities.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()));
            Assert.AreEqual(operation.Telemetry.Context.Operation.Name, this.GetOperationName(currentActivity));

            var childOperation = this.telemetryClient.StartOperation <DependencyTelemetry>("OperationName") as OperationHolder <DependencyTelemetry>;
            var childActivity  = Activity.Current;

            Assert.AreEqual(childOperation.Telemetry.Id, W3CUtilities.FormatTelemetryId(childActivity.TraceId.ToHexString(), childActivity.SpanId.ToHexString()));
            Assert.AreEqual(childOperation.Telemetry.Context.Operation.Name, this.GetOperationName(currentActivity));

            Assert.IsNull(currentActivity.Parent);
            Assert.AreEqual(currentActivity, childActivity.Parent);

            this.telemetryClient.StopOperation(childOperation);
            Assert.AreEqual(currentActivity, Activity.Current);
            this.telemetryClient.StopOperation(operation);
            Assert.IsNull(Activity.Current);
        }
Beispiel #9
0
        public void InitializeWithActivityWinsOverCallContext()
        {
            CallContextHelpers.SaveOperationContext(new OperationContextForCallContext {
                RootOperationId = "callContextRoot"
            });
            var currentActivity = new Activity("test");

            currentActivity.AddTag("OperationName", "operation");
            currentActivity.AddBaggage("k1", "v1");
            currentActivity.Start();
            var telemetry = new RequestTelemetry();

            (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry);

            Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id);
            Assert.AreEqual(W3CUtilities.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()), telemetry.Context.Operation.ParentId);

            Assert.AreEqual("operation", telemetry.Context.Operation.Name);
            Assert.AreEqual(1, telemetry.Properties.Count);
            Assert.AreEqual("v1", telemetry.Properties["k1"]);
            currentActivity.Stop();
        }
        private static void InitializeTelemetry(DependencyTelemetry telemetry, Guid operationId, long timestamp)
        {
            telemetry.Start(timestamp);

            var activity = Activity.Current;

            if (activity != null)
            {
                // SQL Client does NOT create Activity.
                // We initialize SQL dependency using Activity from incoming Request
                // and it is the parent of the SQL dependency

                if (activity.IdFormat == ActivityIdFormat.W3C)
                {
                    var traceId = activity.TraceId.ToHexString();
                    telemetry.Context.Operation.Id       = traceId;
                    telemetry.Context.Operation.ParentId = W3CUtilities.FormatTelemetryId(traceId, activity.SpanId.ToHexString());
                }
                else
                {
                    telemetry.Context.Operation.Id       = activity.RootId;
                    telemetry.Context.Operation.ParentId = activity.Id;
                }

                foreach (var item in activity.Baggage)
                {
                    if (!telemetry.Properties.ContainsKey(item.Key))
                    {
                        telemetry.Properties[item.Key] = item.Value;
                    }
                }
            }
            else
            {
                telemetry.Context.Operation.Id = operationId.ToStringInvariant("N");
            }
        }
Beispiel #11
0
        public void InitializeSetsBaggage()
        {
            var currentActivity = new Activity("test");

            currentActivity.AddTag("OperationName", "operation");
            currentActivity.AddBaggage("k1", "v1");
            currentActivity.AddBaggage("k2", "v2");
            currentActivity.AddBaggage("existingkey", "exitingvalue");
            currentActivity.Start();
            var telemetry = new RequestTelemetry();

            telemetry.Properties.Add("existingkey", "exitingvalue");
            (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry);

            Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id);
            Assert.AreEqual(W3CUtilities.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()), telemetry.Context.Operation.ParentId);
            Assert.AreEqual("operation", telemetry.Context.Operation.Name);

            Assert.AreEqual(3, telemetry.Properties.Count);
            Assert.AreEqual("v1", telemetry.Properties["k1"]);
            Assert.AreEqual("v2", telemetry.Properties["k2"]);
            Assert.AreEqual("exitingvalue", telemetry.Properties["existingkey"], "OperationCorrelationTelemetryInitializer should not override existing telemetry property bag");
            currentActivity.Stop();
        }
Beispiel #12
0
        private void InjectRequestHeaders(HttpRequestMessage request, string instrumentationKey)
        {
            try
            {
                HttpRequestHeaders requestHeaders = request.Headers;
                if (requestHeaders != null && this.setComponentCorrelationHttpHeaders && !this.correlationDomainExclusionList.Contains(request.RequestUri.Host))
                {
                    string sourceApplicationId = null;
                    try
                    {
                        if (!string.IsNullOrEmpty(instrumentationKey) &&
                            !HttpHeadersUtilities.ContainsRequestContextKeyValue(requestHeaders, RequestResponseHeaders.RequestContextCorrelationSourceKey) &&
                            (this.configuration.ApplicationIdProvider?.TryGetApplicationId(instrumentationKey, out sourceApplicationId) ?? false))
                        {
                            HttpHeadersUtilities.SetRequestContextKeyValue(requestHeaders, RequestResponseHeaders.RequestContextCorrelationSourceKey, sourceApplicationId);
                        }
                    }
                    catch (Exception e)
                    {
                        AppMapCorrelationEventSource.Log.UnknownError(ExceptionUtilities.GetExceptionDetailString(e));
                    }

                    var currentActivity = Activity.Current;

                    switch (this.httpInstrumentationVersion)
                    {
                    case HttpInstrumentationVersion.V1:
                        // HttpClient does not add any headers
                        // add W3C or Request-Id depending on Activity format
                        // add correlation-context anyway
                        if (currentActivity.IdFormat == ActivityIdFormat.W3C)
                        {
                            InjectW3CHeaders(currentActivity, requestHeaders);
                            if (this.injectRequestIdInW3CMode)
                            {
                                InjectBackCompatibleRequestId(currentActivity, requestHeaders);
                            }
                        }
                        else
                        {
                            if (!requestHeaders.Contains(RequestResponseHeaders.RequestIdHeader))
                            {
                                requestHeaders.Add(RequestResponseHeaders.RequestIdHeader, currentActivity.Id);
                            }
                        }

                        InjectCorrelationContext(requestHeaders, currentActivity);
                        break;

                    case HttpInstrumentationVersion.V2:
                        // On V2, HttpClient adds Request-Id and Correlation-Context
                        // but not W3C
                        if (currentActivity.IdFormat == ActivityIdFormat.W3C)
                        {
                            // we are going to add W3C and Request-Id (in W3C-compatible format)
                            // as a result HttpClient will not add Request-Id AND Correlation-Context
                            InjectW3CHeaders(currentActivity, requestHeaders);
                            if (this.injectRequestIdInW3CMode)
                            {
                                InjectBackCompatibleRequestId(currentActivity, requestHeaders);
                            }

                            InjectCorrelationContext(requestHeaders, currentActivity);
                        }

                        break;

                    case HttpInstrumentationVersion.V3:
                        // on V3, HttpClient adds either W3C or Request-Id depending on Activity format
                        // and adds Correlation-Context
                        if (currentActivity.IdFormat == ActivityIdFormat.W3C && this.injectRequestIdInW3CMode)
                        {
                            // we are going to override Request-Id to be in W3C compatible mode
                            InjectBackCompatibleRequestId(currentActivity, requestHeaders);
                        }

                        break;
                    }

                    if (this.injectLegacyHeaders)
                    {
                        // Add the root ID (Activity.RootId works with W3C and Hierarchical format)
                        string rootId = currentActivity.RootId;
                        if (!string.IsNullOrEmpty(rootId) && !requestHeaders.Contains(RequestResponseHeaders.StandardRootIdHeader))
                        {
                            requestHeaders.Add(RequestResponseHeaders.StandardRootIdHeader, rootId);
                        }

                        // Add the parent ID
                        string parentId = currentActivity.IdFormat == ActivityIdFormat.W3C ?
                                          W3CUtilities.FormatTelemetryId(rootId, currentActivity.SpanId.ToHexString()) :
                                          currentActivity.Id;

                        if (!string.IsNullOrEmpty(parentId) && !requestHeaders.Contains(RequestResponseHeaders.StandardParentIdHeader))
                        {
                            requestHeaders.Add(RequestResponseHeaders.StandardParentIdHeader, parentId);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                AppMapCorrelationEventSource.Log.UnknownError(ExceptionUtilities.GetExceptionDetailString(e));
            }
        }
Beispiel #13
0
        /// <summary>
        /// The function that needs to be called before sending a request to the server. Creates and initializes dependency telemetry item.
        /// </summary>
        /// <param name="telemetryClient">Telemetry client object to initialize the context of the telemetry item.</param>
        internal static DependencyTelemetry BeginTracking(TelemetryClient telemetryClient)
        {
            var telemetry = new DependencyTelemetry();

            telemetry.Start();
            Activity activity;
            Activity currentActivity = Activity.Current;

            // On .NET46 without profiler, outgoing requests are instrumented with reflection hook in DiagnosticSource
            //// see https://github.com/dotnet/corefx/blob/master/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/HttpHandlerDiagnosticListener.cs
            // it creates an Activity and injects standard 'Request-Id' and 'Correlation-Context' headers itself, so we should not start Activity in this case
            if (currentActivity != null && currentActivity.OperationName == "System.Net.Http.Desktop.HttpRequestOut")
            {
                activity = currentActivity;
            }
            else
            {
                // Every operation must have its own Activity
                // if dependency is tracked with profiler of event source, we need to generate a proper Id for it
                // in case of HTTP it will be propagated into the request header.
                // So, we will create a new Activity for the dependency, just to generate an Id.
                activity = new Activity(DependencyActivityName);
                activity.Start();
            }

            // OperationCorrelationTelemetryInitializer will initialize telemetry as a child of current activity:
            // But we need to initialize dependency telemetry from the current Activity:
            // Activity was created for this dependency in the Http Desktop DiagnosticSource
            if (activity.IdFormat == ActivityIdFormat.W3C)
            {
                var context = telemetry.Context;
                context.Operation.Id = activity.TraceId.ToHexString();

                if (activity.Parent != null || activity.ParentSpanId != default)
                {
                    context.Operation.ParentId = W3CUtilities.FormatTelemetryId(context.Operation.Id, activity.ParentSpanId.ToHexString());
                }

                telemetry.Id = W3CUtilities.FormatTelemetryId(context.Operation.Id, activity.SpanId.ToHexString());

                // TODO[tracestate]: remove, this is done in base SDK
                if (activity.TraceStateString != null && !telemetry.Properties.ContainsKey(W3CConstants.TracestatePropertyKey))
                {
                    telemetry.Properties.Add(W3CConstants.TracestatePropertyKey, activity.TraceStateString);
                }
            }
            else
            {
                var context = telemetry.Context;
                context.Operation.Id       = activity.RootId;
                context.Operation.ParentId = activity.ParentId;
                telemetry.Id = activity.Id;
            }

            foreach (var item in activity.Baggage)
            {
                if (!telemetry.Properties.ContainsKey(item.Key))
                {
                    telemetry.Properties.Add(item);
                }
            }

            telemetryClient.Initialize(telemetry);
            PretendProfilerIsAttached = false;
            return(telemetry);
        }
Beispiel #14
0
        //// netcoreapp 2.0 event

        /// <summary>
        /// Handler for Activity stop event (response is received for the outgoing request).
        /// </summary>
        internal void OnActivityStop(HttpResponseMessage response, HttpRequestMessage request, TaskStatus requestTaskStatus)
        {
            // Even though we have the IsEnabled filter to reject ApplicationInsights URLs before any events are fired, if there
            // are multiple subscribers and one subscriber returns true to IsEnabled then all subscribers will receive the event.
            if (this.applicationInsightsUrlFilter.IsApplicationInsightsUrl(request.RequestUri))
            {
                return;
            }

            Activity currentActivity = Activity.Current;

            if (currentActivity == null)
            {
                DependencyCollectorEventSource.Log.CurrentActivityIsNull(HttpOutStopEventName);
                return;
            }

            if (Activity.DefaultIdFormat == ActivityIdFormat.W3C &&
                request.Headers.TryGetValues(W3C.W3CConstants.TraceParentHeader, out var parents) &&
                parents.FirstOrDefault() != currentActivity.Id)
            {
                DependencyCollectorEventSource.Log.HttpRequestAlreadyInstrumented();
                return;
            }

            DependencyCollectorEventSource.Log.HttpCoreDiagnosticSourceListenerStop(currentActivity.Id);

            Uri requestUri   = request.RequestUri;
            var resourceName = request.Method.Method + " " + requestUri.AbsolutePath;

            DependencyTelemetry telemetry = new DependencyTelemetry();

            telemetry.SetOperationDetail(RemoteDependencyConstants.HttpRequestOperationDetailName, request);

            // properly fill dependency telemetry operation context: OperationCorrelationTelemetryInitializer initializes child telemetry
            if (currentActivity.IdFormat == ActivityIdFormat.W3C)
            {
                var traceId = currentActivity.TraceId.ToHexString();
                telemetry.Context.Operation.Id = traceId;
                if (currentActivity.ParentSpanId != default)
                {
                    telemetry.Context.Operation.ParentId = W3CUtilities.FormatTelemetryId(traceId, currentActivity.ParentSpanId.ToHexString());
                }

                telemetry.Id = W3CUtilities.FormatTelemetryId(traceId, currentActivity.SpanId.ToHexString());
            }
            else
            {
                telemetry.Context.Operation.Id       = currentActivity.RootId;
                telemetry.Context.Operation.ParentId = currentActivity.ParentId;
                telemetry.Id = currentActivity.Id;
            }

            foreach (var item in currentActivity.Baggage)
            {
                if (!telemetry.Properties.ContainsKey(item.Key))
                {
                    telemetry.Properties[item.Key] = item.Value;
                }
            }

            // TODO[tracestate]: remove, this is done in base SDK
            if (!string.IsNullOrEmpty(currentActivity.TraceStateString) && !telemetry.Properties.ContainsKey(W3CConstants.TracestatePropertyKey))
            {
                telemetry.Properties.Add(W3CConstants.TracestatePropertyKey, currentActivity.TraceStateString);
            }

            this.client.Initialize(telemetry);

            // If we started auxiliary Activity before to override the Id with W3C compatible one,
            // now it's time to set end time on it
            if (currentActivity.Duration == TimeSpan.Zero)
            {
                currentActivity.SetEndTime(DateTime.UtcNow);
            }

            telemetry.Timestamp = currentActivity.StartTimeUtc;
            telemetry.Name      = resourceName;
            telemetry.Target    = DependencyTargetNameHelper.GetDependencyTargetName(requestUri);
            telemetry.Type      = RemoteDependencyConstants.HTTP;
            telemetry.Data      = requestUri.OriginalString;
            telemetry.Duration  = currentActivity.Duration;
            if (response != null)
            {
                this.ParseResponse(response, telemetry);
                telemetry.SetOperationDetail(RemoteDependencyConstants.HttpResponseOperationDetailName, response);
            }
            else
            {
                if (this.pendingExceptions.TryRemove(currentActivity.Id, out Exception exception))
                {
                    telemetry.Properties[RemoteDependencyConstants.DependencyErrorPropertyKey] = exception.GetBaseException().Message;
                }

                telemetry.ResultCode = requestTaskStatus.ToString();
                telemetry.Success    = false;
            }

            this.client.TrackDependency(telemetry);
        }
        /// <summary>
        /// Computes the duration and tracks the respective telemetry item on dispose.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing && !this.isDisposed)
            {
                // We need to compare the operation id and name of telemetry with operation id and name of current call context before tracking it
                // to make sure that the customer is tracking the right telemetry.
                lock (this)
                {
                    if (!this.isDisposed)
                    {
                        var operationTelemetry = this.Telemetry;
                        operationTelemetry.Stop();

                        bool isActivityAvailable = false;
                        isActivityAvailable = ActivityExtensions.TryRun(() =>
                        {
                            var currentActivity = Activity.Current;
                            if (currentActivity == null ||
                                (Activity.DefaultIdFormat != ActivityIdFormat.W3C && operationTelemetry.Id != currentActivity.Id) ||
                                (Activity.DefaultIdFormat == ActivityIdFormat.W3C && operationTelemetry.Id != W3CUtilities.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString())))
                            {
                                // W3COperationCorrelationTelemetryInitializer changes Id
                                // but keeps an original one in 'ai_legacyRequestId' property

                                if (!operationTelemetry.Properties.TryGetValue("ai_legacyRequestId", out var legacyId) ||
                                    legacyId != currentActivity?.Id)
                                {
                                    // this is for internal error reporting
                                    CoreEventSource.Log.InvalidOperationToStopError();

                                    // this are details with unique ids for debugging
                                    CoreEventSource.Log.InvalidOperationToStopDetails(
                                        string.Format(
                                            CultureInfo.InvariantCulture,
                                            "Telemetry Id '{0}' does not match current Activity '{1}'",
                                            operationTelemetry.Id,
                                            currentActivity?.Id));

                                    return;
                                }
                            }

                            this.telemetryClient.Track(operationTelemetry);

                            currentActivity?.Stop();

                            if (this.originalActivity != null &&
                                Activity.Current != this.originalActivity &&
                                this.originalActivity is Activity activity)
                            {
                                Activity.Current = activity;
                            }
                        });

                        if (!isActivityAvailable)
                        {
                            var currentOperationContext = CallContextHelpers.GetCurrentOperationContext();
                            if (currentOperationContext == null || operationTelemetry.Id != currentOperationContext.ParentOperationId)
                            {
                                // this is for internal error reporting
                                CoreEventSource.Log.InvalidOperationToStopError();

                                // this are details with unique ids for debugging
                                CoreEventSource.Log.InvalidOperationToStopDetails(
                                    string.Format(
                                        CultureInfo.InvariantCulture,
                                        "Telemetry Id '{0}' does not match current Activity '{1}'",
                                        operationTelemetry.Id,
                                        currentOperationContext?.ParentOperationId));
                                return;
                            }

                            this.telemetryClient.Track(operationTelemetry);

                            CallContextHelpers.RestoreOperationContext(this.ParentContext);
                        }
                    }

                    this.isDisposed = true;
                }
            }
        }
        internal static RequestTelemetry CreateRequestTelemetryPrivate(
            this HttpContext platformContext)
        {
            if (platformContext == null)
            {
                throw new ArgumentNullException(nameof(platformContext));
            }

            var currentActivity = Activity.Current;

            var    result         = new RequestTelemetry();
            var    requestContext = result.Context.Operation;
            string legacyParentId = null;
            string legacyRootId   = null;

            var headers = platformContext.Request.Unvalidated.Headers;

            if (currentActivity == null)
            {
                // if there was no BeginRequest, ASP.NET HttpModule did not have a chance to set current activity yet
                // this could happen if ASP.NET TelemetryCorrelation module is not the first in the pipeline
                // and some module before it tracks telemetry.
                // The ASP.NET module will be invoked later with proper correlation ids.
                // But we only get one chance to create request telemetry and we have to create it when method is called to avoid breaking changes
                // The correlation will be BROKEN anyway as telemetry reported before ASP.NET TelemetryCorrelation HttpModule is called
                // will not be correlated  properly to telemetry reported within the request
                // Here we simply maintaining backward compatibility with this behavior...

                currentActivity = new Activity(ActivityHelpers.RequestActivityItemName);
                if (!currentActivity.Extract(headers))
                {
                    if (ActivityHelpers.ParentOperationIdHeaderName != null &&
                        ActivityHelpers.RootOperationIdHeaderName != null)
                    {
                        legacyRootId = StringUtilities.EnforceMaxLength(platformContext.Request.UnvalidatedGetHeader(ActivityHelpers.RootOperationIdHeaderName),
                                                                        InjectionGuardConstants.RequestHeaderMaxLength);
                        legacyParentId = StringUtilities.EnforceMaxLength(
                            platformContext.Request.UnvalidatedGetHeader(ActivityHelpers.ParentOperationIdHeaderName),
                            InjectionGuardConstants.RequestHeaderMaxLength);
                        currentActivity.SetParentId(legacyRootId);
                    }

                    headers.ReadActivityBaggage(currentActivity);
                }

                currentActivity.Start();
            }

            if (currentActivity.IdFormat == ActivityIdFormat.W3C &&
                currentActivity.ParentId != null &&
                !currentActivity.ParentId.StartsWith("00-", StringComparison.Ordinal))
            {
                if (W3CUtilities.TryGetTraceId(currentActivity.ParentId, out var traceId))
                {
                    legacyParentId = currentActivity.ParentId;

                    currentActivity = CreateSubstituteActivityFromCompatibleRootId(currentActivity, traceId);
                }
                else
                {
                    legacyRootId   = W3CUtilities.GetRootId(currentActivity.ParentId);
                    legacyParentId = legacyParentId ?? GetLegacyParentId(currentActivity.ParentId, platformContext.Request);
                }
            }
            else if (currentActivity.IdFormat == ActivityIdFormat.Hierarchical &&
                     currentActivity.ParentId != null)
            {
                legacyParentId = GetLegacyParentId(currentActivity.ParentId, platformContext.Request);
            }

            if (currentActivity.IdFormat == ActivityIdFormat.W3C)
            {
                // we have Activity.Current, we need to properly initialize request telemetry and store it in HttpContext
                requestContext.Id = currentActivity.TraceId.ToHexString();

                if (currentActivity.ParentSpanId != default && legacyParentId == null)
                {
                    requestContext.ParentId = W3CUtilities.FormatTelemetryId(requestContext.Id, currentActivity.ParentSpanId.ToHexString());
                }
                else
                {
                    requestContext.ParentId = legacyParentId;
                    if (legacyRootId != null)
                    {
                        result.Properties[W3CConstants.LegacyRootPropertyIdKey] = legacyRootId;
                    }
                }

                // TODO[tracestate]: remove, this is done in base SDK
                if (!string.IsNullOrEmpty(currentActivity.TraceStateString))
                {
                    result.Properties[W3CConstants.TracestatePropertyKey] = currentActivity.TraceStateString;
                }

                result.Id = W3CUtilities.FormatTelemetryId(requestContext.Id, currentActivity.SpanId.ToHexString());
            }
            else
            {
                // we have Activity.Current, we need to properly initialize request telemetry and store it in HttpContext
                requestContext.Id       = currentActivity.RootId;
                requestContext.ParentId = legacyParentId ?? currentActivity.ParentId;

                result.Id = currentActivity.Id;
            }

            foreach (var item in currentActivity.Baggage)
            {
                if (!result.Properties.ContainsKey(item.Key))
                {
                    result.Properties.Add(item);
                }
            }

            // save current activity in case it will be lost (under the same name TelemetryCorrelation stores it)
            // TelemetryCorrelation will restore it when possible.
            platformContext.Items[ActivityHelpers.RequestActivityItemName]           = currentActivity;
            platformContext.Items[RequestTrackingConstants.RequestTelemetryItemName] = result;
            WebEventSource.Log.WebTelemetryModuleRequestTelemetryCreated();

            return(result);
        }
        /// <summary>
        /// Creates an operation object with a given telemetry item.
        /// </summary>
        /// <typeparam name="T">Type of the telemetry item.</typeparam>
        /// <param name="telemetryClient">Telemetry client object.</param>
        /// <param name="operationTelemetry">Operation to start.</param>
        /// <returns>Operation item object with a new telemetry item having current start time and timestamp.</returns>
        public static IOperationHolder <T> StartOperation <T>(this TelemetryClient telemetryClient, T operationTelemetry) where T : OperationTelemetry
        {
            if (telemetryClient == null)
            {
                throw new ArgumentNullException(nameof(telemetryClient));
            }

            if (operationTelemetry == null)
            {
                throw new ArgumentNullException(nameof(operationTelemetry));
            }

            var  telemetryContext  = operationTelemetry.Context.Operation;
            bool idsAssignedByUser = !string.IsNullOrEmpty(telemetryContext.Id);

            // We initialize telemetry here AND in Track method because of RichPayloadEventSource.
            // It sends Start and Stop events for OperationTelemetry. During Start event telemetry
            // has to contain essential telemetry properties such as correlations ids and ikey.
            // Also, examples in our documentation rely on the fact that correlation Ids are set
            // after StartOperation call and before operation is stopped.
            // Before removing this call (for optimization), make sure:
            // 1) correlation ids are set before method leaves
            // 2) RichPayloadEventSource is re-factored to work without ikey in Start event (or ikey is set)
            //    and does not require other properties in telemetry
            telemetryClient.Initialize(operationTelemetry);

            // Initialize operation id if it wasn't initialized by telemetry initializers
            if (string.IsNullOrEmpty(operationTelemetry.Id))
            {
                operationTelemetry.GenerateOperationId();
            }

            // If the operation is not executing in the context of any other operation
            // set its name as a context (root) operation name.
            if (string.IsNullOrEmpty(telemetryContext.Name))
            {
                telemetryContext.Name = operationTelemetry.Name;
            }

            OperationHolder <T> operationHolder = null;

            var isActivityAvailable = ActivityExtensions.TryRun(() =>
            {
                var parentActivity    = Activity.Current;
                var operationActivity = new Activity(ChildActivityName);

                string operationName = telemetryContext.Name;
                if (string.IsNullOrEmpty(operationName))
                {
                    operationName = parentActivity?.GetOperationName();
                }

                if (!string.IsNullOrEmpty(operationName))
                {
                    operationActivity.SetOperationName(operationName);
                }

                if (idsAssignedByUser)
                {
                    if (Activity.DefaultIdFormat == ActivityIdFormat.W3C)
                    {
                        if (W3CUtilities.IsCompatibleW3CTraceId(telemetryContext.Id))
                        {
                            // If the user provided operationId is W3C Compatible, use it.
                            operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()),
                                                          default(ActivitySpanId), ActivityTraceFlags.None);
                        }
                        else
                        {
                            // If user provided operationId is not W3C compatible, generate a new one instead.
                            // and store supplied value inside custom property.
                            operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, telemetryContext.Id);
                            telemetryContext.Id = null;
                        }
                    }
                    else
                    {
                        operationActivity.SetParentId(telemetryContext.Id);
                    }
                }

                operationActivity.Start();

                if (operationActivity.IdFormat == ActivityIdFormat.W3C)
                {
                    if (string.IsNullOrEmpty(telemetryContext.Id))
                    {
                        telemetryContext.Id = operationActivity.TraceId.ToHexString();
                    }

                    // ID takes the form |TraceID.SpanId.
                    // TelemetryContext.Id used instead of TraceID.ToHexString() for perf.
                    operationTelemetry.Id = W3CUtilities.FormatTelemetryId(telemetryContext.Id, operationActivity.SpanId.ToHexString());
                }
                else
                {
                    if (string.IsNullOrEmpty(telemetryContext.Id))
                    {
                        telemetryContext.Id = operationActivity.RootId;
                    }

                    operationTelemetry.Id = operationActivity.Id;
                }

                operationHolder = new OperationHolder <T>(telemetryClient, operationTelemetry, parentActivity == operationActivity.Parent ? null : parentActivity);
            });

            if (!isActivityAvailable)
            {
                // Parent context store is assigned to operation that is used to restore call context.
                operationHolder = new OperationHolder <T>(telemetryClient, operationTelemetry)
                {
                    ParentContext = CallContextHelpers.GetCurrentOperationContext(),
                };
                telemetryContext.Id = operationTelemetry.Id;
            }

            operationTelemetry.Start();

            if (!isActivityAvailable)
            {
                // Update the call context to store certain fields that can be used for subsequent operations.
                var operationContext = new OperationContextForCallContext
                {
                    ParentOperationId = operationTelemetry.Id,
                    RootOperationId   = operationTelemetry.Context.Operation.Id,
                    RootOperationName = operationTelemetry.Context.Operation.Name,
                };
                CallContextHelpers.SaveOperationContext(operationContext);
            }

            return(operationHolder);
        }
Beispiel #18
0
        /// <summary>
        /// Initializes/Adds operation context to the existing telemetry item.
        /// </summary>
        /// <param name="telemetryItem">Target telemetry item to add operation context.</param>
        public void Initialize(ITelemetry telemetryItem)
        {
            var itemOperationContext = telemetryItem.Context.Operation;
            var telemetryProp        = telemetryItem as ISupportProperties;

            bool isActivityAvailable = false;

            isActivityAvailable = ActivityExtensions.TryRun(() =>
            {
                var currentActivity = Activity.Current;
                if (currentActivity != null)
                {
                    // we are going to set tracestate property on requests and dependencies only
                    if (currentActivity.IdFormat == ActivityIdFormat.W3C &&
                        !string.IsNullOrEmpty(currentActivity.TraceStateString) &&
                        telemetryItem is OperationTelemetry &&
                        telemetryProp != null &&
                        !telemetryProp.Properties.ContainsKey(TracestatePropertyKey))
                    {
                        telemetryProp.Properties.Add(TracestatePropertyKey, currentActivity.TraceStateString);
                    }

                    // update proactive sampling decision if Activity is recorded
                    // sampling processor may change the decision
                    if (currentActivity.Recorded &&
                        telemetryItem is ISupportAdvancedSampling supportSamplingTelemetry &&
                        supportSamplingTelemetry.ProactiveSamplingDecision == SamplingDecision.None)
                    {
                        supportSamplingTelemetry.ProactiveSamplingDecision = SamplingDecision.SampledIn;
                    }

                    if (string.IsNullOrEmpty(itemOperationContext.Id))
                    {
                        if (currentActivity.IdFormat == ActivityIdFormat.W3C)
                        {
                            // Set OperationID to Activity.TraceId
                            // itemOperationContext.Id = currentActivity.RootId; // check if this can be used
                            itemOperationContext.Id = currentActivity.TraceId.ToHexString();

                            // Set OperationParentID to ID of parent, constructed as !traceid.spanid.
                            // ID for auto collected Request,Dependency are constructed as !traceid.spanid, so parentid must be set to the same format.
                            // While it is possible to set SpanID as the ID for auto collected Request,Dependency we have to stick to this format
                            // to maintain compatibility. This limitation may go away in the future.
                            if (string.IsNullOrEmpty(itemOperationContext.ParentId))
                            {
                                itemOperationContext.ParentId = W3CUtilities.FormatTelemetryId(itemOperationContext.Id,
                                                                                               currentActivity.SpanId.ToHexString());
                            }
                        }
                        else
                        {
                            itemOperationContext.Id = currentActivity.RootId;

                            if (string.IsNullOrEmpty(itemOperationContext.ParentId))
                            {
                                itemOperationContext.ParentId = currentActivity.Id;
                            }
                        }

                        foreach (var baggage in currentActivity.Baggage)
                        {
                            if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(baggage.Key))
                            {
                                telemetryProp.Properties.Add(baggage);
                            }
                        }

                        if (string.IsNullOrEmpty(itemOperationContext.Name))
                        {
                            string operationName = currentActivity.GetOperationName();
                            if (!string.IsNullOrEmpty(operationName))
                            {
                                itemOperationContext.Name = operationName;
                            }
                        }
                    }
                }
            });

            if (!isActivityAvailable)
            {
                if (string.IsNullOrEmpty(itemOperationContext.ParentId) || string.IsNullOrEmpty(itemOperationContext.Id) || string.IsNullOrEmpty(itemOperationContext.Name))
                {
                    var parentContext = CallContextHelpers.GetCurrentOperationContext();
                    if (parentContext != null)
                    {
                        if (string.IsNullOrEmpty(itemOperationContext.ParentId) &&
                            !string.IsNullOrEmpty(parentContext.ParentOperationId))
                        {
                            itemOperationContext.ParentId = parentContext.ParentOperationId;
                        }

                        if (string.IsNullOrEmpty(itemOperationContext.Id) &&
                            !string.IsNullOrEmpty(parentContext.RootOperationId))
                        {
                            itemOperationContext.Id = parentContext.RootOperationId;
                        }

                        if (string.IsNullOrEmpty(itemOperationContext.Name) &&
                            !string.IsNullOrEmpty(parentContext.RootOperationName))
                        {
                            itemOperationContext.Name = parentContext.RootOperationName;
                        }

                        if (parentContext.CorrelationContext != null)
                        {
                            foreach (var item in parentContext.CorrelationContext)
                            {
                                if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(item.Key))
                                {
                                    telemetryProp.Properties.Add(item);
                                }
                            }
                        }
                    }
                }
            }
        }