diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b72cef7b..861ddc54e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## Version 2.9.0-beta3 +- [When Activity has root id compatible with W3C trace Id, use it as trace id](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/1107) + ## Version 2.9.0-beta1 - [Prevent duplicate dependency collection in multi-host apps](https://github.com/Microsoft/ApplicationInsights-aspnetcore/issues/621) - [Fix missing transactions Sql dependencies](https://github.com/Microsoft/ApplicationInsights-dotnet-server/pull/1031) diff --git a/Src/Common/W3C/W3CActivityExtensions.cs b/Src/Common/W3C/W3CActivityExtensions.cs index e548a066e..41b78e490 100644 --- a/Src/Common/W3C/W3CActivityExtensions.cs +++ b/Src/Common/W3C/W3CActivityExtensions.cs @@ -43,7 +43,11 @@ public static Activity GenerateW3CContext(this Activity activity) activity.SetVersion(W3CConstants.DefaultVersion); activity.SetSampled(W3CConstants.TraceFlagRecordedAndNotRequested); activity.SetSpanId(StringUtilities.GenerateSpanId()); - activity.SetTraceId(StringUtilities.GenerateTraceId()); + + activity.SetTraceId(activity.RootId != null && TraceIdRegex.IsMatch(activity.RootId) + ? activity.RootId + : StringUtilities.GenerateTraceId()); + return activity; } diff --git a/Src/DependencyCollector/Shared.Tests/Implementation/ServiceBusDiagnosticListenerTests.cs b/Src/DependencyCollector/Shared.Tests/Implementation/ServiceBusDiagnosticListenerTests.cs index 52b92bfd4..f1f73baac 100644 --- a/Src/DependencyCollector/Shared.Tests/Implementation/ServiceBusDiagnosticListenerTests.cs +++ b/Src/DependencyCollector/Shared.Tests/Implementation/ServiceBusDiagnosticListenerTests.cs @@ -11,6 +11,7 @@ using Microsoft.ApplicationInsights.DependencyCollector.Implementation; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.W3C; using Microsoft.ApplicationInsights.Web.TestFramework; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -218,6 +219,38 @@ public void ServiceBusProcessHandingWithoutParent() } } +#pragma warning disable 612, 618 + [TestMethod] + public void ServiceBusProcessHandingExternalParentW3CCompatibleRequestId() + { + this.configuration.TelemetryInitializers.Add(new W3COperationCorrelationTelemetryInitializer()); + using (var listener = new DiagnosticListener("Microsoft.Azure.ServiceBus")) + using (var module = new DependencyTrackingTelemetryModule()) + { + module.EnableW3CHeadersInjection = true; + module.IncludeDiagnosticSourceActivities.Add("Microsoft.Azure.ServiceBus"); + module.Initialize(this.configuration); + + var telemetry = this.TrackOperation( + listener, + "Microsoft.Azure.ServiceBus.Process", + TaskStatus.RanToCompletion, + "|4bf92f3577b34da6a3ce929d0e0e4736."); + + Assert.IsNotNull(telemetry); + Assert.AreEqual("Process", telemetry.Name); + Assert.AreEqual( + $"type:{RemoteDependencyConstants.AzureServiceBus} | name:queueName | endpoint:sb://queuename.myservicebus.com/", + telemetry.Source); + Assert.IsTrue(telemetry.Success.Value); + + Assert.AreEqual("|4bf92f3577b34da6a3ce929d0e0e4736.", telemetry.Context.Operation.ParentId); + Assert.AreEqual("4bf92f3577b34da6a3ce929d0e0e4736", telemetry.Context.Operation.Id); + Assert.AreEqual("messageId", telemetry.Properties["MessageId"]); + } + } +#pragma warning restore 612, 618 + [TestMethod] public void ServiceBusExceptionsAreIgnored() { diff --git a/Src/DependencyCollector/Shared.Tests/W3C/W3CActiviityExtentionsTests.cs b/Src/DependencyCollector/Shared.Tests/W3C/W3CActiviityExtentionsTests.cs index 93a9f745c..4b7c19799 100644 --- a/Src/DependencyCollector/Shared.Tests/W3C/W3CActiviityExtentionsTests.cs +++ b/Src/DependencyCollector/Shared.Tests/W3C/W3CActiviityExtentionsTests.cs @@ -156,6 +156,45 @@ public void UpdateContextWithoutParent() Assert.IsNull(a.GetTracestate()); } + [TestMethod] + public void UpdateContextFromCompatibleRootId() + { + var a = new Activity("foo"); + a.SetParentId(TraceId); + + Assert.IsFalse(a.IsW3CActivity()); + + a.UpdateContextOnActivity(); + Assert.IsTrue(a.IsW3CActivity()); + Assert.AreEqual(TraceId, a.GetTraceId()); + Assert.IsNotNull(a.GetSpanId()); + Assert.IsNull(a.GetParentSpanId()); + Assert.IsNotNull(a.GetSpanId()); + + Assert.AreEqual($"00-{a.GetTraceId()}-{a.GetSpanId()}-02", a.GetTraceparent()); + Assert.IsNull(a.GetTracestate()); + } + + [TestMethod] + public void UpdateContextFromIncompatibleRootId() + { + var a = new Activity("foo"); + a.SetParentId("abc"); + + Assert.IsFalse(a.IsW3CActivity()); + + a.UpdateContextOnActivity(); + Assert.IsTrue(a.IsW3CActivity()); + Assert.AreNotEqual("abc", a.GetTraceId()); + Assert.IsNotNull(a.GetTraceId()); + Assert.IsNotNull(a.GetSpanId()); + Assert.IsNull(a.GetParentSpanId()); + Assert.IsNotNull(a.GetSpanId()); + + Assert.AreEqual($"00-{a.GetTraceId()}-{a.GetSpanId()}-02", a.GetTraceparent()); + Assert.IsNull(a.GetTracestate()); + } + [TestMethod] public void UpdateContextWithParent() { diff --git a/Src/Web/Web.Net45.Tests/AspNetDiagnosticTelemetryModuleTest.cs b/Src/Web/Web.Net45.Tests/AspNetDiagnosticTelemetryModuleTest.cs index eba204cfe..80049555b 100644 --- a/Src/Web/Web.Net45.Tests/AspNetDiagnosticTelemetryModuleTest.cs +++ b/Src/Web/Web.Net45.Tests/AspNetDiagnosticTelemetryModuleTest.cs @@ -383,6 +383,7 @@ public void TestActivityIdGenerationWithW3CEnabled() Assert.AreEqual(32, request.Context.Operation.Id.Length); Assert.IsTrue(Regex.Match(request.Context.Operation.Id, @"[a-z][0-9]").Success); + Assert.AreEqual(request.Context.Operation.Id, activity.GetTraceId()); Assert.AreEqual(request.Context.Operation.Id, activity.RootId); Assert.AreEqual(request.Context.Operation.ParentId, activity.GetParentSpanId()); Assert.AreEqual(request.Id, $"|{activity.GetTraceId()}.{activity.GetSpanId()}."); @@ -495,6 +496,42 @@ public void RequestIdBecomesParentWhenThereAreNoW3CHeaders() Assert.IsTrue(requestTelemetry.Properties[W3CConstants.LegacyRequestIdProperty].StartsWith("|requestId.")); } + [TestMethod] + public void RequestIdBecomesParentAndRootIfCompatibleWhenThereAreNoW3CHeaders() + { + this.aspNetDiagnosticsSource.FakeContext = + HttpModuleHelper.GetFakeHttpContext(new Dictionary + { + ["Request-Id"] = "|4bf92f3577b34da6a3ce929d0e0e4736." + }); + this.module = this.CreateModule(enableW3cSupport: true); + + var activity = new Activity(FakeAspNetDiagnosticSource.IncomingRequestEventName); + + activity.Extract(HttpContext.Current.Request.Headers); + + Assert.IsTrue(this.aspNetDiagnosticsSource.IsEnabled(FakeAspNetDiagnosticSource.IncomingRequestEventName, activity)); + this.aspNetDiagnosticsSource.StartActivityWithoutChecks(activity); + this.aspNetDiagnosticsSource.StopActivity(); + + Assert.AreEqual("4bf92f3577b34da6a3ce929d0e0e4736", activity.GetTraceId()); + Assert.AreEqual(16, activity.GetSpanId().Length); + Assert.IsNull(activity.GetParentSpanId()); + + Assert.AreEqual(1, this.sendItems.Count); + + var requestTelemetry = this.sendItems[0] as RequestTelemetry; + Assert.IsNotNull(requestTelemetry); + Assert.AreEqual(activity.GetTraceId(), requestTelemetry.Context.Operation.Id); + Assert.AreEqual("|4bf92f3577b34da6a3ce929d0e0e4736.", requestTelemetry.Context.Operation.ParentId); + Assert.AreEqual($"|{activity.GetTraceId()}.{activity.GetSpanId()}.", requestTelemetry.Id); + + Assert.IsFalse(requestTelemetry.Properties.ContainsKey(W3CConstants.LegacyRootIdProperty)); + + Assert.IsTrue(requestTelemetry.Properties.ContainsKey(W3CConstants.LegacyRequestIdProperty)); + Assert.IsTrue(requestTelemetry.Properties[W3CConstants.LegacyRequestIdProperty].StartsWith("|4bf92f3577b34da6a3ce929d0e0e4736.")); + } + [TestMethod] public void CustomHeadersBecomeParentWhenThereAreNoW3CHeaders() {