Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Change Activity Default IdFormat to W3C by tarekgh · Pull Request #37686 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Change Activity Default IdFormat to W3C by tarekgh · Pull Request #37686 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Change Activity Default IdFormat to W3C by tarekgh · Pull Request #37686 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Change Activity Default IdFormat to W3C by tarekgh · Pull Request #37686 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Change Activity Default IdFormat to W3C by tarekgh · Pull Request #37686 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Change Activity Default IdFormat to W3C by tarekgh · Pull Request #37686 · dotnet/runtime · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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)
{
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,6 @@
<NoWarn>$(NoWarn);SA1205</NoWarn>
<Nullable>enable</Nullable>
<TargetFrameworks>$(NetCoreAppCurrent);netstandard1.1;netstandard1.3;net45;net46;netstandard2.0;$(NetFrameworkCurrent)</TargetFrameworks>
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
<ExcludeCurrentFullFrameworkFromPackage>true</ExcludeCurrentFullFrameworkFromPackage>
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand All@@ -20,6 +19,7 @@
<DefineConstants Condition="'$(TargetFramework)' != 'netstandard1.1' and '$(TargetFramework)' != 'netstandard1.3'">$(DefineConstants);EVENTSOURCE_ENUMERATE_SUPPORT</DefineConstants>
<DefineConstants Condition="$(TargetFramework.StartsWith('net4'))">$(DefineConstants);ALLOW_PARTIALLY_TRUSTED_CALLERS;ENABLE_HTTP_HANDLER</DefineConstants>
<ExcludeFromPackage Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExcludeFromPackage>
<DefineConstants Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">$(DefineConstants);W3C_DEFAULT_ID_FORMAT</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Diagnostics\DiagnosticSource.cs" />
Expand DownExpand Up@@ -48,6 +48,12 @@
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == '$(NetCoreAppCurrent)'">
<Compile Include="System\Diagnostics\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net45' And '$(TargetFramework)' != 'netstandard1.1'">
<Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
</ItemGroup>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ public partial class Activity : IDisposable
private static ActivityIdFormat s_defaultIdFormat;
/// <summary>
/// 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.
/// </summary>
Expand DownExpand Up@@ -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
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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');
}
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -188,32 +188,6 @@ public void ActivityIdOverflow()
Assert.Equal('#', activity.Id[activity.Id.Length - 1]);
}

/// <summary>
/// Tests overflow in Id generation when parentId has a single (root) node
/// </summary>
[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);
Comment thread
tarekgh marked this conversation as resolved.
Assert.DoesNotContain('#', activity.Id);
}

/// <summary>
/// Tests activity start and stop
Expand DownExpand Up@@ -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");
Expand DownExpand Up@@ -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]
Expand DownExpand Up@@ -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()
{
Expand All@@ -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))]
Expand Down
Original file line numberDiff line numberDiff line change
@@ -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.<child.OperationName.Replace(., -)>-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;
}
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
<TestRuntime>true</TestRuntime>
</PropertyGroup>
<ItemGroup>
<Compile Include="ActivityTests.cs" />
</ItemGroup>
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"configProperties": {
"System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -219,7 +219,7 @@
<Compile Include="misc\GDI\DeviceContextType.cs" />
<Compile Include="misc\GDI\WindowsGraphics.cs" />
<Compile Include="misc\GDI\WindowsRegion.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -403,7 +403,6 @@
<Compile Include="$(MSBuildThisFileDirectory)System\LazyOfTTMetadata.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimization.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LoaderOptimizationAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\LocalDataStoreSlot.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MarshalByRefObject.cs" />
Expand DownExpand Up@@ -1083,6 +1082,9 @@
<Compile Include="$(CommonPath)SkipLocalsInit.cs">
<Link>Common\SkipLocalsInit.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs">
<Link>Common\System\LocalAppContextSwitches.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)System\HResults.cs">
<Link>Common\System\HResults.cs</Link>
</Compile>
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -791,7 +791,7 @@
<Compile Include="System\Xml\Xsl\Xslt\XsltLoader.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XsltQilFactory.cs" />
<Compile Include="System\Xml\Xsl\Xslt\XslVisitor.cs" />
<Compile Include="$(CoreLibSharedDir)System\LocalAppContextSwitches.Common.cs"
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="System\LocalAppContextSwitches.Common.cs" />
<Compile Include="System\Xml\Core\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
Expand Down