{
// 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)
{
// The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false
private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue)
{
-
bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled);
if (!hasSwitch)
{
<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. -->
<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" />
<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>
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>
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
--- /dev/null
+// 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');
+ }
+ }
+}
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);
- Assert.DoesNotContain('#', activity.Id);
- }
/// <summary>
/// Tests activity start and stop
public void IdGenerationInternalParent()
{
var parent = new Activity("parent");
+ parent.SetIdFormat(ActivityIdFormat.Hierarchical);
parent.Start();
var child1 = new Activity("child1");
var child2 = new Activity("child2");
/****** 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]
}
[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()
{
RemoteExecutor.Invoke(() =>
}
[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))]
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
+ <TestRuntime>true</TestRuntime>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="ActivityTests.cs" />
+ </ItemGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+{
+ "configProperties": {
+ "System.Diagnostics.DefaultActivityIdFormatIsHierarchial": true
+ }
+}
\ No newline at end of file
<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" />
<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" />
<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>
<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" />