From 54235fc936b69a696eb3868c87fe38db303fc2c4 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Thu, 15 Aug 2019 18:55:32 -0700 Subject: [PATCH 01/19] wip1 --- ...ionCorrelationTelemetryInitializerTests.cs | 239 +++++++++++----- .../TelemetryConfigurationTest.cs | 30 +- ...ApplicationInsights.Shared.Tests.projitems | 1 - .../Shared/TelemetryClientExtensionTests.cs | 44 ++- ...ionCorrelationTelemetryInitializerTests.cs | 259 ------------------ .../Implementation/OperationHolder.cs | 5 +- ...perationCorrelationTelemetryInitializer.cs | 89 ++++-- .../Extensibility/TelemetryConfiguration.cs | 31 +++ .../W3C/W3CActivityExtensions.cs | 13 +- ...perationCorrelationTelemetryInitializer.cs | 6 +- .../TelemetryClientExtensions.cs | 27 +- 11 files changed, 373 insertions(+), 371 deletions(-) delete mode 100644 Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3COperationCorrelationTelemetryInitializerTests.cs diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs index 5a8eb1cb4a..68aa203e0c 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs @@ -1,172 +1,277 @@ namespace Microsoft.ApplicationInsights.Extensibility { using System.Diagnostics; + using System.Linq; using Implementation; using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility.W3C; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class OperationCorrelationTelemetryInitializerTests { + private static TelemetryConfiguration tc; + + [ClassInitialize] + public static void Init(TestContext ctx) + { + // Constructor on TelemetryConfiguration forces Activity.IDFormat to be W3C + // OperationCorrelationTelemetryInitializer has no responsibility to set the Activity Format. + // It expects Activity to use W3CFormat, but falls back to use Hierrarchial Id. + tc = new TelemetryConfiguration(); + } + + [TestInitialize] + public void TestInit() + { + tc.EnableW3CCorrelation = true; + } + + [TestCleanup] + public void Cleanup() + { + while (Activity.Current != null) + { + Activity.Current.Stop(); + } + } + [TestMethod] public void InitializerDoesNotFailOnNullCurrentActivity() { + // Arrange + // Does not start Activity. + var telemetry = new DependencyTelemetry(); + + // Act (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); + + // Validate + // Initialize is a no-op, and no exceptions thrown. + Assert.IsNull(telemetry.Context.Operation.Id); Assert.IsNull(telemetry.Context.Operation.ParentId); + Assert.IsFalse(telemetry.Properties.Any()); } [TestMethod] - public void TelemetryContextIsUpdatedWithOperationIdForDependencyTelemetry() + public void InitializePopulatesOperationContextFromActivity() { - Activity parent = new Activity("parent").SetParentId("ParentOperationId").Start(); + // 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(W3CActivityExtensions.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(); + } + + [TestMethod] + public void InitializePopulatesOperationContextFromActivityWhenW3CIsDisabled() + { + // Arrange + tc.EnableW3CCorrelation = false; + Activity parent = new Activity("parent"); + + // Setting parentid like this forces Activity to use Hierrachial ID Format + parent.SetParentId("parent"); + parent.Start(); var telemetry = new DependencyTelemetry(); - (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); + var initializer = new OperationCorrelationTelemetryInitializer(); + + // Act + initializer.Initialize(telemetry); - Assert.AreEqual("ParentOperationId", telemetry.Context.Operation.Id); + // Validate + Assert.AreEqual("parent", telemetry.Context.Operation.Id); Assert.AreEqual(parent.Id, telemetry.Context.Operation.ParentId); - parent.Stop(); + parent.Stop(); } [TestMethod] - public void InitializeDoesNotUpdateOperationIdIfItExists() + public void InitializeDoesNotOverrideOperationIdIfItExists() { - Activity parent = new Activity("parent").SetParentId("ParentOperationId").Start(); - + // Arrange var telemetry = new DependencyTelemetry(); telemetry.Context.Operation.ParentId = "OldParentOperationId"; - (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); + telemetry.Context.Operation.Id = "OldOperationId"; + var initializer = new OperationCorrelationTelemetryInitializer(); + Activity parent = new Activity("parent"); + parent.Start(); + + // Act + initializer.Initialize(telemetry); + + // Validate Assert.AreEqual("OldParentOperationId", telemetry.Context.Operation.ParentId); + Assert.AreEqual("OldOperationId", telemetry.Context.Operation.Id); parent.Stop(); } [TestMethod] - public void TelemetryContextIsUpdatedWithOperationNameForDependencyTelemetry() + public void InitializeDoesNotOverrideEmptyParentIdIfOperationIdExists() { + // Arrange + var telemetry = new DependencyTelemetry(); + telemetry.Context.Operation.Id = "OldOperationId"; + // Does not set parentid and hence it'll be empty + var initializer = new OperationCorrelationTelemetryInitializer(); Activity parent = new Activity("parent"); - parent.AddTag("OperationName", "OperationName"); parent.Start(); - var telemetry = new DependencyTelemetry(); - (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.AreEqual(telemetry.Context.Operation.Name, "OperationName"); + initializer.Initialize(telemetry); + + Assert.IsNull(telemetry.Context.Operation.ParentId, "Operation.ParentID should not be overwritten when Operation.ID is already present"); + Assert.AreEqual("OldOperationId", telemetry.Context.Operation.Id, "Operation should not be overwritten"); parent.Stop(); } [TestMethod] - public void InitilaizeWithActivityWithoutOperationName() + public void InitializeDoesntOverrideContextIfOperationIdSet() { var currentActivity = new Activity("test"); + currentActivity.AddTag("OperationName", "operation"); + currentActivity.AddBaggage("k1", "v1"); + currentActivity.AddBaggage("k2", "v2"); currentActivity.Start(); var telemetry = new RequestTelemetry(); + telemetry.Context.Operation.Id = "operationId"; + telemetry.Context.Operation.ParentId = null; + telemetry.Context.Operation.Name = "operation"; + (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); + Assert.AreEqual("operationId", telemetry.Context.Operation.Id); + Assert.IsNull(telemetry.Context.Operation.ParentId); + Assert.AreEqual("operation", telemetry.Context.Operation.Name); + Assert.AreEqual(0, telemetry.Properties.Count); + currentActivity.Stop(); + } + + [TestMethod] + public void InitializeOverridesContextIfOperationIdIsNotSet() + { + var currentActivity = new Activity("test"); + currentActivity.AddTag("OperationName", "operation"); + currentActivity.AddBaggage("k1", "v1"); + currentActivity.Start(); + var telemetry = new TraceTelemetry(); + + telemetry.Context.Operation.ParentId = "parentId"; + telemetry.Context.Operation.Name = "operation"; (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.IsNull(telemetry.Context.Operation.Name); + Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id); + Assert.AreEqual("parentId", 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(); } [TestMethod] - public void InitializeDoesNotUpdateOperationNameIfItExists() + public void TelemetryContextIsUpdatedWithOperationNameForDependencyTelemetry() { Activity parent = new Activity("parent"); parent.AddTag("OperationName", "OperationName"); parent.Start(); var telemetry = new DependencyTelemetry(); - telemetry.Context.Operation.Name = "OldOperationName"; (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - - Assert.AreEqual(telemetry.Context.Operation.Name, "OldOperationName"); + Assert.AreEqual(telemetry.Context.Operation.Name, "OperationName"); parent.Stop(); } [TestMethod] - public void InitilaizeWithActivitySetsOperationContext() + public void InitializeWithActivityWithoutOperationName() { var currentActivity = new Activity("test"); - currentActivity.SetParentId("parent"); - currentActivity.AddTag("OperationName", "operation"); - currentActivity.AddBaggage("k1", "v1"); - currentActivity.AddBaggage("k2", "v2"); currentActivity.Start(); var telemetry = new RequestTelemetry(); - (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.AreEqual("parent", telemetry.Context.Operation.Id); - Assert.AreEqual(currentActivity.Id, telemetry.Context.Operation.ParentId); - Assert.IsTrue(telemetry.Context.Operation.ParentId.StartsWith("|parent.")); - Assert.AreEqual("operation", telemetry.Context.Operation.Name); + (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); -#pragma warning disable CS0618 // Type or member is obsolete - Assert.AreEqual(2, telemetry.Context.Properties.Count); - Assert.AreEqual("v1", telemetry.Context.Properties["k1"]); - Assert.AreEqual("v2", telemetry.Context.Properties["k2"]); -#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsNull(telemetry.Context.Operation.Name); currentActivity.Stop(); } [TestMethod] - public void InitilaizeWithActivityWinsOverCallContext() + public void InitializeWithActivityWithOperationName() { - CallContextHelpers.SaveOperationContext(new OperationContextForCallContext { RootOperationId = "callContextRoot" }); var currentActivity = new Activity("test"); - currentActivity.SetParentId("activityRoot"); - currentActivity.AddTag("OperationName", "operation"); - currentActivity.AddBaggage("k1", "v1"); + currentActivity.AddTag("OperationName", "OperationName"); currentActivity.Start(); var telemetry = new RequestTelemetry(); - (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.AreEqual("activityRoot", telemetry.Context.Operation.Id); - Assert.AreEqual(currentActivity.Id, telemetry.Context.Operation.ParentId); - Assert.IsTrue(telemetry.Context.Operation.ParentId.StartsWith("|activityRoot.")); + (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.AreEqual("operation", telemetry.Context.Operation.Name); - Assert.AreEqual(1, telemetry.Properties.Count); - Assert.AreEqual("v1", telemetry.Properties["k1"]); + Assert.AreEqual("OperationName", telemetry.Context.Operation.Name); currentActivity.Stop(); } [TestMethod] - public void InitilaizeWithActivityDoesntOverrideContextIfRootIsSet() + public void InitializeDoesNotUpdateOperationNameIfItExists() + { + Activity parent = new Activity("parent"); + parent.AddTag("OperationName", "OperationName"); + parent.Start(); + + var telemetry = new DependencyTelemetry(); + telemetry.Context.Operation.Name = "OldOperationName"; + (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); + + Assert.AreEqual("OldOperationName",telemetry.Context.Operation.Name); + parent.Stop(); + } + + [TestMethod] + public void InitializeSetsBaggage() { var currentActivity = new Activity("test"); - currentActivity.SetParentId("activityRoot"); - currentActivity.AddTag("OperationName", "test"); + currentActivity.AddTag("OperationName", "operation"); currentActivity.AddBaggage("k1", "v1"); + currentActivity.AddBaggage("k2", "v2"); + currentActivity.AddBaggage("existingkey", "exitingvalue"); currentActivity.Start(); - var telemetry = new TraceTelemetry(); - - telemetry.Context.Operation.Id = "rootId"; - telemetry.Context.Operation.ParentId = null; - telemetry.Context.Operation.Name = "operation"; + var telemetry = new RequestTelemetry(); + telemetry.Properties.Add("existingkey", "exitingvalue"); (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.AreEqual("rootId", telemetry.Context.Operation.Id); - Assert.IsNull(telemetry.Context.Operation.ParentId); + Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id); + Assert.AreEqual(W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()), telemetry.Context.Operation.ParentId); Assert.AreEqual("operation", telemetry.Context.Operation.Name); - Assert.AreEqual(0, telemetry.Properties.Count); + + 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(); } [TestMethod] - public void InitilaizeWithActivityOverridesContextIfRootIsNotSet() + public void InitilaizeWithActivityWinsOverCallContext() { + CallContextHelpers.SaveOperationContext(new OperationContextForCallContext { RootOperationId = "callContextRoot" }); var currentActivity = new Activity("test"); - currentActivity.SetParentId("activityRoot"); - currentActivity.AddTag("OperationName", "test"); + currentActivity.AddTag("OperationName", "operation"); currentActivity.AddBaggage("k1", "v1"); currentActivity.Start(); - var telemetry = new TraceTelemetry(); - - telemetry.Context.Operation.ParentId = "parentId"; - telemetry.Context.Operation.Name = "operation"; + var telemetry = new RequestTelemetry(); (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); - Assert.AreEqual("activityRoot", telemetry.Context.Operation.Id); - Assert.AreEqual("parentId", telemetry.Context.Operation.ParentId); + Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id); + Assert.AreEqual(W3CActivityExtensions.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"]); diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs index 1197df9dca..ca646c867c 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs @@ -9,10 +9,38 @@ using Microsoft.ApplicationInsights.Extensibility.Implementation; using Microsoft.ApplicationInsights.TestFramework; using Microsoft.VisualStudio.TestTools.UnitTesting; - + using System.Diagnostics; + [TestClass] public class TelemetryConfigurationTest { + #region W3C + [TestMethod] + public void TelemetryConfigurationConstructorSetsW3CToTrueByDefault() + { + var tc = new TelemetryConfiguration(); + Assert.IsTrue(tc.EnableW3CCorrelation); + } + + [TestMethod] + public void TelemetryConfigurationEnableW3CCorrelationSetsActivityDefaultFormatToW3C() + { + var original = Activity.DefaultIdFormat; + var tc = new TelemetryConfiguration(); + tc.EnableW3CCorrelation = true; + Assert.AreEqual(ActivityIdFormat.W3C, Activity.DefaultIdFormat); + } + + [TestMethod] + public void TelemetryConfigurationDisableW3CCorrelationRestoresActivityDefaultFormat() + { + var original = Activity.DefaultIdFormat; + var tc = new TelemetryConfiguration(); + tc.EnableW3CCorrelation = false; + Assert.AreEqual(original, Activity.DefaultIdFormat); + } + #endregion + [TestMethod] public void TelemetryConfigurationIsPublicToAllowUsersManipulateConfigurationProgrammatically() { diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems b/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems index 0ba8c48816..1fe7fb640e 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems @@ -124,7 +124,6 @@ - diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs index 6b4208c9a8..2b27978ed4 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs @@ -10,22 +10,24 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Extensibility.Implementation; using TestFramework; + using Microsoft.ApplicationInsights.Extensibility.W3C; [TestClass] public class TelemetryClientExtensionTests { private TelemetryClient telemetryClient; + private TelemetryConfiguration telemetryConfiguration; private List sendItems; [TestInitialize] public void TestInitialize() { - var configuration = new TelemetryConfiguration(); + this.telemetryConfiguration = new TelemetryConfiguration(); this.sendItems = new List(); - configuration.TelemetryChannel = new StubTelemetryChannel { OnSend = item => this.sendItems.Add(item) }; - configuration.InstrumentationKey = Guid.NewGuid().ToString(); - configuration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer()); - this.telemetryClient = new TelemetryClient(configuration); + telemetryConfiguration.TelemetryChannel = new StubTelemetryChannel { OnSend = item => this.sendItems.Add(item) }; + telemetryConfiguration.InstrumentationKey = Guid.NewGuid().ToString(); + telemetryConfiguration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer()); + this.telemetryClient = new TelemetryClient(telemetryConfiguration); CallContextHelpers.RestoreOperationContext(null); } @@ -225,8 +227,10 @@ public void ContextPropagatesThroughNestedOperations() } [TestMethod] - public void StartOperationCanOverrideOperationId() + public void StartOperationCanOverrideOperationIdNonW3C() { + this.telemetryConfiguration.EnableW3CCorrelation = false; + using (this.telemetryClient.StartOperation("Request", "HOME")) { } @@ -238,6 +242,34 @@ public void StartOperationCanOverrideOperationId() Assert.AreEqual("HOME", requestTelemetry.Context.Operation.Id); } + [TestMethod] + public void StartOperationOperationIdIsIgnoredIfNotW3cCompatible() + { + using (this.telemetryClient.StartOperation("Request", "HOME")) + { + } + + Assert.AreEqual(1, this.sendItems.Count); + + var requestTelemetry = (RequestTelemetry)this.sendItems[0]; + Assert.IsNull(requestTelemetry.Context.Operation.ParentId); + Assert.AreEqual("HOME", requestTelemetry.Properties[W3CConstants.LegacyRootIdProperty]); + } + + [TestMethod] + public void StartOperationOperationIdIsUsedIfW3cCompatible() + { + using (this.telemetryClient.StartOperation("Request", "8ee8641cbdd8dd280d239fa2121c7e4e")) + { + } + + Assert.AreEqual(1, this.sendItems.Count); + + var requestTelemetry = (RequestTelemetry)this.sendItems[0]; + Assert.IsNull(requestTelemetry.Context.Operation.ParentId); + Assert.AreEqual("8ee8641cbdd8dd280d239fa2121c7e4e", requestTelemetry.Context.Operation.Id); + } + [TestMethod] public void StartOperationCanOverrideRootAndParentOperationId() { diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3COperationCorrelationTelemetryInitializerTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3COperationCorrelationTelemetryInitializerTests.cs deleted file mode 100644 index 007fab979e..0000000000 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3COperationCorrelationTelemetryInitializerTests.cs +++ /dev/null @@ -1,259 +0,0 @@ -namespace Microsoft.ApplicationInsights.W3C -{ - using System.Diagnostics; - using System.Linq; - using Microsoft.ApplicationInsights.DataContracts; - using Microsoft.ApplicationInsights.Extensibility.Implementation; - using Microsoft.ApplicationInsights.Extensibility.W3C; - using Microsoft.VisualStudio.TestTools.UnitTesting; - - [TestClass] - public class W3COperationCorrelationTelemetryInitializerTests - { - [TestCleanup] - public void Cleanup() - { - while (Activity.Current != null) - { - Activity.Current.Stop(); - } - } - - [TestMethod] - public void InitializerCreatesNewW3CContext() - { - Activity a = new Activity("dummy") - .Start(); - - RequestTelemetry request = new RequestTelemetry(); - - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.IsNotNull(request.Context.Operation.Id); - Assert.IsNull(request.Context.Operation.ParentId); - Assert.AreEqual($"|{a.GetTraceId()}.{a.GetSpanId()}.", request.Id); - - Assert.AreEqual(2, request.Properties.Count); - - Assert.IsTrue(request.Properties.ContainsKey(W3CConstants.LegacyRequestIdProperty)); - Assert.AreEqual(a.Id, request.Properties[W3CConstants.LegacyRequestIdProperty]); - - Assert.IsTrue(request.Properties.ContainsKey(W3CConstants.LegacyRootIdProperty)); - Assert.AreEqual(a.RootId, request.Properties[W3CConstants.LegacyRootIdProperty]); - } - - [TestMethod] - public void InitializerSetsCorrelationIdsOnTraceTelemetry() - { - Activity a = new Activity("dummy") - .Start() - .GenerateW3CContext(); - - string expectedTrace = a.GetTraceId(); - string expectedParent = a.GetSpanId(); - - TraceTelemetry trace = new TraceTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(trace); - - Assert.AreEqual(expectedTrace, trace.Context.Operation.Id); - Assert.AreEqual($"|{expectedTrace}.{expectedParent}.", trace.Context.Operation.ParentId); - - Assert.IsFalse(trace.Properties.Any()); - } - - [TestMethod] - public void InitializerSetsCorrelationIdsOnRequestTelemetry() - { - Activity a = new Activity("dummy") - .Start() - .GenerateW3CContext(); - - string expectedTrace = a.GetTraceId(); - string expectedSpanId = a.GetSpanId(); - - string expectedParent = "0123456789abcdef"; - a.AddTag(W3CConstants.ParentSpanIdTag, expectedParent); - - RequestTelemetry request = new RequestTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.AreEqual(expectedTrace, request.Context.Operation.Id); - Assert.AreEqual($"|{expectedTrace}.{expectedParent}.", request.Context.Operation.ParentId); - Assert.AreEqual($"|{expectedTrace}.{expectedSpanId}.", request.Id); - - Assert.AreEqual(2, request.Properties.Count); - - Assert.IsTrue(request.Properties.ContainsKey(W3CConstants.LegacyRequestIdProperty)); - Assert.AreEqual(a.Id, request.Properties[W3CConstants.LegacyRequestIdProperty]); - - Assert.IsTrue(request.Properties.ContainsKey(W3CConstants.LegacyRootIdProperty)); - Assert.AreEqual(a.RootId, request.Properties[W3CConstants.LegacyRootIdProperty]); - } - - [TestMethod] - public void InitializerSetsCorrelationIdsOnRequestTelemetryNoParent() - { - Activity a = new Activity("dummy") - .Start() - .GenerateW3CContext(); - - string expectedTrace = a.GetTraceId(); - string expectedSpanId = a.GetSpanId(); - - RequestTelemetry request = new RequestTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.AreEqual(expectedTrace, request.Context.Operation.Id); - Assert.IsNull(request.Context.Operation.ParentId); - Assert.AreEqual($"|{expectedTrace}.{expectedSpanId}.", request.Id); - - Assert.AreEqual(2, request.Properties.Count); - - Assert.IsTrue(request.Properties.ContainsKey(W3CConstants.LegacyRequestIdProperty)); - Assert.AreEqual(a.Id, request.Properties[W3CConstants.LegacyRequestIdProperty]); - - Assert.IsTrue(request.Properties.ContainsKey(W3CConstants.LegacyRootIdProperty)); - Assert.AreEqual(a.RootId, request.Properties[W3CConstants.LegacyRootIdProperty]); - } - - [TestMethod] - public void InitializerNoopWithoutActivity() - { - RequestTelemetry request = new RequestTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.IsNull(request.Context.Operation.Id); - Assert.IsNull(request.Context.Operation.ParentId); - - Assert.IsFalse(request.Properties.Any()); - } - - [TestMethod] - public void InitializerIgnoresExistingValues() - { - Activity a = new Activity("dummy") - .Start() - .GenerateW3CContext(); - - string expectedTrace = a.GetTraceId(); - string expectedSpanId = a.GetSpanId(); - - string expectedParent = "0123456789abcdef"; - a.AddTag(W3CConstants.ParentSpanIdTag, expectedParent); - - RequestTelemetry request = new RequestTelemetry(); - - request.Context.Operation.Id = "operation id"; - request.Context.Operation.ParentId = "parent id"; - request.Id = "id"; - - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.AreEqual(expectedTrace, request.Context.Operation.Id); - Assert.AreEqual($"|{expectedTrace}.{expectedParent}.", request.Context.Operation.ParentId); - Assert.AreEqual($"|{expectedTrace}.{expectedSpanId}.", request.Id); - } - - [TestMethod] - public void InitializerPopulatesTraceStateOnRequestAndDependencyTelemetry() - { - Activity a = new Activity("dummy") - .Start() - .GenerateW3CContext(); - - a.SetTracestate("key=value"); - - string expectedTrace = a.GetTraceId(); - string expectedSpanId = a.GetSpanId(); - - RequestTelemetry request = new RequestTelemetry(); - DependencyTelemetry dependency = new DependencyTelemetry(); - TraceTelemetry trace = new TraceTelemetry(); - var initializer = new W3COperationCorrelationTelemetryInitializer(); - initializer.Initialize(request); - initializer.Initialize(dependency); - initializer.Initialize(trace); - - Assert.AreEqual(expectedTrace, request.Context.Operation.Id); - Assert.AreEqual($"|{expectedTrace}.{expectedSpanId}.", request.Id); - - Assert.AreEqual("key=value", request.Properties[W3CConstants.TracestateTag]); - Assert.AreEqual("key=value", dependency.Properties[W3CConstants.TracestateTag]); - Assert.IsFalse(trace.Properties.Any()); - } - - [TestMethod] - public void InitializerOnNestedActivitities() - { - Activity requestActivity = new Activity("request") - .Start(); - - RequestTelemetry request = new RequestTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Activity nested1 = new Activity("nested1").Start(); - Activity nested2 = new Activity("nested1").Start(); - - DependencyTelemetry dependency2 = new DependencyTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(dependency2); - - Assert.AreEqual(request.Context.Operation.Id, nested2.GetTraceId()); - Assert.AreEqual(request.Context.Operation.Id, nested1.GetTraceId()); - - Assert.AreEqual(request.Id, $"|{nested1.GetTraceId()}.{nested1.GetParentSpanId()}."); - Assert.AreEqual(nested1.GetSpanId(), nested2.GetParentSpanId()); - - Assert.AreEqual(request.Context.Operation.Id, dependency2.Context.Operation.Id); - - nested2.Stop(); - - DependencyTelemetry dependency1 = new DependencyTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(dependency1); - - Assert.AreEqual(request.Id, $"|{nested1.GetTraceId()}.{nested1.GetParentSpanId()}."); - Assert.AreEqual(dependency2.Context.Operation.ParentId, dependency1.Id); - Assert.AreEqual(request.Context.Operation.Id, dependency1.Context.Operation.Id); - Assert.AreEqual(request.Id, dependency1.Context.Operation.ParentId); - } - - [TestMethod] - public void InitializerOnSqlDependency() - { - Activity requestActivity = new Activity("request") - .Start() - .GenerateW3CContext(); - - RequestTelemetry request = new RequestTelemetry(); - DependencyTelemetry sqlDependency = new DependencyTelemetry() - { - Type = "SQL" - }; - sqlDependency.Context.GetInternalContext().SdkVersion = "rdddsc:12345"; - string expectedId = sqlDependency.Id; - - new W3COperationCorrelationTelemetryInitializer().Initialize(sqlDependency); - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.AreEqual(request.Context.Operation.Id, sqlDependency.Context.Operation.Id); - Assert.AreEqual(request.Id, sqlDependency.Context.Operation.ParentId); - Assert.AreEqual(expectedId, sqlDependency.Id); - } - - [TestMethod] - public void InitializerOnActivityWithParentWithoutW3CTags() - { - Activity parentActivity = new Activity("parent") - .Start(); - Activity childActivity = new Activity("child") - .Start(); - - RequestTelemetry request = new RequestTelemetry(); - new W3COperationCorrelationTelemetryInitializer().Initialize(request); - - Assert.AreEqual(request.Context.Operation.Id, parentActivity.GetTraceId()); - Assert.AreEqual(request.Context.Operation.Id, childActivity.GetTraceId()); - Assert.AreEqual(request.Id, $"|{childActivity.GetTraceId()}.{childActivity.GetSpanId()}."); - Assert.AreEqual(request.Context.Operation.ParentId, $"|{childActivity.GetTraceId()}.{parentActivity.GetSpanId()}."); - } - } -} diff --git a/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs b/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs index 8f8e7a805f..74c7f803be 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Globalization; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; + using Microsoft.ApplicationInsights.Extensibility.W3C; /// /// Operation class that holds the telemetry item and the corresponding telemetry client. @@ -68,7 +69,9 @@ protected virtual void Dispose(bool disposing) isActivityAvailable = ActivityExtensions.TryRun(() => { var currentActivity = Activity.Current; - if (currentActivity == null || operationTelemetry.Id != currentActivity.Id) + if (currentActivity == null + || (Activity.DefaultIdFormat != ActivityIdFormat.W3C && operationTelemetry.Id != currentActivity.Id) + || (Activity.DefaultIdFormat == ActivityIdFormat.W3C && operationTelemetry.Id != W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()) )) { // W3COperationCorrelationTelemetryInitializer changes Id // but keeps an original one in 'ai_legacyRequestId' property diff --git a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs index 3238690f9f..7ab2488f41 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs @@ -1,61 +1,94 @@ namespace Microsoft.ApplicationInsights.Extensibility { + using System; using System.Diagnostics; using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Extensibility.W3C; -#if NET45 /// - /// Telemetry initializer that populates OperationContext for the telemetry item based on context stored in CallContext. + /// Telemetry initializer that populates OperationContext for the telemetry item from Activity. /// +#if NET45 + /// + /// Internally based on context stored in CallContext. + /// #else - /// - /// Telemetry initializer that populates OperationContext for the telemetry item based on context stored in an AsyncLocal variable. - /// + /// + /// Internally based on context stored in an AsyncLocal variable. + /// #endif public class OperationCorrelationTelemetryInitializer : ITelemetryInitializer { /// - /// Initializes/Adds operation id to the existing telemetry item. + /// Initializes/Adds operation context to the existing telemetry item. /// - /// Target telemetry item to add operation id. + /// Target telemetry item to add operation context. public void Initialize(ITelemetry telemetryItem) { - var itemContext = telemetryItem.Context.Operation; - var telemetryProp = telemetryItem as ISupportProperties; + var itemOperationContext = telemetryItem.Context.Operation; + var telemetryProp = telemetryItem as ISupportProperties; + bool isActivityAvailable = false; isActivityAvailable = ActivityExtensions.TryRun(() => - { + { var currentActivity = Activity.Current; if (currentActivity != null) { - if (string.IsNullOrEmpty(itemContext.Id)) + if (currentActivity.IdFormat == ActivityIdFormat.W3C) { - itemContext.Id = currentActivity.RootId; - - if (string.IsNullOrEmpty(itemContext.ParentId)) + if (string.IsNullOrEmpty(itemOperationContext.Id)) { - itemContext.ParentId = currentActivity.Id; - } + // Set OperationID to Activity.TraceId + // itemOperationContext.Id = currentActivity.RootId; // heck if this can be used + itemOperationContext.Id = currentActivity.TraceId.ToHexString(); + + // Set OperationParentID to ID of parent constructed from traceid, spand id. + // ID for auto collected Request,Dependency are made from trace id + span id, so parentid must be set to the same format. + if (string.IsNullOrEmpty(itemOperationContext.ParentId)) + { + itemOperationContext.ParentId = W3CActivityExtensions.FormatTelemetryId(itemOperationContext.Id, currentActivity.SpanId.ToHexString()); + } - foreach (var baggage in currentActivity.Baggage) + foreach (var baggage in currentActivity.Baggage) + { + if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(baggage.Key)) + { + telemetryProp.Properties.Add(baggage); + } + } + } + } + else + { + if (string.IsNullOrEmpty(itemOperationContext.Id)) { - if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(baggage.Key)) + itemOperationContext.Id = currentActivity.RootId; + + if (string.IsNullOrEmpty(itemOperationContext.ParentId)) + { + itemOperationContext.ParentId = currentActivity.Id; + } + + foreach (var baggage in currentActivity.Baggage) { - telemetryProp.Properties.Add(baggage); + if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(baggage.Key)) + { + telemetryProp.Properties.Add(baggage); + } } } } - if (string.IsNullOrEmpty(itemContext.Name)) + if (string.IsNullOrEmpty(itemOperationContext.Name)) { string operationName = currentActivity.GetOperationName(); if (!string.IsNullOrEmpty(operationName)) { - itemContext.Name = operationName; + itemOperationContext.Name = operationName; } } } @@ -63,27 +96,27 @@ public void Initialize(ITelemetry telemetryItem) if (!isActivityAvailable) { - if (string.IsNullOrEmpty(itemContext.ParentId) || string.IsNullOrEmpty(itemContext.Id) || string.IsNullOrEmpty(itemContext.Name)) + if (string.IsNullOrEmpty(itemOperationContext.ParentId) || string.IsNullOrEmpty(itemOperationContext.Id) || string.IsNullOrEmpty(itemOperationContext.Name)) { var parentContext = CallContextHelpers.GetCurrentOperationContext(); if (parentContext != null) { - if (string.IsNullOrEmpty(itemContext.ParentId) + if (string.IsNullOrEmpty(itemOperationContext.ParentId) && !string.IsNullOrEmpty(parentContext.ParentOperationId)) { - itemContext.ParentId = parentContext.ParentOperationId; + itemOperationContext.ParentId = parentContext.ParentOperationId; } - if (string.IsNullOrEmpty(itemContext.Id) + if (string.IsNullOrEmpty(itemOperationContext.Id) && !string.IsNullOrEmpty(parentContext.RootOperationId)) { - itemContext.Id = parentContext.RootOperationId; + itemOperationContext.Id = parentContext.RootOperationId; } - if (string.IsNullOrEmpty(itemContext.Name) + if (string.IsNullOrEmpty(itemOperationContext.Name) && !string.IsNullOrEmpty(parentContext.RootOperationName)) { - itemContext.Name = parentContext.RootOperationName; + itemOperationContext.Name = parentContext.RootOperationName; } if (parentContext.CorrelationContext != null) diff --git a/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs b/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs index 36d3b040ee..8d0bc69692 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; + using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.ApplicationInsights.Channel; @@ -27,6 +28,7 @@ public sealed class TelemetryConfiguration : IDisposable private static object syncRoot = new object(); private static TelemetryConfiguration active; + private bool enableW3c; private readonly SnapshottingList telemetryInitializers = new SnapshottingList(); private readonly TelemetrySinkCollection telemetrySinks = new TelemetrySinkCollection(); @@ -74,6 +76,7 @@ public TelemetryConfiguration(string instrumentationKey, ITelemetryChannel chann var defaultSink = new TelemetrySink(this, channel); defaultSink.Name = "default"; this.telemetrySinks.Add(defaultSink); + this.EnableW3CCorrelation = true; } /// @@ -169,6 +172,34 @@ public bool DisableTelemetry } } + /// + /// Gets or sets a flag indicating whether W3C based correlation is enabled. + /// + public bool EnableW3CCorrelation + { + get + { + return enableW3c; + } + set + { + enableW3c = value; + ActivityExtensions.TryRun(() => + { + if (enableW3c) + { + Activity.DefaultIdFormat = ActivityIdFormat.W3C; + Activity.ForceDefaultIdFormat = true; + } + else + { + Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical; + Activity.ForceDefaultIdFormat = true; + } + }); + } + } + /// /// Gets the list of objects that supply additional information about telemetry. /// diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs index 0970ba1781..9e68e3f40b 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs @@ -285,17 +285,17 @@ public static void UpdateTelemetry(this Activity activity, ITelemetry telemetry, return; } #endif - opTelemetry.Id = FormatRequestId(telemetry.Context.Operation.Id, spanId); + opTelemetry.Id = FormatTelemetryId(telemetry.Context.Operation.Id, spanId); if (parentSpanId != null) { telemetry.Context.Operation.ParentId = - FormatRequestId(telemetry.Context.Operation.Id, parentSpanId); + FormatTelemetryId(telemetry.Context.Operation.Id, parentSpanId); } } else { telemetry.Context.Operation.ParentId = - FormatRequestId(telemetry.Context.Operation.Id, spanId); + FormatTelemetryId(telemetry.Context.Operation.Id, spanId); } if (opTelemetry != null) @@ -377,9 +377,14 @@ private static bool IsValidTelemetryId(string id, string operationId) } #endif - private static string FormatRequestId(string traceId, string spanId) + public static string FormatTelemetryId(string traceId, string spanId) { return string.Concat("|", traceId, ".", spanId, "."); } + + public static bool IsCompatibleW3CTraceID(string traceId) + { + return TraceIdRegex.IsMatch(traceId); + } } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3COperationCorrelationTelemetryInitializer.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3COperationCorrelationTelemetryInitializer.cs index 044280c04e..5f2ba16f6e 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3COperationCorrelationTelemetryInitializer.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3COperationCorrelationTelemetryInitializer.cs @@ -1,5 +1,6 @@ namespace Microsoft.ApplicationInsights.Extensibility.W3C { + using System; using System.ComponentModel; using System.Diagnostics; using Microsoft.ApplicationInsights.Channel; @@ -9,6 +10,7 @@ /// Telemetry Initializer that sets correlation ids for W3C. /// [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Obsolete in favor of OperationCorrelationTelemetryInitializer which is now W3C aware.")] public class W3COperationCorrelationTelemetryInitializer : ITelemetryInitializer { /// @@ -17,8 +19,8 @@ public class W3COperationCorrelationTelemetryInitializer : ITelemetryInitializer /// Telemetry item. public void Initialize(ITelemetry telemetry) { - Activity currentActivity = Activity.Current; - currentActivity.UpdateTelemetry(telemetry, false); + // No op. This is no longer needed as OperationCorrelationTelemetryInitializer does the job. + // Since this class was part of Public API, not removing this, but keeping it no-op } } } diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index d51f2fe2ca..c8550c0bb3 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -7,6 +7,7 @@ using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; + using Microsoft.ApplicationInsights.Extensibility.W3C; /// /// Extension class to telemetry client that creates operation object with the respective fields initialized. @@ -53,6 +54,10 @@ public static class TelemetryClientExtensions if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.Id) && !string.IsNullOrEmpty(operationId)) { + if (Activity.DefaultIdFormat == ActivityIdFormat.W3C && !W3CActivityExtensions.IsCompatibleW3CTraceID(operationId)) + { + operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, operationId); + } operationTelemetry.Context.Operation.Id = operationId; } @@ -133,11 +138,29 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet if (parentActivity == null) { // telemetryContext.Id is always set: if it was null, it is set to opTelemetry.Id and opTelemetry.Id is never null - operationActivity.SetParentId(telemetryContext.Id); + if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) //w3c and telemetryContext.Id is w3c compatible) + { + if(W3CActivityExtensions.IsCompatibleW3CTraceID(telemetryContext.Id)) + { + operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), ActivitySpanId.CreateRandom()); + } + } + else + { + operationActivity.SetParentId(telemetryContext.Id); + } } operationActivity.Start(); - operationTelemetry.Id = operationActivity.Id; + if (operationActivity.IdFormat == ActivityIdFormat.W3C) + { + operationTelemetry.Id = W3CActivityExtensions.FormatTelemetryId(operationActivity.TraceId.ToHexString(), operationActivity.SpanId.ToHexString()); + operationTelemetry.Context.Operation.Id = operationActivity.TraceId.ToHexString(); + } + else + { + operationTelemetry.Id = operationActivity.Id; + } }); var operationHolder = new OperationHolder(telemetryClient, operationTelemetry); From 82f7f43b35a522daa77281e13d45381113558a54 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Fri, 16 Aug 2019 13:21:18 -0700 Subject: [PATCH 02/19] UnitTests for TelemetryClient StartOperation --- .../Shared/TelemetryClientExtensionTests.cs | 304 +++++++++++++++++- .../TelemetryClientExtensions.cs | 2 +- 2 files changed, 302 insertions(+), 4 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs index 2b27978ed4..39db2ac9ad 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs @@ -15,6 +15,11 @@ [TestClass] public class TelemetryClientExtensionTests { + const string NonW3CCompatileOperationId = "NonCompliantRootId"; + const string W3CCompatileOperationId = "8ee8641cbdd8dd280d239fa2121c7e4e"; + const string AnyRootId = "ANYID"; + const string AnyParentId = "ANYParentID"; + private TelemetryClient telemetryClient; private TelemetryConfiguration telemetryConfiguration; private List sendItems; @@ -119,8 +124,31 @@ public void UsingWithStopOperationSendsTelemetryAndDisposesOperationItemOnlyOnce } [TestMethod] - public void StartDependencyTrackingHandlesMultipleContextStoresInCurrentActivity() + public void StartDependencyTrackingHandlesMultipleContextStoresInCurrentActivityW3C() + { + var operation = this.telemetryClient.StartOperation("OperationName") as OperationHolder; + var currentActivity = Activity.Current; + Assert.AreEqual(operation.Telemetry.Id, W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString())); + Assert.AreEqual(operation.Telemetry.Context.Operation.Name, this.GetOperationName(currentActivity)); + + var childOperation = this.telemetryClient.StartOperation("OperationName") as OperationHolder; + var childActivity = Activity.Current; + Assert.AreEqual(childOperation.Telemetry.Id, W3CActivityExtensions.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); + } + + [TestMethod] + public void StartDependencyTrackingHandlesMultipleContextStoresInCurrentActivityNonW3C() { + this.telemetryConfiguration.EnableW3CCorrelation = false; var operation = this.telemetryClient.StartOperation("OperationName") as OperationHolder; var currentActivity = Activity.Current; Assert.AreEqual(operation.Telemetry.Id, currentActivity.Id); @@ -207,8 +235,30 @@ public void DisposeOperationAppliesChangesOnActivityDoneAfterStart() } [TestMethod] - public void ContextPropagatesThroughNestedOperations() + public void ContextPropagatesThroughNestedOperationsW3C() + { + using (this.telemetryClient.StartOperation("OuterRequest")) + { + using (this.telemetryClient.StartOperation("DependentCall")) + { + } + } + + Assert.AreEqual(2, this.sendItems.Count); + + var requestTelemetry = (RequestTelemetry)this.sendItems[1]; + var dependentTelemetry = (DependencyTelemetry)this.sendItems[0]; + Assert.IsNull(requestTelemetry.Context.Operation.ParentId); + Assert.AreEqual(requestTelemetry.Id, dependentTelemetry.Context.Operation.ParentId); + Assert.AreEqual(requestTelemetry.Context.Operation.Id, dependentTelemetry.Context.Operation.Id); + Assert.AreEqual(requestTelemetry.Context.Operation.Name, dependentTelemetry.Context.Operation.Name); + } + + [TestMethod] + public void ContextPropagatesThroughNestedOperationsNonW3C() { + this.telemetryConfiguration.EnableW3CCorrelation = false; + using (this.telemetryClient.StartOperation("OuterRequest")) { using (this.telemetryClient.StartOperation("DependentCall")) @@ -271,8 +321,31 @@ public void StartOperationOperationIdIsUsedIfW3cCompatible() } [TestMethod] - public void StartOperationCanOverrideRootAndParentOperationId() + public void StartOperationCanOverrideRootAndParentOperationIdNonW3C() + { + this.telemetryConfiguration.EnableW3CCorrelation = false; + + using (this.telemetryClient.StartOperation("Request", operationId: "ROOT", parentOperationId: "PARENT")) + { + this.telemetryClient.TrackTrace("child trace"); + } + + Assert.AreEqual(2, this.sendItems.Count); + + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + Assert.AreEqual("PARENT", requestTelemetry.Context.Operation.ParentId); + Assert.AreEqual("ROOT", requestTelemetry.Context.Operation.Id); + + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + Assert.AreEqual(requestTelemetry.Id, traceTelemetry.Context.Operation.ParentId); + Assert.AreEqual("ROOT", traceTelemetry.Context.Operation.Id); + } + + [TestMethod] + public void StartOperationCanOverrideRootAndParentOperationIdNotW3CCompatible() { + this.telemetryConfiguration.EnableW3CCorrelation = false; + using (this.telemetryClient.StartOperation("Request", operationId: "ROOT", parentOperationId: "PARENT")) { this.telemetryClient.TrackTrace("child trace"); @@ -289,6 +362,231 @@ public void StartOperationCanOverrideRootAndParentOperationId() Assert.AreEqual("ROOT", traceTelemetry.Context.Operation.Id); } + // + [TestMethod] + public void StartOperationPopulatesContextCorrectlyW3C() + { + // Act - start an operation, and generate telemetry inside it. + using (this.telemetryClient.StartOperation("Request")) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + + private void ValidateRootTelemetry(OperationTelemetry operationTelemetry, string expectedOperationId = "", string expectedOperationParentId = null, bool isW3C = true) + { + Assert.AreEqual(expectedOperationParentId, operationTelemetry.Context.Operation.ParentId); + Assert.IsNotNull(operationTelemetry.Context.Operation.Id); + + if (!string.IsNullOrEmpty(expectedOperationId)) + { + Assert.AreEqual(expectedOperationId, operationTelemetry.Context.Operation.Id); + } + + if (isW3C) + { + Assert.IsTrue(W3CActivityExtensions.IsCompatibleW3CTraceID(operationTelemetry.Context.Operation.Id)); + } + Assert.IsNotNull(operationTelemetry.Id); + // ID is shaped like |TraceID.SpanID. + Assert.IsTrue(operationTelemetry.Id.Contains(operationTelemetry.Context.Operation.Id)); + } + + private void ValidateChildTelemetry(OperationTelemetry rootOperationTelemetry, ITelemetry childTelemetry) + { + Assert.AreEqual(rootOperationTelemetry.Id, childTelemetry.Context.Operation.ParentId); + Assert.AreEqual(rootOperationTelemetry.Context.Operation.Id, childTelemetry.Context.Operation.Id, "OperationID should be same for all operations in same context"); + } + + [TestMethod] + public void StartOperationPopulatesContextCorrectlyNonW3C() + { + this.telemetryConfiguration.EnableW3CCorrelation = false; + // Act - start an operation, and generate telemetry inside it. + using (this.telemetryClient.StartOperation("Request")) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry, isW3C: false); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + + [TestMethod] + public void StartOperationPopulatesContextCorrectlyWithOverridingNonW3CCompatibleRootIdW3C() + { + // Act - start an operation, supply operation ID which is NOT W3C compatible, and generate a telemetry inside it. + using (this.telemetryClient.StartOperation("Request", operationId: NonW3CCompatileOperationId)) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + // The user provided operationid will be ignore as it is not W3C compatible, and it will + // be stored inside custom property. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry); + + // Additional Validations. + Assert.AreNotEqual(NonW3CCompatileOperationId, requestTelemetry.Context.Operation.Id, "Non compatible operation id supplied by user should be ignored in W3C mode."); + Assert.AreEqual(NonW3CCompatileOperationId, requestTelemetry.Properties[W3CConstants.LegacyRootIdProperty], "Non compatible operation id supplied by user should be stored in custom property"); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + + [TestMethod] + public void StartOperationPopulatesContextCorrectlyWithOverridingW3CCompatibleRootIdW3C() + { + // Act - start an operation, supply operation ID which is NOT W3C compatible, and generate a telemetry inside it. + using (this.telemetryClient.StartOperation("Request", operationId: W3CCompatileOperationId)) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + // The user provided operationid will be used as it is W3C compatible. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry, expectedOperationId:W3CCompatileOperationId); + Assert.AreEqual(W3CCompatileOperationId, requestTelemetry.Context.Operation.Id, "W3C compatible operation id supplied by user should be used."); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + + [TestMethod] + [Description("For NonW3C, Validate that any root id supplied by user will be respected.")] + public void StartOperationPopulatesContextCorrectlyWithAnyOverridingRootIdNonW3C() + { + this.telemetryConfiguration.EnableW3CCorrelation = false; + + // Act - start an operation, supply ANY operation ID, and generate a telemetry inside it. + using (this.telemetryClient.StartOperation("Request", operationId: AnyRootId)) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + // The user provided operationid will be used as is. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry, expectedOperationId: AnyRootId, isW3C:false); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + + [TestMethod] + [Description("For W3C, Validate that any parentid id supplied by user will be respected.")] + public void StartOperationPopulatesContextCorrectlyWithAnyOverridingParentIdW3C() + { + // Act - start an operation, supply ANY parent operation ID, and generate a telemetry inside it. + using (this.telemetryClient.StartOperation("Request", operationId: W3CCompatileOperationId, parentOperationId: AnyParentId)) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + // The user provided parent operationid will be used as is. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry, expectedOperationParentId: AnyParentId); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + + [TestMethod] + [Description("For Non W3C, Validate that any parentid id supplied by user will be respected.")] + public void StartOperationPopulatesContextCorrectlyWithAnyOverridingParentIdNonW3C() + { + this.telemetryConfiguration.EnableW3CCorrelation = false; + + // Act - start an operation, supply ANY parent operation ID, and generate a telemetry inside it. + using (this.telemetryClient.StartOperation("Request", operationId: AnyRootId, parentOperationId: AnyParentId)) + { + this.telemetryClient.TrackTrace("child trace"); + this.telemetryClient.TrackEvent("child event"); + } + + Assert.AreEqual(3, this.sendItems.Count); + + // The RequestTelemetry is the root operation here. + // The user provided parent operationid will be used as is. + var requestTelemetry = (RequestTelemetry)this.sendItems.Single(t => t is RequestTelemetry); + ValidateRootTelemetry(requestTelemetry, expectedOperationParentId: AnyParentId, isW3C:false); + + // The generated TraceTelemetry should become the child of the root RequestTelemetry + var traceTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, traceTelemetry); + + + // The generated EventTelemetry should become the child of the root RequestTelemetry + var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); + ValidateChildTelemetry(requestTelemetry, eventTelemetry); + } + // + [TestMethod] [ExpectedException(typeof(ArgumentNullException))] public void StartOperationThrowsOnNullOperationTelemetry() diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index c8550c0bb3..47c9e314e3 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -138,7 +138,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet if (parentActivity == null) { // telemetryContext.Id is always set: if it was null, it is set to opTelemetry.Id and opTelemetry.Id is never null - if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) //w3c and telemetryContext.Id is w3c compatible) + if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { if(W3CActivityExtensions.IsCompatibleW3CTraceID(telemetryContext.Id)) { From 2970c71729b1bf5bda87a8b62bbecaabc3c0947a Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Fri, 16 Aug 2019 13:51:10 -0700 Subject: [PATCH 03/19] comments. --- .../TelemetryConfigurationTest.cs | 6 ++-- ...perationCorrelationTelemetryInitializer.cs | 33 ++++++++----------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs index ca646c867c..2618051e57 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/TelemetryConfigurationTest.cs @@ -25,7 +25,6 @@ public void TelemetryConfigurationConstructorSetsW3CToTrueByDefault() [TestMethod] public void TelemetryConfigurationEnableW3CCorrelationSetsActivityDefaultFormatToW3C() { - var original = Activity.DefaultIdFormat; var tc = new TelemetryConfiguration(); tc.EnableW3CCorrelation = true; Assert.AreEqual(ActivityIdFormat.W3C, Activity.DefaultIdFormat); @@ -34,10 +33,11 @@ public void TelemetryConfigurationEnableW3CCorrelationSetsActivityDefaultFormatT [TestMethod] public void TelemetryConfigurationDisableW3CCorrelationRestoresActivityDefaultFormat() { - var original = Activity.DefaultIdFormat; + Activity.DefaultIdFormat = ActivityIdFormat.Hierarchical; var tc = new TelemetryConfiguration(); + // DisablingW3C should reset default id format to Hierrachical tc.EnableW3CCorrelation = false; - Assert.AreEqual(original, Activity.DefaultIdFormat); + Assert.AreEqual(ActivityIdFormat.Hierarchical, Activity.DefaultIdFormat); } #endregion diff --git a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs index 7ab2488f41..0d3022ed62 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs @@ -11,6 +11,7 @@ /// /// Telemetry initializer that populates OperationContext for the telemetry item from Activity. + /// This initializer is responsible for correlation of telemetry items within the same process. /// #if NET45 /// @@ -43,23 +44,17 @@ public void Initialize(ITelemetry telemetryItem) if (string.IsNullOrEmpty(itemOperationContext.Id)) { // Set OperationID to Activity.TraceId - // itemOperationContext.Id = currentActivity.RootId; // heck if this can be used + // itemOperationContext.Id = currentActivity.RootId; // check if this can be used itemOperationContext.Id = currentActivity.TraceId.ToHexString(); - // Set OperationParentID to ID of parent constructed from traceid, spand id. - // ID for auto collected Request,Dependency are made from trace id + span id, so parentid must be set to the same format. + // 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 = W3CActivityExtensions.FormatTelemetryId(itemOperationContext.Id, currentActivity.SpanId.ToHexString()); } - - foreach (var baggage in currentActivity.Baggage) - { - if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(baggage.Key)) - { - telemetryProp.Properties.Add(baggage); - } - } } } else @@ -71,15 +66,15 @@ public void Initialize(ITelemetry telemetryItem) 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); - } - } + foreach (var baggage in currentActivity.Baggage) + { + if (telemetryProp != null && !telemetryProp.Properties.ContainsKey(baggage.Key)) + { + telemetryProp.Properties.Add(baggage); } } From 37bfc9638ee9f68a664227b3b17268261d494f7f Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Fri, 16 Aug 2019 14:09:26 -0700 Subject: [PATCH 04/19] some refactorings --- ...ionCorrelationTelemetryInitializerTests.cs | 6 +- ...ApplicationInsights.Shared.Tests.projitems | 1 - .../Shared/TelemetryClientExtensionTests.cs | 6 +- .../Shared/W3C/W3CActivityExtensionsTests.cs | 399 ------------------ .../Implementation/OperationHolder.cs | 2 +- ...perationCorrelationTelemetryInitializer.cs | 36 +- .../W3C/W3CActivityExtensions.cs | 390 ----------------- .../Extensibility/W3C/W3CConstants.cs | 50 --- .../Extensibility/W3C/W3CUtilities.cs | 22 + .../TelemetryClientExtensions.cs | 6 +- 10 files changed, 47 insertions(+), 871 deletions(-) delete mode 100644 Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CActivityExtensionsTests.cs delete mode 100644 src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs index 68aa203e0c..21b1ca78ce 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/Extensibility/OperationCorrelationTelemetryInitializerTests.cs @@ -69,7 +69,7 @@ public void InitializePopulatesOperationContextFromActivity() // Validate Assert.AreEqual(activity.TraceId.ToHexString(), telemetry.Context.Operation.Id, "OperationCorrelationTelemetryInitializer is expected to populate OperationID from Activity"); - Assert.AreEqual(W3CActivityExtensions.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString()), + 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"); @@ -248,7 +248,7 @@ public void InitializeSetsBaggage() (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id); - Assert.AreEqual(W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()), telemetry.Context.Operation.ParentId); + 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); @@ -270,7 +270,7 @@ public void InitilaizeWithActivityWinsOverCallContext() (new OperationCorrelationTelemetryInitializer()).Initialize(telemetry); Assert.AreEqual(currentActivity.TraceId.ToHexString(), telemetry.Context.Operation.Id); - Assert.AreEqual(W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()), telemetry.Context.Operation.ParentId); + 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); diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems b/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems index 1fe7fb640e..71a7b4ca53 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/Microsoft.ApplicationInsights.Shared.Tests.projitems @@ -123,7 +123,6 @@ - diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs index 39db2ac9ad..6610f6c377 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs @@ -128,12 +128,12 @@ public void StartDependencyTrackingHandlesMultipleContextStoresInCurrentActivity { var operation = this.telemetryClient.StartOperation("OperationName") as OperationHolder; var currentActivity = Activity.Current; - Assert.AreEqual(operation.Telemetry.Id, W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString())); + 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("OperationName") as OperationHolder; var childActivity = Activity.Current; - Assert.AreEqual(childOperation.Telemetry.Id, W3CActivityExtensions.FormatTelemetryId(childActivity.TraceId.ToHexString(), childActivity.SpanId.ToHexString())); + 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); @@ -400,7 +400,7 @@ private void ValidateRootTelemetry(OperationTelemetry operationTelemetry, string if (isW3C) { - Assert.IsTrue(W3CActivityExtensions.IsCompatibleW3CTraceID(operationTelemetry.Context.Operation.Id)); + Assert.IsTrue(W3CUtilities.IsCompatibleW3CTraceID(operationTelemetry.Context.Operation.Id)); } Assert.IsNotNull(operationTelemetry.Id); // ID is shaped like |TraceID.SpanID. diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CActivityExtensionsTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CActivityExtensionsTests.cs deleted file mode 100644 index 2e7f06e44a..0000000000 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CActivityExtensionsTests.cs +++ /dev/null @@ -1,399 +0,0 @@ -using Microsoft.ApplicationInsights.DataContracts; - -namespace Microsoft.ApplicationInsights.W3C -{ - using System.Diagnostics; - using System.Linq; - using Microsoft.ApplicationInsights.Extensibility.W3C; - using Microsoft.VisualStudio.TestTools.UnitTesting; - - [TestClass] - public class W3CActivityExtensionsTests - { - private const string TraceId = "01010101010101010101010101010101"; - private const string ParenSpanId = "0202020202020202"; - - [TestCleanup] - public void Cleanup() - { - while (Activity.Current != null) - { - Activity.Current.Stop(); - } - } - - [TestMethod] - public void SetInvalidTraceParent() - { - var invalidTraceParents = new[] - { - "123", string.Empty, null, "00-00", "00-00-00", "00-00-00-", "-00-00-00", "00-00-00-00-00", - "00-00-00- ", " -00-00-00", "---", "00---", "00-00--", "00--00-", "00---00" - }; - foreach (var traceparent in invalidTraceParents) - { - var a = new Activity("foo"); - a.SetTraceparent(traceparent); - - Assert.IsFalse(a.Tags.Any(t => t.Key == W3CConstants.ParentSpanIdTag), traceparent); - Assert.IsNull(a.GetParentSpanId()); - Assert.IsNull(a.GetTracestate()); - - Assert.AreEqual(W3CConstants.DefaultVersion, a.Tags.Single(t => t.Key == W3CConstants.VersionTag).Value, traceparent); - Assert.AreEqual(W3CConstants.TraceFlagRecordedAndNotRequested, a.Tags.Single(t => t.Key == W3CConstants.SampledTag).Value, traceparent); - - Assert.IsTrue(a.IsW3CActivity(), traceparent); - Assert.AreEqual(32, a.GetTraceId().Length, traceparent); - Assert.AreEqual(16, a.GetSpanId().Length, traceparent); - - Assert.AreEqual($"{W3CConstants.DefaultVersion}-{a.GetTraceId()}-{a.GetSpanId()}-{W3CConstants.TraceFlagRecordedAndNotRequested}", a.GetTraceparent(), traceparent); - } - } - - [TestMethod] - public void InvalidTraceIdAllTraceparentIsIgnored() - { - var invalidTraceIds = new[] - { - "123", - "000102030405060708090a0b0c0d0f", // 30 chars - "000102030405060708090a0b0c0d0f0", // 31 char - "000102030405060708090a0b0c0d0f0g", // 32 char non-hex - "000102030405060708090a0b0c0d0f0A", // 32 char upper case - "000102030405060708090a0b0c0d0f000" // 33 chars - }; - foreach (var traceId in invalidTraceIds) - { - var a = new Activity("foo"); - - a.SetTraceparent($"00-{traceId}-{ParenSpanId}-00"); - - Assert.IsFalse(a.Tags.Any(t => t.Key == W3CConstants.ParentSpanIdTag), traceId); - Assert.IsNull(a.GetParentSpanId()); - Assert.IsNull(a.GetTracestate()); - - Assert.AreEqual(W3CConstants.DefaultVersion, a.Tags.Single(t => t.Key == W3CConstants.VersionTag).Value, traceId); - Assert.AreEqual(W3CConstants.TraceFlagRecordedAndNotRequested, a.Tags.Single(t => t.Key == W3CConstants.SampledTag).Value, traceId); - - Assert.IsTrue(a.IsW3CActivity(), traceId); - Assert.AreEqual(32, a.GetTraceId().Length, traceId); - Assert.AreEqual(16, a.GetSpanId().Length, traceId); - - Assert.AreEqual($"{W3CConstants.DefaultVersion}-{a.GetTraceId()}-{a.GetSpanId()}-{W3CConstants.TraceFlagRecordedAndNotRequested}", a.GetTraceparent(), traceId); - } - } - - [TestMethod] - public void InvalidSapnIdAllTraceparentIsIgnored() - { - var invalidSpanIds = new[] - { - "123", - "00010203040506", // 14 chars - "000102030405060", // 15 char - "000102030405060g", // 16 char non-hex - "000102030405060A", // 16 char upper case - "00010203040506070" // 15 chars - }; - foreach (var parentSpanId in invalidSpanIds) - { - var a = new Activity("foo"); - - a.SetTraceparent($"00-{TraceId}-{parentSpanId}-00"); - - Assert.IsFalse(a.Tags.Any(t => t.Key == W3CConstants.ParentSpanIdTag), parentSpanId); - Assert.IsNull(a.GetParentSpanId()); - Assert.IsNull(a.GetTracestate()); - - Assert.AreEqual(W3CConstants.DefaultVersion, a.Tags.Single(t => t.Key == W3CConstants.VersionTag).Value, parentSpanId); - Assert.AreEqual(W3CConstants.TraceFlagRecordedAndNotRequested, a.Tags.Single(t => t.Key == W3CConstants.SampledTag).Value, parentSpanId); - - Assert.IsTrue(a.IsW3CActivity(), parentSpanId); - Assert.AreEqual(32, a.GetTraceId().Length, parentSpanId); - Assert.AreEqual(16, a.GetSpanId().Length, parentSpanId); - - Assert.AreEqual($"{W3CConstants.DefaultVersion}-{a.GetTraceId()}-{a.GetSpanId()}-{W3CConstants.TraceFlagRecordedAndNotRequested}", a.GetTraceparent(), parentSpanId); - } - } - - [TestMethod] - public void SetValidTraceParent() - { - var a = new Activity("foo"); - a.SetTraceparent($"00-{TraceId}-{ParenSpanId}-00"); - - Assert.IsTrue(a.IsW3CActivity()); - Assert.AreEqual(TraceId, a.Tags.SingleOrDefault(t => t.Key == W3CConstants.TraceIdTag).Value); - Assert.AreEqual(ParenSpanId, a.Tags.SingleOrDefault(t => t.Key == W3CConstants.ParentSpanIdTag).Value); - Assert.IsNotNull(a.Tags.SingleOrDefault(t => t.Key == W3CConstants.SpanIdTag)); - Assert.AreEqual(16, a.Tags.Single(t => t.Key == W3CConstants.SpanIdTag).Value.Length); - Assert.AreEqual(W3CConstants.TraceFlagRecordedAndNotRequested, a.Tags.SingleOrDefault(t => t.Key == W3CConstants.SampledTag).Value); - Assert.AreEqual(W3CConstants.DefaultVersion, a.Tags.SingleOrDefault(t => t.Key == W3CConstants.VersionTag).Value); - - Assert.AreEqual(TraceId, a.GetTraceId()); - Assert.AreEqual(ParenSpanId, a.GetParentSpanId()); - Assert.IsNotNull(a.GetSpanId()); - Assert.AreEqual(a.Tags.Single(t => t.Key == W3CConstants.SpanIdTag).Value, a.GetSpanId()); - Assert.AreEqual($"{W3CConstants.DefaultVersion}-{TraceId}-{a.GetSpanId()}-{W3CConstants.TraceFlagRecordedAndNotRequested}", a.GetTraceparent()); - Assert.IsNull(a.GetTracestate()); - } - - [TestMethod] - public void UpdateContextWithoutParent() - { - var a = new Activity("foo"); - - Assert.IsFalse(a.IsW3CActivity()); - - a.UpdateContextOnActivity(); - Assert.IsTrue(a.IsW3CActivity()); - 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 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() - { - var parent = new Activity("foo").Start(); - parent.SetTraceparent($"00-{TraceId}-{ParenSpanId}-01"); - parent.SetTracestate("some=state"); - var child = new Activity("bar").Start(); - child.UpdateContextOnActivity(); - - Assert.IsTrue(child.IsW3CActivity()); - Assert.AreEqual(TraceId, child.GetTraceId()); - Assert.AreEqual(parent.GetSpanId(), child.GetParentSpanId()); - Assert.AreEqual($"{W3CConstants.DefaultVersion}-{TraceId}-{child.GetSpanId()}-{W3CConstants.TraceFlagRecordedAndRequested}", child.GetTraceparent()); - Assert.AreEqual(parent.GetTracestate(), child.GetTracestate()); - } - - [TestMethod] - public void SetTraceState() - { - var a = new Activity("foo").Start(); - a.SetTracestate("some=state"); - Assert.AreEqual("some=state", a.GetTracestate()); - } - - [TestMethod] - public void UnsupportedVersionsAreIgnored() - { - var a = new Activity("foo").Start(); - a.SetTraceparent($"12-{TraceId}-{ParenSpanId}-00"); - - var b = new Activity("bar").Start(); - b.SetTraceparent($"ff-{TraceId}-{ParenSpanId}-00"); - - Assert.AreEqual($"00-{TraceId}-{a.GetSpanId()}-02", a.GetTraceparent()); - Assert.AreEqual($"00-{TraceId}-{b.GetSpanId()}-02", b.GetTraceparent()); - } - - [TestMethod] - public void RequestedFlagIsRespected() - { - var requestedParents = new[] { "01", "03", "05", "ff" }; - var notRequestedParents = new[] { "00", "02", "04", "fe" }; - - foreach (var req in requestedParents) - { - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{TraceId}-{ParenSpanId}-{req}"); - Assert.AreEqual($"00-{TraceId}-{a.GetSpanId()}-03", a.GetTraceparent(), req); - } - - foreach (var notReq in notRequestedParents) - { - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{TraceId}-{ParenSpanId}-{notReq}"); - Assert.AreEqual($"00-{TraceId}-{a.GetSpanId()}-02", a.GetTraceparent(), notReq); - } - } - - [TestMethod] - public void UpdateValidRequestTelemetryWithForceFalse() - { - var traceId = W3CUtilities.GenerateTraceId(); - var parentSpanId = W3CUtilities.GenerateSpanId(); - var spanId = W3CUtilities.GenerateSpanId(); - - var telemetry = new RequestTelemetry(); - telemetry.Context.Operation.Id = traceId; - telemetry.Context.Operation.ParentId = $"|{traceId}.{parentSpanId}."; - telemetry.Id = $"|{traceId}.{spanId}."; - - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{traceId}-{spanId}-01"); - - a.UpdateTelemetry(telemetry, false); - - Assert.AreEqual(traceId, telemetry.Context.Operation.Id); - -#if NET45 || NET46 - Assert.AreEqual($"|{traceId}.{parentSpanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Id); -#else - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{a.GetSpanId()}.", telemetry.Id); -#endif - } - - [TestMethod] - public void UpdateValidRequestTelemetryWithForceInvalidIdFalse() - { - var traceId = W3CUtilities.GenerateTraceId(); - var parentSpanId = W3CUtilities.GenerateSpanId(); - var spanId = W3CUtilities.GenerateSpanId(); - - var telemetry = new RequestTelemetry(); - telemetry.Context.Operation.Id = traceId; - telemetry.Context.Operation.ParentId = $"|{traceId}.{parentSpanId}."; - telemetry.Id = "|123.456."; - - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{traceId}-{spanId}-01"); - - a.UpdateTelemetry(telemetry, false); - - Assert.AreEqual(traceId, telemetry.Context.Operation.Id); - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{a.GetSpanId()}.", telemetry.Id); - } - - [TestMethod] - public void UpdateValidRequestTelemetryWithForceTrue() - { - var traceId = W3CUtilities.GenerateTraceId(); - var parentSpanId = W3CUtilities.GenerateSpanId(); - var spanId = W3CUtilities.GenerateSpanId(); - - var telemetry = new RequestTelemetry(); - telemetry.Context.Operation.Id = traceId; - telemetry.Context.Operation.ParentId = $"|{traceId}.{parentSpanId}."; - telemetry.Id = $"|{traceId}.{spanId}."; - - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{traceId}-{spanId}-01"); - - a.UpdateTelemetry(telemetry, true); - - Assert.AreEqual(traceId, telemetry.Context.Operation.Id); - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{a.GetSpanId()}.", telemetry.Id); - } - - [TestMethod] - public void UpdateValidDependencyTelemetryWithForceFalse() - { - var traceId = W3CUtilities.GenerateTraceId(); - var parentSpanId = W3CUtilities.GenerateSpanId(); - var spanId = W3CUtilities.GenerateSpanId(); - - var telemetry = new DependencyTelemetry(); - telemetry.Context.Operation.Id = traceId; - telemetry.Context.Operation.ParentId = $"|{traceId}.{parentSpanId}."; - telemetry.Id = $"|{traceId}.{spanId}."; - - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{traceId}-{spanId}-01"); - - a.UpdateTelemetry(telemetry, false); - - Assert.AreEqual(traceId, telemetry.Context.Operation.Id); -#if NET45 || NET46 - Assert.AreEqual($"|{traceId}.{parentSpanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Id); -#else - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{a.GetSpanId()}.", telemetry.Id); -#endif - } - - [TestMethod] - public void UpdateValidDependencyTelemetryWithForceInvalidIdFalse() - { - var traceId = W3CUtilities.GenerateTraceId(); - var parentSpanId = W3CUtilities.GenerateSpanId(); - var spanId = W3CUtilities.GenerateSpanId(); - - var telemetry = new DependencyTelemetry(); - telemetry.Context.Operation.Id = traceId; - telemetry.Context.Operation.ParentId = $"|{traceId}.{parentSpanId}."; - telemetry.Id = "|123.456."; - - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{traceId}-{spanId}-01"); - - a.UpdateTelemetry(telemetry, false); - - Assert.AreEqual(traceId, telemetry.Context.Operation.Id); - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{a.GetSpanId()}.", telemetry.Id); - } - - [TestMethod] - public void UpdateValidDependencyTelemetryTelemetryWithForceTrue() - { - var traceId = W3CUtilities.GenerateTraceId(); - var parentSpanId = W3CUtilities.GenerateSpanId(); - var spanId = W3CUtilities.GenerateSpanId(); - - var telemetry = new DependencyTelemetry(); - telemetry.Context.Operation.Id = traceId; - telemetry.Context.Operation.ParentId = $"|{traceId}.{parentSpanId}."; - telemetry.Id = $"|{traceId}.{spanId}."; - - var a = new Activity("foo").Start(); - a.SetTraceparent($"00-{traceId}-{spanId}-01"); - - a.UpdateTelemetry(telemetry, true); - - Assert.AreEqual(traceId, telemetry.Context.Operation.Id); - Assert.AreEqual($"|{traceId}.{spanId}.", telemetry.Context.Operation.ParentId); - Assert.AreEqual($"|{traceId}.{a.GetSpanId()}.", telemetry.Id); - } - } -} diff --git a/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs b/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs index 74c7f803be..c84397bf32 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs @@ -71,7 +71,7 @@ protected virtual void Dispose(bool disposing) var currentActivity = Activity.Current; if (currentActivity == null || (Activity.DefaultIdFormat != ActivityIdFormat.W3C && operationTelemetry.Id != currentActivity.Id) - || (Activity.DefaultIdFormat == ActivityIdFormat.W3C && operationTelemetry.Id != W3CActivityExtensions.FormatTelemetryId(currentActivity.TraceId.ToHexString(), currentActivity.SpanId.ToHexString()) )) + || (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 diff --git a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs index 0d3022ed62..73a56f579b 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs @@ -37,36 +37,30 @@ public void Initialize(ITelemetry telemetryItem) isActivityAvailable = ActivityExtensions.TryRun(() => { var currentActivity = Activity.Current; - if (currentActivity != null) + if (currentActivity != null && string.IsNullOrEmpty(itemOperationContext.Id)) { if (currentActivity.IdFormat == ActivityIdFormat.W3C) { - if (string.IsNullOrEmpty(itemOperationContext.Id)) - { - // Set OperationID to Activity.TraceId - // itemOperationContext.Id = currentActivity.RootId; // check if this can be used - itemOperationContext.Id = currentActivity.TraceId.ToHexString(); + // 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 = W3CActivityExtensions.FormatTelemetryId(itemOperationContext.Id, currentActivity.SpanId.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 { - if (string.IsNullOrEmpty(itemOperationContext.Id)) - { - itemOperationContext.Id = currentActivity.RootId; + itemOperationContext.Id = currentActivity.RootId; - if (string.IsNullOrEmpty(itemOperationContext.ParentId)) - { - itemOperationContext.ParentId = currentActivity.Id; - } + if (string.IsNullOrEmpty(itemOperationContext.ParentId)) + { + itemOperationContext.ParentId = currentActivity.Id; } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs deleted file mode 100644 index 9e68e3f40b..0000000000 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs +++ /dev/null @@ -1,390 +0,0 @@ -namespace Microsoft.ApplicationInsights.Extensibility.W3C -{ - using System; - using System.ComponentModel; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Linq; - using System.Text.RegularExpressions; - using Microsoft.ApplicationInsights.Channel; - using Microsoft.ApplicationInsights.DataContracts; - using Microsoft.ApplicationInsights.Extensibility.Implementation; - - /// - /// Extends Activity to support W3C distributed tracing standard. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public static class W3CActivityExtensions - { - private const string RddDiagnosticSourcePrefix = "rdddsc"; - private const string SqlRemoteDependencyType = "SQL"; - - private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); - private static readonly Regex SpanIdRegex = new Regex("^[a-f0-9]{16}$", RegexOptions.Compiled); - - /// - /// Generate new W3C context. - /// - /// Activity to generate W3C context on. - /// The same Activity for chaining. - [EditorBrowsable(EditorBrowsableState.Never)] - public static Activity GenerateW3CContext(this Activity activity) - { - activity.SetVersion(W3CConstants.DefaultVersion); - activity.SetSampled(W3CConstants.TraceFlagRecordedAndNotRequested); - activity.SetSpanId(W3CUtilities.GenerateSpanId()); - - activity.SetTraceId(activity.RootId != null && TraceIdRegex.IsMatch(activity.RootId) - ? activity.RootId - : W3CUtilities.GenerateTraceId()); - - return activity; - } - - /// - /// Checks if current Activity has W3C properties on it. - /// - /// Activity to check. - /// True if Activity has W3C properties, false otherwise. - [EditorBrowsable(EditorBrowsableState.Never)] - public static bool IsW3CActivity(this Activity activity) - { - return activity != null && activity.Tags.Any(t => t.Key == W3CConstants.TraceIdTag); - } - - /// - /// Updates context on the Activity based on the W3C Context in the parent Activity tree. - /// - /// Activity to update W3C context on. - /// The same Activity for chaining. - [EditorBrowsable(EditorBrowsableState.Never)] - public static Activity UpdateContextOnActivity(this Activity activity) - { - if (activity == null || activity.Tags.Any(t => t.Key == W3CConstants.TraceIdTag)) - { - return activity; - } - - // no w3c Tags on Activity - activity.Parent.UpdateContextOnActivity(); - - // at this point, Parent has W3C tags, but current activity does not - update it - return activity.UpdateContextFromParent(); - } - - /// - /// Gets traceparent header value for the Activity or null if there is no W3C context on it. - /// - /// Activity to read W3C context from. - /// traceparent header value. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GetTraceparent(this Activity activity) - { - string version = null, traceId = null, spanId = null, sampled = null; - foreach (var tag in activity.Tags) - { - switch (tag.Key) - { - case W3CConstants.TraceIdTag: - traceId = tag.Value; - break; - case W3CConstants.SpanIdTag: - spanId = tag.Value; - break; - case W3CConstants.VersionTag: - version = tag.Value; - break; - case W3CConstants.SampledTag: - sampled = tag.Value; - break; - } - } - - if (traceId == null || spanId == null || version == null || sampled == null) - { - return null; - } - - return string.Join("-", version, traceId, spanId, sampled); - } - - /// - /// Initializes W3C context on the Activity from traceparent header value. - /// - /// Activity to set W3C context on. - /// Valid traceparent header like 00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01. - [EditorBrowsable(EditorBrowsableState.Never)] - public static void SetTraceparent(this Activity activity, string value) - { - if (activity.IsW3CActivity()) - { - return; - } - - // we only support 00 version and ignore caller version - activity.SetVersion(W3CConstants.DefaultVersion); - - string traceId = null, parentSpanId = null, sampledStr = null; - bool isValid = false; - - var parts = value?.Split('-'); - if (parts != null && parts.Length == 4) - { - traceId = parts[1]; - parentSpanId = parts[2]; - sampledStr = parts[3]; - isValid = TraceIdRegex.IsMatch(traceId) && SpanIdRegex.IsMatch(parentSpanId); - } - - if (isValid) - { - byte.TryParse(sampledStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var sampled); - - // we always defer sampling - if ((sampled & W3CConstants.RequestedTraceFlag) == W3CConstants.RequestedTraceFlag) - { - activity.SetSampled(W3CConstants.TraceFlagRecordedAndRequested); - } - else - { - activity.SetSampled(W3CConstants.TraceFlagRecordedAndNotRequested); - } - - activity.SetParentSpanId(parentSpanId); - activity.SetSpanId(W3CUtilities.GenerateSpanId()); - activity.SetTraceId(traceId); - } - else - { - activity.SetSampled(W3CConstants.TraceFlagRecordedAndNotRequested); - activity.SetSpanId(W3CUtilities.GenerateSpanId()); - activity.SetTraceId(W3CUtilities.GenerateTraceId()); - } - - if (activity.Id == null) - { - // activity is not started yet - activity.SetParentId(string.Concat("|", activity.GetTraceId(), ".", activity.GetParentSpanId(), ".")); - } - } - - /// - /// Gets tracestate header value from the Activity. - /// - /// Activity to get tracestate from. - /// tracestate header value. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GetTracestate(this Activity activity) => - activity.Tags.FirstOrDefault(t => t.Key == W3CConstants.TracestateTag).Value; - - /// - /// Sets tracestate header value on the Activity. - /// - /// Activity to set tracestate on. - /// tracestate header value. - [EditorBrowsable(EditorBrowsableState.Never)] - public static void SetTracestate(this Activity activity, string value) => - activity.AddTag(W3CConstants.TracestateTag, value); - - /// - /// Gets TraceId from the Activity. - /// Use carefully: if may cause iteration over all tags!. - /// - /// Activity to get traceId from. - /// TraceId value or null if it does not exist. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GetTraceId(this Activity activity) => activity.Tags.FirstOrDefault(t => t.Key == W3CConstants.TraceIdTag).Value; - - /// - /// Gets SpanId from the Activity. - /// Use carefully: if may cause iteration over all tags!. - /// - /// Activity to get spanId from. - /// SpanId value or null if it does not exist. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GetSpanId(this Activity activity) => activity.Tags.FirstOrDefault(t => t.Key == W3CConstants.SpanIdTag).Value; - - /// - /// Gets ParentSpanId from the Activity. - /// Use carefully: if may cause iteration over all tags!. - /// - /// Activity to get ParentSpanId from. - /// ParentSpanId value or null if it does not exist. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GetParentSpanId(this Activity activity) => activity.Tags.FirstOrDefault(t => t.Key == W3CConstants.ParentSpanIdTag).Value; - - /// - /// Sets Activity W3C context on the telemetry. - /// - /// Activity to update telemetry from. - /// Telemetry item. - /// Force update if properties are already set. - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = - "This method has different code for Net45/NetCore")] - public static void UpdateTelemetry(this Activity activity, ITelemetry telemetry, bool forceUpdate) - { - if (activity == null) - { - return; - } - - activity.UpdateContextOnActivity(); - - // Requests and dependencies are initialized from the current Activity - // (i.e. telemetry.Id = current.Id). Activity is created for such requests specifically - // Traces, exceptions, events on the other side are children of current activity - // There is one exception - SQL DiagnosticSource where current Activity is a parent - // for dependency calls. - - OperationTelemetry opTelemetry = telemetry as OperationTelemetry; - bool initializeFromCurrent = opTelemetry != null; - - if (initializeFromCurrent) - { - initializeFromCurrent &= !(opTelemetry is DependencyTelemetry dependency && - dependency.Type == SqlRemoteDependencyType && - dependency.Context.GetInternalContext().SdkVersion - .StartsWith(RddDiagnosticSourcePrefix, StringComparison.Ordinal)); - } - - string spanId = null, parentSpanId = null; - foreach (var tag in activity.Tags) - { - switch (tag.Key) - { - case W3CConstants.TraceIdTag: - telemetry.Context.Operation.Id = tag.Value; - break; - case W3CConstants.SpanIdTag: - spanId = tag.Value; - break; - case W3CConstants.ParentSpanIdTag: - parentSpanId = tag.Value; - break; - case W3CConstants.TracestateTag: - if (telemetry is OperationTelemetry operation) - { - operation.Properties[W3CConstants.TracestateTag] = tag.Value; - } - - break; - } - } - - if (initializeFromCurrent) - { -#if NET45 || NET46 - // on .NET Fx Activities are not always reliable, this code prevents update - // of the telemetry that was forcibly updated during Activity lifetime - // ON .NET Core there is no such problem - // if spanId is valid already and update is not forced, ignore it - if (!forceUpdate && IsValidTelemetryId(opTelemetry.Id, telemetry.Context.Operation.Id)) - { - return; - } -#endif - opTelemetry.Id = FormatTelemetryId(telemetry.Context.Operation.Id, spanId); - if (parentSpanId != null) - { - telemetry.Context.Operation.ParentId = - FormatTelemetryId(telemetry.Context.Operation.Id, parentSpanId); - } - } - else - { - telemetry.Context.Operation.ParentId = - FormatTelemetryId(telemetry.Context.Operation.Id, spanId); - } - - if (opTelemetry != null) - { - if (opTelemetry.Context.Operation.Id != activity.RootId) - { - opTelemetry.Properties[W3CConstants.LegacyRootIdProperty] = activity.RootId; - } - - if (opTelemetry.Id != activity.Id) - { - opTelemetry.Properties[W3CConstants.LegacyRequestIdProperty] = activity.Id; - } - } - } - - [EditorBrowsable(EditorBrowsableState.Never)] - internal static void SetParentSpanId(this Activity activity, string value) => - activity.AddTag(W3CConstants.ParentSpanIdTag, value); - - private static void SetTraceId(this Activity activity, string value) => - activity.AddTag(W3CConstants.TraceIdTag, value); - - private static void SetSpanId(this Activity activity, string value) => - activity.AddTag(W3CConstants.SpanIdTag, value); - - private static void SetVersion(this Activity activity, string value) => - activity.AddTag(W3CConstants.VersionTag, value); - - private static void SetSampled(this Activity activity, string value) => - activity.AddTag(W3CConstants.SampledTag, value); - - private static Activity UpdateContextFromParent(this Activity activity) - { - if (activity != null && activity.Tags.All(t => t.Key != W3CConstants.TraceIdTag)) - { - if (activity.Parent == null) - { - activity.GenerateW3CContext(); - } - else - { - foreach (var tag in activity.Parent.Tags) - { - switch (tag.Key) - { - case W3CConstants.TraceIdTag: - activity.SetTraceId(tag.Value); - break; - case W3CConstants.SpanIdTag: - activity.SetParentSpanId(tag.Value); - activity.SetSpanId(W3CUtilities.GenerateSpanId()); - break; - case W3CConstants.VersionTag: - activity.SetVersion(tag.Value); - break; - case W3CConstants.SampledTag: - activity.SetSampled(tag.Value); - break; - case W3CConstants.TracestateTag: - activity.SetTracestate(tag.Value); - break; - } - } - } - } - - return activity; - } - -#if NET45 || NET46 - private static bool IsValidTelemetryId(string id, string operationId) - { - return id.Length == 51 && - id[0] == '|' && - id[33] == '.' && - id.IndexOf('.', 34) == 50 && - id.IndexOf(operationId, 1, 33, StringComparison.Ordinal) == 1; - } -#endif - - public static string FormatTelemetryId(string traceId, string spanId) - { - return string.Concat("|", traceId, ".", spanId, "."); - } - - public static bool IsCompatibleW3CTraceID(string traceId) - { - return TraceIdRegex.IsMatch(traceId); - } - } -} diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index 2a1dacb799..d9aaefd714 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -5,56 +5,6 @@ /// internal static class W3CConstants { - /// - /// Trace-Id tag name. - /// - internal const string TraceIdTag = "w3c_traceId"; - - /// - /// Span-Id tag name. - /// - internal const string SpanIdTag = "w3c_spanId"; - - /// - /// Parent span-Id tag name. - /// - internal const string ParentSpanIdTag = "w3c_parentSpanId"; - - /// - /// Version tag name. - /// - internal const string VersionTag = "w3c_version"; - - /// - /// Sampled tag name. - /// - internal const string SampledTag = "w3c_sampled"; - - /// - /// Tracestate tag name. - /// - internal const string TracestateTag = "w3c_tracestate"; - - /// - /// Default version value. - /// - internal const string DefaultVersion = "00"; - - /// - /// Default sampled flag value: may be recorded, not requested. - /// - internal const string TraceFlagRecordedAndNotRequested = "02"; - - /// - /// Recorded and requested sampled flag value. - /// - internal const string TraceFlagRecordedAndRequested = "03"; - - /// - /// Requested trace flag. - /// - internal const byte RequestedTraceFlag = 1; - /// /// Legacy root Id tag name. /// diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index 61c1bca422..d34cdb1d9a 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; using System.Globalization; + using System.Text.RegularExpressions; using Microsoft.ApplicationInsights.Extensibility.Implementation; /// @@ -12,6 +13,27 @@ public static class W3CUtilities { private static readonly uint[] Lookup32 = CreateLookup32(); + private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); + + /// + /// Constructs a Telemetry ID from given traceid and span id in the format |traceid.spanid. + /// This is the format used by Application Insights. + /// + /// constructed Telemetry ID. + public static string FormatTelemetryId(string traceId, string spanId) + { + return string.Concat("|", traceId, ".", spanId, "."); + } + + /// + /// Checks if the given string is a valid trace-id as per W3C Specs. + /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . + /// + /// true if valid w3c trace id, otherwise false. + public static bool IsCompatibleW3CTraceID(string traceId) + { + return TraceIdRegex.IsMatch(traceId); + } /// /// Generates random trace Id as per W3C Distributed tracing specification. diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 47c9e314e3..6dc6cf2d24 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -54,7 +54,7 @@ public static class TelemetryClientExtensions if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.Id) && !string.IsNullOrEmpty(operationId)) { - if (Activity.DefaultIdFormat == ActivityIdFormat.W3C && !W3CActivityExtensions.IsCompatibleW3CTraceID(operationId)) + if (Activity.DefaultIdFormat == ActivityIdFormat.W3C && !W3CUtilities.IsCompatibleW3CTraceID(operationId)) { operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, operationId); } @@ -140,7 +140,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet // telemetryContext.Id is always set: if it was null, it is set to opTelemetry.Id and opTelemetry.Id is never null if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { - if(W3CActivityExtensions.IsCompatibleW3CTraceID(telemetryContext.Id)) + if(W3CUtilities.IsCompatibleW3CTraceID(telemetryContext.Id)) { operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), ActivitySpanId.CreateRandom()); } @@ -154,7 +154,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet operationActivity.Start(); if (operationActivity.IdFormat == ActivityIdFormat.W3C) { - operationTelemetry.Id = W3CActivityExtensions.FormatTelemetryId(operationActivity.TraceId.ToHexString(), operationActivity.SpanId.ToHexString()); + operationTelemetry.Id = W3CUtilities.FormatTelemetryId(operationActivity.TraceId.ToHexString(), operationActivity.SpanId.ToHexString()); operationTelemetry.Context.Operation.Id = operationActivity.TraceId.ToHexString(); } else From a39d911d4ee8172b0df684062cd90cfa8a740bb0 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 12:01:45 -0700 Subject: [PATCH 05/19] un-delete activityextension for w3c most methods are no op with some internally calling the native Activity methods for W3c --- .../W3C/W3CActivityExtensions.cs | 182 ++++++++++++++++++ .../Extensibility/W3C/W3CConstants.cs | 11 +- 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs new file mode 100644 index 0000000000..163a20b72d --- /dev/null +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs @@ -0,0 +1,182 @@ +namespace Microsoft.ApplicationInsights.Extensibility.W3C +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Text.RegularExpressions; + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + + /// + /// Extends Activity to support W3C distributed tracing standard. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C making extension methods in this class no longer required.")] + public static class W3CActivityExtensions + { + private const string RddDiagnosticSourcePrefix = "rdddsc"; + private const string SqlRemoteDependencyType = "SQL"; + + private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); + private static readonly Regex SpanIdRegex = new Regex("^[a-f0-9]{16}$", RegexOptions.Compiled); + + /// + /// Generate new W3C context. + /// + /// Activity to generate W3C context on. + /// The same Activity for chaining. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static Activity GenerateW3CContext(this Activity activity) + { + // No-op + return activity; + } + + /// + /// Checks if current Activity has W3C properties on it. + /// + /// Activity to check. + /// True if Activity has W3C properties, false otherwise. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static bool IsW3CActivity(this Activity activity) + { + return activity != null && Activity.DefaultIdFormat.Equals(ActivityIdFormat.W3C); + } + + /// + /// Updates context on the Activity based on the W3C Context in the parent Activity tree. + /// + /// Activity to update W3C context on. + /// The same Activity for chaining. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static Activity UpdateContextOnActivity(this Activity activity) + { + // No-op + return activity; + } + + /// + /// Gets traceparent header value for the Activity or null if there is no W3C context on it. + /// + /// Activity to read W3C context from. + /// traceparent header value. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static string GetTraceparent(this Activity activity) + { + // no-op as there is no native way to obtain TraceParent from Activity. + return string.Empty; + + /* OR //TODO decide if we want to keep this no-op + var traceId = activity.TraceId.ToHexString(); + var spanId = activity.SpanId.ToHexString(); + if (traceId.Equals("00000000000000000000000000000000") || spanId.Equals("0000000000000000")) + { + return null; + } + + return string.Join("-", W3CConstants.DefaultVersion, traceId, spanId, activity.Recorded ? "00" : "01"); + */ + } + + /// + /// Initializes W3C context on the Activity from traceparent header value. + /// + /// Activity to set W3C context on. + /// Valid traceparent header like 00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static void SetTraceparent(this Activity activity, string value) + { + // no-op + } + + /// + /// Gets tracestate header value from the Activity. + /// + /// Activity to get tracestate from. + /// tracestate header value. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static string GetTracestate(this Activity activity) + { + return activity.TraceStateString; + } + + /// + /// Sets tracestate header value on the Activity. + /// + /// Activity to set tracestate on. + /// tracestate header value. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + public static void SetTracestate(this Activity activity, string value) + { + activity.TraceStateString = value; + } + + + /// + /// Gets TraceId from the Activity. + /// + /// Activity to get traceId from. + /// TraceId value or null if it does not exist. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C. Use Activity.TraceId to get Trace ID")] + public static string GetTraceId(this Activity activity) + { + return activity.TraceId.ToHexString(); + } + + /// + /// Gets SpanId from the Activity. + /// + /// Activity to get spanId from. + /// SpanId value or null if it does not exist. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C. Use Activity.SpanId to get Span ID")] + public static string GetSpanId(this Activity activity) + { + return activity.SpanId.ToHexString(); + } + + /// + /// Gets ParentSpanId from the Activity. + /// + /// Activity to get ParentSpanId from. + /// ParentSpanId value or null if it does not exist. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C. Use Activity.ParentSpanId to get ParentSpan ID")] + public static string GetParentSpanId(this Activity activity) + { + return activity.ParentSpanId.ToHexString(); + } + + /// + /// Sets Activity W3C context on the telemetry. + /// + /// Activity to update telemetry from. + /// Telemetry item. + /// Force update if properties are already set. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C. OperationCorrelationTelemetryInitializer is W3C aware and is recommended to update telemetry from current Activity.")] + public static void UpdateTelemetry(this Activity activity, ITelemetry telemetry, bool forceUpdate) + { + // no-op + } + + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] + internal static void SetParentSpanId(this Activity activity, string value) + { + //no-op + } + } +} diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index d9aaefd714..3f84bd40fd 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -1,4 +1,7 @@ -namespace Microsoft.ApplicationInsights.Extensibility.W3C +using System; +using System.Diagnostics; + +namespace Microsoft.ApplicationInsights.Extensibility.W3C { /// /// W3C constants. @@ -14,5 +17,11 @@ internal static class W3CConstants /// Legacy root Id tag name. /// internal const string LegacyRequestIdProperty = "ai_legacyRequestId"; + + /// + /// Default version value. + /// + internal const string DefaultVersion = "00"; + } } From dfb1f621b424b560a9deeb50821ade5a0c3be80f Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 14:45:56 -0700 Subject: [PATCH 06/19] more changes --- .../Shared/TelemetryClientExtensionTests.cs | 7 ++-- .../Extensibility/W3C/W3CConstants.cs | 9 +++++ .../Extensibility/W3C/W3CUtilities.cs | 4 +- .../TelemetryClientExtensions.cs | 37 +++++++++++++------ 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs index 6610f6c377..c23e7e783a 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs @@ -62,14 +62,14 @@ public void StartDependencyTrackingReturnsOperationWithInitializedOperationName( } [TestMethod] - public void StartDependencyTrackingReturnsOperationWithInitializedOperationId() + public void StartDependencyTrackingReturnsOperationWithInitializedOperationTelemetryId() { var operation = this.telemetryClient.StartOperation("TestOperationName"); - Assert.IsNotNull(operation.Telemetry.Context.Operation.Id); + Assert.IsNotNull(operation.Telemetry.Id); } [TestMethod] - public void StartDependencyTrackingReturnsOperationWithInitializedOperationRootId() + public void StartDependencyTrackingReturnsOperationWithInitializedOperationId() { var operation = this.telemetryClient.StartOperation("TestOperationName"); Assert.IsNotNull(operation.Telemetry.Context.Operation.Id); @@ -96,6 +96,7 @@ public void StartDependencyTrackingAddsOperationContextStoreToCurrentActivity() Assert.IsNull(Activity.Current); var operation = this.telemetryClient.StartOperation(operationName: null); Assert.IsNotNull(Activity.Current); + Assert.AreEqual(ActivityIdFormat.W3C, Activity.Current.IdFormat); } [TestMethod] diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index 3f84bd40fd..bc4f819181 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -23,5 +23,14 @@ internal static class W3CConstants /// internal const string DefaultVersion = "00"; + /// + /// Default trace flag value. + /// + internal const string DefaultTraceFlag = "00"; + + /// + /// String representation of the invalid spanid of all zeroes. + /// + internal const string InvalidSpanID = "0000000000000000"; } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index d34cdb1d9a..15981f420d 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -20,7 +20,7 @@ public static class W3CUtilities /// This is the format used by Application Insights. /// /// constructed Telemetry ID. - public static string FormatTelemetryId(string traceId, string spanId) + internal static string FormatTelemetryId(string traceId, string spanId) { return string.Concat("|", traceId, ".", spanId, "."); } @@ -30,7 +30,7 @@ public static string FormatTelemetryId(string traceId, string spanId) /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . /// /// true if valid w3c trace id, otherwise false. - public static bool IsCompatibleW3CTraceID(string traceId) + internal static bool IsCompatibleW3CTraceID(string traceId) { return TraceIdRegex.IsMatch(traceId); } diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 6dc6cf2d24..1c0ba9d9d6 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -3,7 +3,7 @@ using System; using System.ComponentModel; using System.Diagnostics; - + using System.Runtime.InteropServices; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; @@ -54,11 +54,26 @@ public static class TelemetryClientExtensions if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.Id) && !string.IsNullOrEmpty(operationId)) { - if (Activity.DefaultIdFormat == ActivityIdFormat.W3C && !W3CUtilities.IsCompatibleW3CTraceID(operationId)) + if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { + if(W3CUtilities.IsCompatibleW3CTraceID(operationId)) + { + // If the user provided operationid is W3C Compatible, use it. + operationTelemetry.Context.Operation.Id = operationId; + } + else + { + // If user provided operationid is not W3C compatible, generate a new one instead. + // and store supplied value inside customproperty. + operationTelemetry.Context.Operation.Id = W3CUtilities.GenerateTraceId(); operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, operationId); + } } - operationTelemetry.Context.Operation.Id = operationId; + else + { + operationTelemetry.Context.Operation.Id = operationId; + } + } if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.ParentId) && !string.IsNullOrEmpty(parentOperationId)) @@ -108,10 +123,10 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet } // If the operation is not executing in the context of any other operation - // set its name and id as a context (root) operation name and id + // set its name and id as a context (root) operation name and generate new W3C compatible id if (string.IsNullOrEmpty(telemetryContext.Id)) - { - telemetryContext.Id = operationTelemetry.Id; + { + telemetryContext.Id = W3CUtilities.GenerateTraceId(); } if (string.IsNullOrEmpty(telemetryContext.Name)) @@ -137,13 +152,12 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet if (parentActivity == null) { - // telemetryContext.Id is always set: if it was null, it is set to opTelemetry.Id and opTelemetry.Id is never null + // telemetryContext.Id is always set: if it was null, it is set to newly generated W3C compatible TraceID if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { - if(W3CUtilities.IsCompatibleW3CTraceID(telemetryContext.Id)) - { - operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), ActivitySpanId.CreateRandom()); - } + // There is no need of checking if TelemetryContext.ID is W3C Compatible. It is always set to + // W3C compatible id. Even user supplied non-compatible ID is ignored. + operationActivity.SetParentId(string.Join("-", W3CConstants.DefaultVersion, telemetryContext.Id, W3CConstants.InvalidSpanID, W3CConstants.DefaultTraceFlag)); } else { @@ -155,7 +169,6 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet if (operationActivity.IdFormat == ActivityIdFormat.W3C) { operationTelemetry.Id = W3CUtilities.FormatTelemetryId(operationActivity.TraceId.ToHexString(), operationActivity.SpanId.ToHexString()); - operationTelemetry.Context.Operation.Id = operationActivity.TraceId.ToHexString(); } else { From 2f9e039c66a5869ec346ecccd69838f229cd6d88 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 14:54:34 -0700 Subject: [PATCH 07/19] build fix --- .../Shared/TelemetryClientExtensionTests.cs | 52 +++++++++---------- ...perationCorrelationTelemetryInitializer.cs | 9 ---- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs index c23e7e783a..cd1582d133 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/TelemetryClientExtensionTests.cs @@ -363,7 +363,6 @@ public void StartOperationCanOverrideRootAndParentOperationIdNotW3CCompatible() Assert.AreEqual("ROOT", traceTelemetry.Context.Operation.Id); } - // [TestMethod] public void StartOperationPopulatesContextCorrectlyW3C() { @@ -387,32 +386,7 @@ public void StartOperationPopulatesContextCorrectlyW3C() // The generated EventTelemetry should become the child of the root RequestTelemetry var eventTelemetry = (TraceTelemetry)this.sendItems.Single(t => t is TraceTelemetry); ValidateChildTelemetry(requestTelemetry, eventTelemetry); - } - - private void ValidateRootTelemetry(OperationTelemetry operationTelemetry, string expectedOperationId = "", string expectedOperationParentId = null, bool isW3C = true) - { - Assert.AreEqual(expectedOperationParentId, operationTelemetry.Context.Operation.ParentId); - Assert.IsNotNull(operationTelemetry.Context.Operation.Id); - - if (!string.IsNullOrEmpty(expectedOperationId)) - { - Assert.AreEqual(expectedOperationId, operationTelemetry.Context.Operation.Id); - } - - if (isW3C) - { - Assert.IsTrue(W3CUtilities.IsCompatibleW3CTraceID(operationTelemetry.Context.Operation.Id)); - } - Assert.IsNotNull(operationTelemetry.Id); - // ID is shaped like |TraceID.SpanID. - Assert.IsTrue(operationTelemetry.Id.Contains(operationTelemetry.Context.Operation.Id)); - } - - private void ValidateChildTelemetry(OperationTelemetry rootOperationTelemetry, ITelemetry childTelemetry) - { - Assert.AreEqual(rootOperationTelemetry.Id, childTelemetry.Context.Operation.ParentId); - Assert.AreEqual(rootOperationTelemetry.Context.Operation.Id, childTelemetry.Context.Operation.Id, "OperationID should be same for all operations in same context"); - } + } [TestMethod] public void StartOperationPopulatesContextCorrectlyNonW3C() @@ -638,6 +612,30 @@ public void StopOperationWhenTelemetryIdDoesNotMatchActivityIdButMatchesLegacyId Assert.AreEqual(1, this.sendItems.Count); } + private void ValidateRootTelemetry(OperationTelemetry operationTelemetry, string expectedOperationId = "", string expectedOperationParentId = null, bool isW3C = true) + { + Assert.AreEqual(expectedOperationParentId, operationTelemetry.Context.Operation.ParentId); + Assert.IsNotNull(operationTelemetry.Context.Operation.Id); + + if (!string.IsNullOrEmpty(expectedOperationId)) + { + Assert.AreEqual(expectedOperationId, operationTelemetry.Context.Operation.Id); + } + + if (isW3C) + { + Assert.IsTrue(W3CUtilities.IsCompatibleW3CTraceID(operationTelemetry.Context.Operation.Id)); + } + Assert.IsNotNull(operationTelemetry.Id); + // ID is shaped like |TraceID.SpanID. + Assert.IsTrue(operationTelemetry.Id.Contains(operationTelemetry.Context.Operation.Id)); + } + + private void ValidateChildTelemetry(OperationTelemetry rootOperationTelemetry, ITelemetry childTelemetry) + { + Assert.AreEqual(rootOperationTelemetry.Id, childTelemetry.Context.Operation.ParentId); + Assert.AreEqual(rootOperationTelemetry.Context.Operation.Id, childTelemetry.Context.Operation.Id, "OperationID should be same for all operations in same context"); + } private string GetOperationName(Activity activity) { return activity.Tags.FirstOrDefault(tag => tag.Key == "OperationName").Value; diff --git a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs index 73a56f579b..fd49ac9c07 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/OperationCorrelationTelemetryInitializer.cs @@ -13,15 +13,6 @@ /// Telemetry initializer that populates OperationContext for the telemetry item from Activity. /// This initializer is responsible for correlation of telemetry items within the same process. /// -#if NET45 - /// - /// Internally based on context stored in CallContext. - /// -#else - /// - /// Internally based on context stored in an AsyncLocal variable. - /// -#endif public class OperationCorrelationTelemetryInitializer : ITelemetryInitializer { /// From 4754a705f95e0db10f63516b4decada70385c5ba Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 15:02:08 -0700 Subject: [PATCH 08/19] more build fixes stylecop public api analyzers --- .../net45/PublicAPI.Unshipped.txt | 3 +- .../net46/PublicAPI.Unshipped.txt | 2 ++ .../netstandard1.3/PublicAPI.Unshipped.txt | 2 ++ .../netstandard2.0/PublicAPI.Shipped.txt | 2 ++ .../Implementation/OperationHolder.cs | 2 +- .../Extensibility/TelemetryConfiguration.cs | 13 +++++---- .../W3C/W3CActivityExtensions.cs | 9 +----- .../Extensibility/W3C/W3CConstants.cs | 24 ++++++++-------- .../Extensibility/W3C/W3CUtilities.cs | 28 +++++++++---------- .../TelemetryClientExtensions.cs | 3 +- 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/PublicAPI/Microsoft.ApplicationInsights.dll/net45/PublicAPI.Unshipped.txt b/PublicAPI/Microsoft.ApplicationInsights.dll/net45/PublicAPI.Unshipped.txt index 8ca2b400ef..f27e52556b 100644 --- a/PublicAPI/Microsoft.ApplicationInsights.dll/net45/PublicAPI.Unshipped.txt +++ b/PublicAPI/Microsoft.ApplicationInsights.dll/net45/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental.ExperimentalFeaturesExtension +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.set -> void +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.get -> bool Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.ExperimentalFeatures.get -> System.Collections.Generic.IList static Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental.ExperimentalFeaturesExtension.EvaluateExperimentalFeature(this Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration telemetryConfiguration, string featureName) -> bool Microsoft.ApplicationInsights.Extensibility.Implementation.TelemetryConfigurationExtensions @@ -45,7 +47,6 @@ Microsoft.ApplicationInsights.DataContracts.ExceptionTelemetry.IsSampledOutAtHea Microsoft.ApplicationInsights.DataContracts.PageViewPerformanceTelemetry.IsSampledOutAtHead.set -> void Microsoft.ApplicationInsights.DataContracts.PageViewTelemetry.IsSampledOutAtHead.set -> void Microsoft.ApplicationInsights.DataContracts.RequestTelemetry.IsSampledOutAtHead.set -> void - Microsoft.ApplicationInsights.Extensibility.Implementation.TaskTimer.TaskTimer() -> void Microsoft.ApplicationInsights.Extensibility.W3C.W3COperationCorrelationTelemetryInitializer.W3COperationCorrelationTelemetryInitializer() -> void Microsoft.ApplicationInsights.Extensibility.Implementation.TelemetryDebugWriter.TelemetryDebugWriter() -> void diff --git a/PublicAPI/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt b/PublicAPI/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt index 8ca2b400ef..69e5e45c86 100644 --- a/PublicAPI/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt +++ b/PublicAPI/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental.ExperimentalFeaturesExtension +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.set -> void +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.get -> bool Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.ExperimentalFeatures.get -> System.Collections.Generic.IList static Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental.ExperimentalFeaturesExtension.EvaluateExperimentalFeature(this Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration telemetryConfiguration, string featureName) -> bool Microsoft.ApplicationInsights.Extensibility.Implementation.TelemetryConfigurationExtensions diff --git a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard1.3/PublicAPI.Unshipped.txt b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard1.3/PublicAPI.Unshipped.txt index 8ca2b400ef..69e5e45c86 100644 --- a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard1.3/PublicAPI.Unshipped.txt +++ b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard1.3/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental.ExperimentalFeaturesExtension +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.set -> void +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.get -> bool Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.ExperimentalFeatures.get -> System.Collections.Generic.IList static Microsoft.ApplicationInsights.Extensibility.Implementation.Experimental.ExperimentalFeaturesExtension.EvaluateExperimentalFeature(this Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration telemetryConfiguration, string featureName) -> bool Microsoft.ApplicationInsights.Extensibility.Implementation.TelemetryConfigurationExtensions diff --git a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt index 9e51abc690..08f7e89bca 100644 --- a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt +++ b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt @@ -1,3 +1,5 @@ +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.set -> void +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.get -> bool abstract Microsoft.ApplicationInsights.Extensibility.Implementation.OperationTelemetry.Context.get -> Microsoft.ApplicationInsights.DataContracts.TelemetryContext abstract Microsoft.ApplicationInsights.Extensibility.Implementation.OperationTelemetry.DeepClone() -> Microsoft.ApplicationInsights.Channel.ITelemetry abstract Microsoft.ApplicationInsights.Extensibility.Implementation.OperationTelemetry.Duration.get -> System.TimeSpan diff --git a/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs b/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs index c84397bf32..5abdb07de1 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/Implementation/OperationHolder.cs @@ -71,7 +71,7 @@ protected virtual void Dispose(bool disposing) 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()) )) + || (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 diff --git a/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs b/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs index 8d0bc69692..4208eae672 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs @@ -27,8 +27,7 @@ public sealed class TelemetryConfiguration : IDisposable internal readonly SamplingRateStore LastKnownSampleRateStore = new SamplingRateStore(); private static object syncRoot = new object(); - private static TelemetryConfiguration active; - private bool enableW3c; + private static TelemetryConfiguration active; private readonly SnapshottingList telemetryInitializers = new SnapshottingList(); private readonly TelemetrySinkCollection telemetrySinks = new TelemetrySinkCollection(); @@ -36,6 +35,7 @@ public sealed class TelemetryConfiguration : IDisposable private TelemetryProcessorChain telemetryProcessorChain; private string instrumentationKey = string.Empty; private bool disableTelemetry = false; + private bool enableW3c; private TelemetryProcessorChainBuilder builder; private MetricManager metricManager = null; @@ -173,20 +173,21 @@ public bool DisableTelemetry } /// - /// Gets or sets a flag indicating whether W3C based correlation is enabled. + /// Gets or sets a value indicating whether W3C based correlation is enabled. /// public bool EnableW3CCorrelation { get { - return enableW3c; + return this.enableW3c; } + set { - enableW3c = value; + this.enableW3c = value; ActivityExtensions.TryRun(() => { - if (enableW3c) + if (this.enableW3c) { Activity.DefaultIdFormat = ActivityIdFormat.W3C; Activity.ForceDefaultIdFormat = true; diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs index 163a20b72d..1f32866edb 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs @@ -18,12 +18,6 @@ [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C making extension methods in this class no longer required.")] public static class W3CActivityExtensions { - private const string RddDiagnosticSourcePrefix = "rdddsc"; - private const string SqlRemoteDependencyType = "SQL"; - - private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); - private static readonly Regex SpanIdRegex = new Regex("^[a-f0-9]{16}$", RegexOptions.Compiled); - /// /// Generate new W3C context. /// @@ -122,7 +116,6 @@ public static void SetTracestate(this Activity activity, string value) activity.TraceStateString = value; } - /// /// Gets TraceId from the Activity. /// @@ -176,7 +169,7 @@ public static void UpdateTelemetry(this Activity activity, ITelemetry telemetry, [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] internal static void SetParentSpanId(this Activity activity, string value) { - //no-op + // no-op } } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index bc4f819181..5191fbd19b 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -1,8 +1,8 @@ -using System; -using System.Diagnostics; - -namespace Microsoft.ApplicationInsights.Extensibility.W3C +namespace Microsoft.ApplicationInsights.Extensibility.W3C { + using System; + using System.Diagnostics; + /// /// W3C constants. /// @@ -18,19 +18,19 @@ internal static class W3CConstants /// internal const string LegacyRequestIdProperty = "ai_legacyRequestId"; - /// - /// Default version value. - /// + /// + /// Default version value. + /// internal const string DefaultVersion = "00"; - /// - /// Default trace flag value. - /// + /// + /// Default trace flag value. + /// internal const string DefaultTraceFlag = "00"; - /// + /// /// String representation of the invalid spanid of all zeroes. - /// + /// internal const string InvalidSpanID = "0000000000000000"; } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index 15981f420d..0815622940 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -15,6 +15,20 @@ public static class W3CUtilities private static readonly uint[] Lookup32 = CreateLookup32(); private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); + /// + /// Generates random trace Id as per W3C Distributed tracing specification. + /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . + /// + /// Random 16 bytes array encoded as hex string. + [EditorBrowsable(EditorBrowsableState.Never)] + public static string GenerateTraceId() + { + byte[] firstHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); + byte[] secondHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); + + return GenerateId(firstHalf, secondHalf, 0, 16); + } + /// /// Constructs a Telemetry ID from given traceid and span id in the format |traceid.spanid. /// This is the format used by Application Insights. @@ -35,20 +49,6 @@ internal static bool IsCompatibleW3CTraceID(string traceId) return TraceIdRegex.IsMatch(traceId); } - /// - /// Generates random trace Id as per W3C Distributed tracing specification. - /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . - /// - /// Random 16 bytes array encoded as hex string. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GenerateTraceId() - { - byte[] firstHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); - byte[] secondHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); - - return GenerateId(firstHalf, secondHalf, 0, 16); - } - /// /// Generates random span Id as per W3C Distributed tracing specification. /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#span-id . diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 1c0ba9d9d6..2fa82b8d5f 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -56,7 +56,7 @@ public static class TelemetryClientExtensions { if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { - if(W3CUtilities.IsCompatibleW3CTraceID(operationId)) + if (W3CUtilities.IsCompatibleW3CTraceID(operationId)) { // If the user provided operationid is W3C Compatible, use it. operationTelemetry.Context.Operation.Id = operationId; @@ -73,7 +73,6 @@ public static class TelemetryClientExtensions { operationTelemetry.Context.Operation.Id = operationId; } - } if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.ParentId) && !string.IsNullOrEmpty(parentOperationId)) From d04959e2eeafb89045508deea937b420b239a5af Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 15:30:41 -0700 Subject: [PATCH 09/19] StartOperation with Activity fixed --- .../Shared/StartOperationActivityTests.cs | 15 ++++++++++++--- .../TelemetryClientExtensions.cs | 14 +++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/StartOperationActivityTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/StartOperationActivityTests.cs index d22d841a45..0ede5b7911 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/StartOperationActivityTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/StartOperationActivityTests.cs @@ -11,9 +11,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Extensibility.Implementation; using TestFramework; + using Microsoft.ApplicationInsights.Extensibility.W3C; /// - /// This class tests TelemetryClientEzxtensions.StartOperation(TelemetryClient c, Activity a) overload + /// This class tests TelemetryClientExtensions.StartOperation(TelemetryClient c, Activity a) overload /// [TestClass] public class StartOperationActivityTests @@ -272,10 +273,18 @@ private async Task ProcessAsync(Activity activity, Activity parentActivity) return telemetry; } - private void ValidateTelemetry(T telemetry, Activity activity) where T : OperationTelemetry + private void ValidateTelemetry(T telemetry, Activity activity, bool isW3C = true) where T : OperationTelemetry { Assert.AreEqual(activity.OperationName, telemetry.Name); - Assert.AreEqual(activity.Id, telemetry.Id); + 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); diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 2fa82b8d5f..74cdc02b71 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -290,10 +290,18 @@ public static void StopOperation(this TelemetryClient telemetryClient, IOpera var telemetry = new T { Name = activity.OperationName }; OperationContext operationContext = telemetry.Context.Operation; - operationContext.Name = activity.GetOperationName(); - operationContext.Id = activity.RootId; + operationContext.Name = activity.GetOperationName(); operationContext.ParentId = activity.ParentId; - telemetry.Id = activity.Id; + if (activity.IdFormat == ActivityIdFormat.W3C) + { + operationContext.Id = activity.TraceId.ToHexString(); + telemetry.Id = W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString()); + } + else + { + operationContext.Id = activity.RootId; + telemetry.Id = activity.Id; + } foreach (var item in activity.Baggage) { From 69a638ea1f948a0a49380fbf2a37202dd9811f28 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 16:10:27 -0700 Subject: [PATCH 10/19] fix standalone usage of sdk --- .../Standalone/AppInsightsStandaloneTests.cs | 23 ++++++++++++++- .../TelemetryClientExtensions.cs | 29 ++++++++++++------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Standalone/AppInsightsStandaloneTests.cs b/Test/Microsoft.ApplicationInsights.Test/Standalone/AppInsightsStandaloneTests.cs index 27d2315c44..fb75acbbd7 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Standalone/AppInsightsStandaloneTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Standalone/AppInsightsStandaloneTests.cs @@ -41,10 +41,11 @@ public void AppInsightsDllCouldRunStandalone() } [TestMethod] - public void AppInsightsUsesActivityWhenDiagnosticSourceIsAvailable() + public void AppInsightsUsesActivityWhenDiagnosticSourceIsAvailableNonW3C() { // Regular use case - System.DiagnosticSource is available. Regular unit test can cover this scenario. var config = new TelemetryConfiguration(); + config.EnableW3CCorrelation = false; config.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer()); var tc = new TelemetryClient(config); using (var requestOperation = tc.StartOperation("request", "guid")) @@ -57,6 +58,26 @@ public void AppInsightsUsesActivityWhenDiagnosticSourceIsAvailable() } } + [TestMethod] + public void AppInsightsUsesActivityWhenDiagnosticSourceIsAvailableW3C() + { + // Regular use case - System.DiagnosticSource is available. Regular unit test can cover this scenario. + var config = new TelemetryConfiguration(); + config.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer()); + var tc = new TelemetryClient(config); + using (var requestOperation = tc.StartOperation("request", "guid")) + { + using (var dependencyOperation = tc.StartOperation("dependency", "guid")) + { + // "guid" is not w3c compatible. Ignored + Assert.IsFalse(dependencyOperation.Telemetry.Id.StartsWith("|guid.")); + // but "guid" will be stored in custom properties + Assert.AreEqual("guid",dependencyOperation.Telemetry.Properties["ai_legacyRootId"]); + tc.TrackTrace("Hello World!"); + } + } + } + private string RunTestApplication(string operationId) { var fileName = $"{this.tempPath}\\ActivityTest.exe"; diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 74cdc02b71..2cc390df4b 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -54,27 +54,36 @@ public static class TelemetryClientExtensions if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.Id) && !string.IsNullOrEmpty(operationId)) { - if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) + var isActivityAvailable = ActivityExtensions.TryRun(() => { - if (W3CUtilities.IsCompatibleW3CTraceID(operationId)) + if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { - // If the user provided operationid is W3C Compatible, use it. - operationTelemetry.Context.Operation.Id = operationId; + if (W3CUtilities.IsCompatibleW3CTraceID(operationId)) + { + // If the user provided operationid is W3C Compatible, use it. + operationTelemetry.Context.Operation.Id = operationId; + } + else + { + // If user provided operationid is not W3C compatible, generate a new one instead. + // and store supplied value inside customproperty. + operationTelemetry.Context.Operation.Id = W3CUtilities.GenerateTraceId(); + operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, operationId); + } } else { - // If user provided operationid is not W3C compatible, generate a new one instead. - // and store supplied value inside customproperty. - operationTelemetry.Context.Operation.Id = W3CUtilities.GenerateTraceId(); - operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, operationId); + operationTelemetry.Context.Operation.Id = operationId; } - } - else + }); + + if(!isActivityAvailable) { operationTelemetry.Context.Operation.Id = operationId; } } + if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.ParentId) && !string.IsNullOrEmpty(parentOperationId)) { operationTelemetry.Context.Operation.ParentId = parentOperationId; From 56fa356bd0f072c239849aa41c2bea752e0547f2 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 16:16:07 -0700 Subject: [PATCH 11/19] stylecop --- src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 2cc390df4b..1ec0d5592c 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -77,7 +77,7 @@ public static class TelemetryClientExtensions } }); - if(!isActivityAvailable) + if (!isActivityAvailable) { operationTelemetry.Context.Operation.Id = operationId; } From 52cc851de8e733d804a7b5f58d56e4591615c0ed Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Mon, 19 Aug 2019 16:21:45 -0700 Subject: [PATCH 12/19] fix space --- src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 1ec0d5592c..9947a585f3 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -83,7 +83,6 @@ public static class TelemetryClientExtensions } } - if (string.IsNullOrEmpty(operationTelemetry.Context.Operation.ParentId) && !string.IsNullOrEmpty(parentOperationId)) { operationTelemetry.Context.Operation.ParentId = parentOperationId; From 5fdc7a2e5925d7e68373c5f7c2788cfb36320755 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Tue, 20 Aug 2019 09:51:25 -0700 Subject: [PATCH 13/19] Address PR comments --- .../W3C/W3CActivityExtensions.cs | 19 +++------- .../Extensibility/W3C/W3CConstants.cs | 2 +- .../Extensibility/W3C/W3CUtilities.cs | 35 ++++++++++++++++--- .../TelemetryClientExtensions.cs | 12 ++++--- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs index 1f32866edb..b040db05d4 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs @@ -39,8 +39,8 @@ public static Activity GenerateW3CContext(this Activity activity) [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] public static bool IsW3CActivity(this Activity activity) - { - return activity != null && Activity.DefaultIdFormat.Equals(ActivityIdFormat.W3C); + { + return activity != null && activity.IdFormat.Equals(ActivityIdFormat.W3C); } /// @@ -65,19 +65,8 @@ public static Activity UpdateContextOnActivity(this Activity activity) [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] public static string GetTraceparent(this Activity activity) { - // no-op as there is no native way to obtain TraceParent from Activity. - return string.Empty; - - /* OR //TODO decide if we want to keep this no-op - var traceId = activity.TraceId.ToHexString(); - var spanId = activity.SpanId.ToHexString(); - if (traceId.Equals("00000000000000000000000000000000") || spanId.Equals("0000000000000000")) - { - return null; - } - - return string.Join("-", W3CConstants.DefaultVersion, traceId, spanId, activity.Recorded ? "00" : "01"); - */ + // returning activity id as there is no native way to obtain exact TraceParent header from Activity. + return activity.Id; } /// diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index 5191fbd19b..f97f7a7963 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -31,6 +31,6 @@ internal static class W3CConstants /// /// String representation of the invalid spanid of all zeroes. /// - internal const string InvalidSpanID = "0000000000000000"; + internal const string InvalidSpanID = "0000000000000000"; } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index 0815622940..c3c0a4d1d5 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -2,6 +2,7 @@ { using System; using System.ComponentModel; + using System.Diagnostics; using System.Globalization; using System.Text.RegularExpressions; using Microsoft.ApplicationInsights.Extensibility.Implementation; @@ -15,7 +16,7 @@ public static class W3CUtilities private static readonly uint[] Lookup32 = CreateLookup32(); private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); - /// + /// /// Generates random trace Id as per W3C Distributed tracing specification. /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . /// @@ -23,10 +24,22 @@ public static class W3CUtilities [EditorBrowsable(EditorBrowsableState.Never)] public static string GenerateTraceId() { - byte[] firstHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); - byte[] secondHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); + string generatedId = string.Empty; + + var isActivityAvailable = ActivityExtensions.TryRun(() => + { + generatedId = ActivityTraceId.CreateRandom().ToHexString(); + }); + + if (!isActivityAvailable) + { + byte[] firstHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); + byte[] secondHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); + + generatedId = GenerateId(firstHalf, secondHalf, 0, 16); + } - return GenerateId(firstHalf, secondHalf, 0, 16); + return generatedId; } /// @@ -57,7 +70,19 @@ internal static bool IsCompatibleW3CTraceID(string traceId) [EditorBrowsable(EditorBrowsableState.Never)] internal static string GenerateSpanId() { - return GenerateId(BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()), 0, 8); + string generatedId = string.Empty; + + var isActivityAvailable = ActivityExtensions.TryRun(() => + { + generatedId = ActivitySpanId.CreateRandom().ToHexString(); + }); + + if (!isActivityAvailable) + { + generatedId = GenerateId(BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()), 0, 8); + } + + return generatedId; } /// diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 9947a585f3..77805d58ed 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -16,6 +16,7 @@ public static class TelemetryClientExtensions { private const string ChildActivityName = "Microsoft.ApplicationInsights.OperationContext"; + private static readonly ActivitySpanId AllZeroSpanId = new Activity("none").SpanId; /// /// Start operation creates an operation object with a respective telemetry item. @@ -132,7 +133,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet // If the operation is not executing in the context of any other operation // set its name and id as a context (root) operation name and generate new W3C compatible id if (string.IsNullOrEmpty(telemetryContext.Id)) - { + { telemetryContext.Id = W3CUtilities.GenerateTraceId(); } @@ -164,7 +165,8 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet { // There is no need of checking if TelemetryContext.ID is W3C Compatible. It is always set to // W3C compatible id. Even user supplied non-compatible ID is ignored. - operationActivity.SetParentId(string.Join("-", W3CConstants.DefaultVersion, telemetryContext.Id, W3CConstants.InvalidSpanID, W3CConstants.DefaultTraceFlag)); + operationActivity.SetParentId(string.Join("-", W3CConstants.DefaultVersion, telemetryContext.Id, W3CConstants.InvalidSpanID, W3CConstants.DefaultTraceFlag)); + operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), AllZeroSpanId, ActivityTraceFlags.None); } else { @@ -175,7 +177,9 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet operationActivity.Start(); if (operationActivity.IdFormat == ActivityIdFormat.W3C) { - operationTelemetry.Id = W3CUtilities.FormatTelemetryId(operationActivity.TraceId.ToHexString(), operationActivity.SpanId.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 { @@ -303,7 +307,7 @@ public static void StopOperation(this TelemetryClient telemetryClient, IOpera if (activity.IdFormat == ActivityIdFormat.W3C) { operationContext.Id = activity.TraceId.ToHexString(); - telemetry.Id = W3CUtilities.FormatTelemetryId(activity.TraceId.ToHexString(), activity.SpanId.ToHexString()); + telemetry.Id = W3CUtilities.FormatTelemetryId(operationContext.Id, activity.SpanId.ToHexString()); } else { From 3dc2a2e9b757ddab57fcc563fe60ee177d3600c7 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Tue, 20 Aug 2019 11:39:30 -0700 Subject: [PATCH 14/19] fixes --- .../netstandard2.0/PublicAPI.Shipped.txt | 2 -- .../netstandard2.0/PublicAPI.Unshipped.txt | 2 ++ .../TelemetryClientExtensions.cs | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt index 08f7e89bca..9e51abc690 100644 --- a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt +++ b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Shipped.txt @@ -1,5 +1,3 @@ -Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.set -> void -Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.get -> bool abstract Microsoft.ApplicationInsights.Extensibility.Implementation.OperationTelemetry.Context.get -> Microsoft.ApplicationInsights.DataContracts.TelemetryContext abstract Microsoft.ApplicationInsights.Extensibility.Implementation.OperationTelemetry.DeepClone() -> Microsoft.ApplicationInsights.Channel.ITelemetry abstract Microsoft.ApplicationInsights.Extensibility.Implementation.OperationTelemetry.Duration.get -> System.TimeSpan diff --git a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt index 295f21c8fc..aa188e6dfd 100644 --- a/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt +++ b/PublicAPI/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,3 +1,5 @@ +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.set -> void +Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.EnableW3CCorrelation.get -> bool Microsoft.ApplicationInsights.Extensibility.W3C.W3CUtilities static Microsoft.ApplicationInsights.Extensibility.W3C.W3CUtilities.GenerateTraceId() -> string Microsoft.ApplicationInsights.Extensibility.W3C.W3COperationCorrelationTelemetryInitializer diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 77805d58ed..cfbc36bfc6 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -15,8 +15,7 @@ [EditorBrowsable(EditorBrowsableState.Never)] public static class TelemetryClientExtensions { - private const string ChildActivityName = "Microsoft.ApplicationInsights.OperationContext"; - private static readonly ActivitySpanId AllZeroSpanId = new Activity("none").SpanId; + private const string ChildActivityName = "Microsoft.ApplicationInsights.OperationContext"; /// /// Start operation creates an operation object with a respective telemetry item. @@ -166,7 +165,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet // There is no need of checking if TelemetryContext.ID is W3C Compatible. It is always set to // W3C compatible id. Even user supplied non-compatible ID is ignored. operationActivity.SetParentId(string.Join("-", W3CConstants.DefaultVersion, telemetryContext.Id, W3CConstants.InvalidSpanID, W3CConstants.DefaultTraceFlag)); - operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), AllZeroSpanId, ActivityTraceFlags.None); + // operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), AllZeroSpanId, ActivityTraceFlags.None); } else { From fdbd57110708075848194d7d821c74761eb1ad13 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Tue, 20 Aug 2019 15:33:34 -0700 Subject: [PATCH 15/19] call TraceId.gneraterandom where possible. --- .../Shared/W3C/W3CUtilitiesTests.cs | 8 ---- .../Extensibility/W3C/W3CUtilities.cs | 45 ------------------- .../TelemetryClientExtensions.cs | 13 +++++- 3 files changed, 11 insertions(+), 55 deletions(-) diff --git a/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CUtilitiesTests.cs b/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CUtilitiesTests.cs index 80639ae025..804aa481c0 100644 --- a/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CUtilitiesTests.cs +++ b/Test/Microsoft.ApplicationInsights.Test/Shared/W3C/W3CUtilitiesTests.cs @@ -7,16 +7,8 @@ [TestClass] public class W3CActivityUtilitiesTests { - private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); private static readonly Regex SpanIdRegex = new Regex("^[a-f0-9]{16}$", RegexOptions.Compiled); - [TestMethod] - public void GenerateTraceIdGeneratesValidId() - { - var traceId = W3CUtilities.GenerateTraceId(); - Assert.IsTrue(TraceIdRegex.IsMatch(traceId)); - } - [TestMethod] public void GenerateSpanIdGeneratesValidId() { diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index c3c0a4d1d5..b05530141c 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -16,32 +16,6 @@ public static class W3CUtilities private static readonly uint[] Lookup32 = CreateLookup32(); private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); - /// - /// Generates random trace Id as per W3C Distributed tracing specification. - /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . - /// - /// Random 16 bytes array encoded as hex string. - [EditorBrowsable(EditorBrowsableState.Never)] - public static string GenerateTraceId() - { - string generatedId = string.Empty; - - var isActivityAvailable = ActivityExtensions.TryRun(() => - { - generatedId = ActivityTraceId.CreateRandom().ToHexString(); - }); - - if (!isActivityAvailable) - { - byte[] firstHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); - byte[] secondHalf = BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()); - - generatedId = GenerateId(firstHalf, secondHalf, 0, 16); - } - - return generatedId; - } - /// /// Constructs a Telemetry ID from given traceid and span id in the format |traceid.spanid. /// This is the format used by Application Insights. @@ -103,25 +77,6 @@ private static string GenerateId(byte[] bytes, int start, int length) return new string(result); } - /// - /// Converts byte arrays to hex lower case string. - /// - /// Array encoded as hex string. - private static string GenerateId(byte[] firstHalf, byte[] secondHalf, int start, int length) - { - // See https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa/24343727#24343727 - var result = new char[length * 2]; - int arrayBorder = length / 2; - for (int i = start; i < start + length; i++) - { - var val = Lookup32[i < arrayBorder ? firstHalf[i] : secondHalf[i - arrayBorder]]; - result[2 * i] = (char)val; - result[(2 * i) + 1] = (char)(val >> 16); - } - - return new string(result); - } - private static uint[] CreateLookup32() { // See https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa/24343727#24343727 diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index cfbc36bfc6..bc63673ce5 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -67,7 +67,7 @@ public static class TelemetryClientExtensions { // If user provided operationid is not W3C compatible, generate a new one instead. // and store supplied value inside customproperty. - operationTelemetry.Context.Operation.Id = W3CUtilities.GenerateTraceId(); + operationTelemetry.Context.Operation.Id = ActivityTraceId.CreateRandom().ToHexString(); operationTelemetry.Properties.Add(W3CConstants.LegacyRootIdProperty, operationId); } } @@ -133,7 +133,16 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet // set its name and id as a context (root) operation name and generate new W3C compatible id if (string.IsNullOrEmpty(telemetryContext.Id)) { - telemetryContext.Id = W3CUtilities.GenerateTraceId(); + + bool isTraceIDSet = ActivityExtensions.TryRun(() => + { + telemetryContext.Id = ActivityTraceId.CreateRandom().ToHexString(); + }); + + if (!isTraceIDSet) + { + telemetryContext.Id = operationTelemetry.Id; + } } if (string.IsNullOrEmpty(telemetryContext.Name)) From b7d2f2cbb96bded19c956a2002586bec941fb6f9 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Tue, 20 Aug 2019 16:20:28 -0700 Subject: [PATCH 16/19] style --- .../Extensibility/W3C/W3CUtilities.cs | 12 ++++++++++++ .../TelemetryClientExtensions.cs | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index b05530141c..c0d9d16f0e 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -16,6 +16,18 @@ public static class W3CUtilities private static readonly uint[] Lookup32 = CreateLookup32(); private static readonly Regex TraceIdRegex = new Regex("^[a-f0-9]{32}$", RegexOptions.Compiled); + /// + /// Generates random trace Id as per W3C Distributed tracing specification. + /// https://github.com/w3c/distributed-tracing/blob/master/trace_context/HTTP_HEADER_FORMAT.md#trace-id . + /// + /// Random 16 bytes array encoded as hex string. + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use ActivityTraceId.CreateRandom().ToHexString() instead.")] + public static string GenerateTraceId() + { + return ActivityTraceId.CreateRandom().ToHexString(); + } + /// /// Constructs a Telemetry ID from given traceid and span id in the format |traceid.spanid. /// This is the format used by Application Insights. diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index bc63673ce5..888a3bf0cc 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -133,8 +133,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet // set its name and id as a context (root) operation name and generate new W3C compatible id if (string.IsNullOrEmpty(telemetryContext.Id)) { - - bool isTraceIDSet = ActivityExtensions.TryRun(() => + bool isTraceIDSet = ActivityExtensions.TryRun(() => { telemetryContext.Id = ActivityTraceId.CreateRandom().ToHexString(); }); From 8769d757809c0b6bce7fbc02082015f0232d23e4 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Wed, 21 Aug 2019 10:19:07 -0700 Subject: [PATCH 17/19] review comments --- .../Extensibility/W3C/W3CConstants.cs | 15 ---------- .../Extensibility/W3C/W3CUtilities.cs | 14 +--------- .../TelemetryClientExtensions.cs | 28 ++++++++----------- 3 files changed, 12 insertions(+), 45 deletions(-) diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index f97f7a7963..ebd35d784f 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -17,20 +17,5 @@ internal static class W3CConstants /// Legacy root Id tag name. /// internal const string LegacyRequestIdProperty = "ai_legacyRequestId"; - - /// - /// Default version value. - /// - internal const string DefaultVersion = "00"; - - /// - /// Default trace flag value. - /// - internal const string DefaultTraceFlag = "00"; - - /// - /// String representation of the invalid spanid of all zeroes. - /// - internal const string InvalidSpanID = "0000000000000000"; } } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs index c0d9d16f0e..3b705b66bd 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CUtilities.cs @@ -56,19 +56,7 @@ internal static bool IsCompatibleW3CTraceID(string traceId) [EditorBrowsable(EditorBrowsableState.Never)] internal static string GenerateSpanId() { - string generatedId = string.Empty; - - var isActivityAvailable = ActivityExtensions.TryRun(() => - { - generatedId = ActivitySpanId.CreateRandom().ToHexString(); - }); - - if (!isActivityAvailable) - { - generatedId = GenerateId(BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()), 0, 8); - } - - return generatedId; + return GenerateId(BitConverter.GetBytes(WeakConcurrentRandom.Instance.Next()), 0, 8); } /// diff --git a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs index 888a3bf0cc..8e4865d232 100644 --- a/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs +++ b/src/Microsoft.ApplicationInsights/TelemetryClientExtensions.cs @@ -130,20 +130,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet } // If the operation is not executing in the context of any other operation - // set its name and id as a context (root) operation name and generate new W3C compatible id - if (string.IsNullOrEmpty(telemetryContext.Id)) - { - bool isTraceIDSet = ActivityExtensions.TryRun(() => - { - telemetryContext.Id = ActivityTraceId.CreateRandom().ToHexString(); - }); - - if (!isTraceIDSet) - { - telemetryContext.Id = operationTelemetry.Id; - } - } - + // set its name as a context (root) operation name. if (string.IsNullOrEmpty(telemetryContext.Name)) { telemetryContext.Name = operationTelemetry.Name; @@ -151,6 +138,13 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet var isActivityAvailable = ActivityExtensions.TryRun(() => { + // If the operation is not executing in the context of any other operation + // set its id to newly generated W3C Compatible Trace ID + if (string.IsNullOrEmpty(telemetryContext.Id)) + { + telemetryContext.Id = ActivityTraceId.CreateRandom().ToHexString(); + } + var parentActivity = Activity.Current; var operationActivity = new Activity(ChildActivityName); @@ -171,9 +165,8 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet if (Activity.DefaultIdFormat == ActivityIdFormat.W3C) { // There is no need of checking if TelemetryContext.ID is W3C Compatible. It is always set to - // W3C compatible id. Even user supplied non-compatible ID is ignored. - operationActivity.SetParentId(string.Join("-", W3CConstants.DefaultVersion, telemetryContext.Id, W3CConstants.InvalidSpanID, W3CConstants.DefaultTraceFlag)); - // operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), AllZeroSpanId, ActivityTraceFlags.None); + // W3C compatible id. Even user supplied non-compatible ID is ignored. + operationActivity.SetParentId(ActivityTraceId.CreateFromString(telemetryContext.Id.AsSpan()), default(ActivitySpanId), ActivityTraceFlags.None); } else { @@ -199,6 +192,7 @@ public static IOperationHolder StartOperation(this TelemetryClient telemet { // Parent context store is assigned to operation that is used to restore call context. operationHolder.ParentContext = CallContextHelpers.GetCurrentOperationContext(); + telemetryContext.Id = operationTelemetry.Id; } operationTelemetry.Start(); From 07f6958a4f777695cd529a8fd922e043f7c7b684 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Wed, 21 Aug 2019 11:36:53 -0700 Subject: [PATCH 18/19] remove unused --- .../Extensibility/W3C/W3CActivityExtensions.cs | 4 ++-- .../Extensibility/W3C/W3CConstants.cs | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs index b040db05d4..8d934799c9 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CActivityExtensions.cs @@ -40,7 +40,7 @@ public static Activity GenerateW3CContext(this Activity activity) [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] public static bool IsW3CActivity(this Activity activity) { - return activity != null && activity.IdFormat.Equals(ActivityIdFormat.W3C); + return activity != null && activity.IdFormat == ActivityIdFormat.W3C; } /// @@ -65,7 +65,7 @@ public static Activity UpdateContextOnActivity(this Activity activity) [Obsolete("Activity from System.Diagnostics.DiagnosticSource 4.6.0 onwards natively support W3C.")] public static string GetTraceparent(this Activity activity) { - // returning activity id as there is no native way to obtain exact TraceParent header from Activity. + // Activity.ID is the trasceparent header. return activity.Id; } diff --git a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs index ebd35d784f..0467663979 100644 --- a/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs +++ b/src/Microsoft.ApplicationInsights/Extensibility/W3C/W3CConstants.cs @@ -12,10 +12,5 @@ internal static class W3CConstants /// Legacy root Id tag name. /// internal const string LegacyRootIdProperty = "ai_legacyRootId"; - - /// - /// Legacy root Id tag name. - /// - internal const string LegacyRequestIdProperty = "ai_legacyRequestId"; } } From 5acae040086f0e28c3d38df914c9dee65c7e7289 Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Wed, 21 Aug 2019 11:57:08 -0700 Subject: [PATCH 19/19] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e4e7f630f..89b1495107 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog will be used to generate documentation on [release notes page](ht - [Fix: Channels not handling AggregateException, not logging full HttpRequestException.](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1173) - [Metric Aggregator background thread safeguards added to never throw unhandled exception.](https://github.com/microsoft/ApplicationInsights-dotnet/issues/1179) - [Updated version of System.Diagnostics.DiagnosticSource to 4.6.0-preview7.19362.9. Also remove marking SDK as CLS-Compliant](https://github.com/microsoft/ApplicationInsights-dotnet/pull/1183) +- [Make BaseSDK use W3C Trace Context based correlation by default. Set TelemetryConfiguration.EnableW3CCorrelation=false to disable this.](https://github.com/microsoft/ApplicationInsights-dotnet/pull/1193) ## Version 2.11.0-beta1 - [Performance fixes: Support Head Sampling; Remove NewGuid(); Sampling Flags; etc... ](https://github.com/microsoft/ApplicationInsights-dotnet/pull/1158)