diff --git a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs
similarity index 88%
rename from src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs
rename to src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs
index a892b2162331d..5acaf03a87ee7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs
+++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs
@@ -11,6 +11,11 @@ namespace System
// every framework assembly that implements any compatibility quirks.
internal static partial class LocalAppContextSwitches
{
+ // Returns value of given switch using provided cache.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool GetSwitchValue(string switchName, ref bool switchValue) =>
+ AppContext.TryGetSwitch(switchName, out switchValue);
+
// Returns value of given switch using provided cache.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue)
@@ -24,7 +29,6 @@ internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitc
private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue)
{
-
bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled);
if (!hasSwitch)
{
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
index 4619c9519c840..52f340c36ac42 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
@@ -5,7 +5,6 @@
$(NoWarn);SA1205enable$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)
- truetrue
@@ -20,6 +19,7 @@
$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLERtrue
+ $(DefineConstants);W3C_DEFAULT_ID_FORMAT
@@ -48,6 +48,12 @@
+
+
+
+ Common\System\LocalAppContextSwitches.Common.cs
+
+
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
index e3a7f04590694..ffb8ef8b89d80 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
///
/// Normally if the ParentID is defined, the format of that is used to determine the
- /// format used by the Activity. However if ForceDefaultFormat is set to true, the
+ /// format used by the Activity. However if ForceDefaultFormat is set to true, the
/// ID format will always be the DefaultIdFormat even if the ParentID is define and is
/// a different format.
///
@@ -735,7 +735,13 @@ public static ActivityIdFormat DefaultIdFormat
get
{
if (s_defaultIdFormat == ActivityIdFormat.Unknown)
+ {
+#if W3C_DEFAULT_ID_FORMAT
+ s_defaultIdFormat = LocalAppContextSwitches.DefaultActivityIdFormatIsHierarchial ? ActivityIdFormat.Hierarchical : ActivityIdFormat.W3C;
+#else
s_defaultIdFormat = ActivityIdFormat.Hierarchical;
+#endif // W3C_DEFAULT_ID_FORMAT
+ }
return s_defaultIdFormat;
}
set
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LocalAppContextSwitches.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LocalAppContextSwitches.cs
new file mode 100644
index 0000000000000..6a3ed6341f7fa
--- /dev/null
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LocalAppContextSwitches.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ internal static partial class LocalAppContextSwitches
+ {
+ public static bool DefaultActivityIdFormatIsHierarchial { get; } = InitializeDefaultActivityIdFormat();
+
+ private static bool InitializeDefaultActivityIdFormat()
+ {
+ bool defaultActivityIdFormatIsHierarchial = false;
+
+ if (!LocalAppContextSwitches.GetSwitchValue("System.Diagnostics.DefaultActivityIdFormatIsHierarchial", ref defaultActivityIdFormatIsHierarchial))
+ {
+ string? switchValue = Environment.GetEnvironmentVariable("DOTNET_SYSTEM_DIAGNOSTICS_DEFAULTACTIVITYIDFORMATISHIERARCHIAL");
+ if (switchValue != null)
+ {
+ defaultActivityIdFormatIsHierarchial = IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1");
+ }
+ }
+
+ return defaultActivityIdFormatIsHierarchial;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsTrueStringIgnoreCase(string value)
+ {
+ return value.Length == 4 &&
+ (value[0] == 't' || value[0] == 'T') &&
+ (value[1] == 'r' || value[1] == 'R') &&
+ (value[2] == 'u' || value[2] == 'U') &&
+ (value[3] == 'e' || value[3] == 'E');
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs
index d79c701fe16fb..8e7c58f697918 100644
--- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs
@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}
- ///
- /// Tests overflow in Id generation when parentId has a single (root) node
- ///
- [Fact]
- public void ActivityIdNonHierarchicalOverflow()
- {
- // find out Activity Id length on this platform in this AppDomain
- Activity testActivity = new Activity("activity")
- .Start();
- var expectedIdLength = testActivity.Id.Length;
- testActivity.Stop();
-
- // check that if parentId '|aaa...a' 1024 bytes long is set with single node (no dots or underscores in the Id)
- // it causes overflow during Id generation, and new root Id is generated for the new Activity
- var parentId = '|' + new string('a', 1022) + '.';
-
- var activity = new Activity("activity")
- .SetParentId(parentId)
- .Start();
-
- Assert.Equal(parentId, activity.ParentId);
-
- // With probability 1/MaxLong, Activity.Id length may be expectedIdLength + 1
- Assert.InRange(activity.Id.Length, expectedIdLength, expectedIdLength + 1);
- Assert.DoesNotContain('#', activity.Id);
- }
///
/// Tests activity start and stop
@@ -258,6 +232,7 @@ public void IdGenerationNoParent()
public void IdGenerationInternalParent()
{
var parent = new Activity("parent");
+ parent.SetIdFormat(ActivityIdFormat.Hierarchical);
parent.Start();
var child1 = new Activity("child1");
var child2 = new Activity("child2");
@@ -535,11 +510,11 @@ public void ActivitySpanIdTests()
/****** WC3 Format tests *****/
[Fact]
- public void IdFormat_HierarchicalIsDefault()
+ public void IdFormat_W3CIsDefaultForNet5()
{
Activity activity = new Activity("activity1");
activity.Start();
- Assert.Equal(ActivityIdFormat.Hierarchical, activity.IdFormat);
+ Assert.Equal(PlatformDetection.IsNetCore ? ActivityIdFormat.W3C : ActivityIdFormat.Hierarchical, activity.IdFormat);
}
[Fact]
@@ -617,6 +592,20 @@ public void IdFormat_W3CWhenDefaultIsW3C()
}).Dispose();
}
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void IdFormat_WithTheEnvironmentSwitch()
+ {
+ var psi = new ProcessStartInfo();
+ psi.Environment.Add("DOTNET_SYSTEM_DIAGNOSTICS_DEFAULTACTIVITYIDFORMATISHIERARCHIAL", "true");
+
+ RemoteExecutor.Invoke(() =>
+ {
+ Activity activity = new Activity("activity15");
+ activity.Start();
+ Assert.Equal(ActivityIdFormat.Hierarchical, activity.IdFormat);
+ }, new RemoteInvokeOptions() { StartInfo = psi }).Dispose();
+ }
+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public void IdFormat_HierarchicalWhenDefaultIsW3CButHierarchicalParentId()
{
@@ -633,13 +622,23 @@ public void IdFormat_HierarchicalWhenDefaultIsW3CButHierarchicalParentId()
}
[Fact]
- public void IdFormat_ZeroTraceIdAndSpanIdWithHierarchicalFormat()
+ public void IdFormat_ZeroTraceIdAndSpanIdWithW3CFormat()
{
Activity activity = new Activity("activity");
activity.Start();
- Assert.Equal(ActivityIdFormat.Hierarchical, activity.IdFormat);
- Assert.Equal("00000000000000000000000000000000", activity.TraceId.ToHexString());
- Assert.Equal("0000000000000000", activity.SpanId.ToHexString());
+
+ if (PlatformDetection.IsNetCore)
+ {
+ Assert.Equal(ActivityIdFormat.W3C, activity.IdFormat);
+ Assert.NotEqual("00000000000000000000000000000000", activity.TraceId.ToHexString());
+ Assert.NotEqual("0000000000000000", activity.SpanId.ToHexString());
+ }
+ else
+ {
+ Assert.Equal(ActivityIdFormat.Hierarchical, activity.IdFormat);
+ Assert.Equal("00000000000000000000000000000000", activity.TraceId.ToHexString());
+ Assert.Equal("0000000000000000", activity.SpanId.ToHexString());
+ }
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/ActivityTests.cs
new file mode 100644
index 0000000000000..e3b64f2f70f32
--- /dev/null
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/ActivityTests.cs
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Diagnostics.Tests
+{
+ public class ActivityTests : IDisposable
+ {
+ [Fact]
+ public void ActivityIdNonHierarchicalOverflow()
+ {
+ // find out Activity Id length on this platform in this AppDomain
+ Activity testActivity = new Activity("activity")
+ .Start();
+ var expectedIdLength = testActivity.Id.Length;
+ testActivity.Stop();
+
+ // check that if parentId '|aaa...a' 1024 bytes long is set with single node (no dots or underscores in the Id)
+ // it causes overflow during Id generation, and new root Id is generated for the new Activity
+ var parentId = '|' + new string('a', 1022) + '.';
+
+ var activity = new Activity("activity")
+ .SetParentId(parentId)
+ .Start();
+
+ Assert.Equal(parentId, activity.ParentId);
+
+ // With probability 1/MaxLong, Activity.Id length may be expectedIdLength + 1
+ Assert.InRange(activity.Id.Length, expectedIdLength, expectedIdLength + 1);
+ Assert.DoesNotContain('#', activity.Id);
+ }
+
+ [Fact]
+ public void IdGenerationInternalParent()
+ {
+ var parent = new Activity("parent");
+ parent.Start();
+ var child1 = new Activity("child1");
+ var child2 = new Activity("child2");
+ //start 2 children in different execution contexts
+ Task.Run(() => child1.Start()).Wait();
+ Task.Run(() => child2.Start()).Wait();
+
+ // In Debug builds of System.Diagnostics.DiagnosticSource, the child operation Id will be constructed as follows
+ // "|parent.RootId.-childCount.".
+ // This is for debugging purposes to know which operation the child Id is comming from.
+ //
+ // In Release builds of System.Diagnostics.DiagnosticSource, it will not contain the operation name to keep it simple and it will be as
+ // "|parent.RootId.childCount.".
+
+ string child1DebugString = $"|{parent.RootId}.{child1.OperationName}-1.";
+ string child2DebugString = $"|{parent.RootId}.{child2.OperationName}-2.";
+ string child1ReleaseString = $"|{parent.RootId}.1.";
+ string child2ReleaseString = $"|{parent.RootId}.2.";
+
+ AssertExtensions.AtLeastOneEquals(child1DebugString, child1ReleaseString, child1.Id);
+ AssertExtensions.AtLeastOneEquals(child2DebugString, child2ReleaseString, child2.Id);
+
+ Assert.Equal(parent.RootId, child1.RootId);
+ Assert.Equal(parent.RootId, child2.RootId);
+ child1.Stop();
+ child2.Stop();
+ var child3 = new Activity("child3");
+ child3.Start();
+
+ string child3DebugString = $"|{parent.RootId}.{child3.OperationName}-3.";
+ string child3ReleaseString = $"|{parent.RootId}.3.";
+
+ AssertExtensions.AtLeastOneEquals(child3DebugString, child3ReleaseString, child3.Id);
+
+ var grandChild = new Activity("grandChild");
+ grandChild.Start();
+
+ child3DebugString = $"{child3.Id}{grandChild.OperationName}-1.";
+ child3ReleaseString = $"{child3.Id}1.";
+
+ AssertExtensions.AtLeastOneEquals(child3DebugString, child3ReleaseString, grandChild.Id);
+ }
+
+ [Fact]
+ public void IdFormat_HierarchicalIsDefault()
+ {
+ Activity activity = new Activity("activity1");
+ activity.Start();
+ Assert.Equal(ActivityIdFormat.Hierarchical, activity.IdFormat);
+ }
+
+ [Fact]
+ public void IdFormat_ZeroTraceIdAndSpanIdWithHierarchicalFormat()
+ {
+ Activity activity = new Activity("activity");
+ activity.Start();
+ Assert.Equal(ActivityIdFormat.Hierarchical, activity.IdFormat);
+ Assert.Equal("00000000000000000000000000000000", activity.TraceId.ToHexString());
+ Assert.Equal("0000000000000000", activity.SpanId.ToHexString());
+ }
+
+ public void Dispose()
+ {
+ Activity.Current = null;
+ }
+ }
+}
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj
new file mode 100644
index 0000000000000..04a992f8cd81c
--- /dev/null
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/System.Diagnostics.DiagnosticSource.Switches.Tests.csproj
@@ -0,0 +1,9 @@
+
+
+ $(NetCoreAppCurrent)
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/runtimeconfig.template.json b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/runtimeconfig.template.json
new file mode 100644
index 0000000000000..1b600a96bff58
--- /dev/null
+++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TestWithConfigSwitches/runtimeconfig.template.json
@@ -0,0 +1,5 @@
+{
+ "configProperties": {
+ "System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
+ }
+}
\ No newline at end of file
diff --git a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj
index 0291615e11c0b..7bb92b189a59c 100644
--- a/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj
+++ b/src/libraries/System.Drawing.Common/src/System.Drawing.Common.csproj
@@ -219,7 +219,7 @@
-
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index d9c25f17664ee..fcb68d7de40fe 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -403,7 +403,6 @@
-
@@ -1084,6 +1083,9 @@
Common\SkipLocalsInit.cs
+
+ Common\System\LocalAppContextSwitches.Common.cs
+
Common\System\HResults.cs
diff --git a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj
index 833bf50383d10..1c9982edfc840 100644
--- a/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj
+++ b/src/libraries/System.Private.Xml/src/System.Private.Xml.csproj
@@ -791,7 +791,7 @@
-