diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index 68082e5a183c94..90126cf85da13e 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -87,6 +87,7 @@ System.Diagnostics.DiagnosticSource + @@ -107,7 +108,6 @@ System.Diagnostics.DiagnosticSource - diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index e7d36273408355..e33938066d9d24 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -58,8 +58,8 @@ public partial class Activity : IDisposable #pragma warning disable CA1825 // Array.Empty() doesn't exist in all configurations private static readonly IEnumerable> s_emptyBaggageTags = new KeyValuePair[0]; private static readonly IEnumerable> s_emptyTagObjects = new KeyValuePair[0]; - private static readonly IEnumerable s_emptyLinks = new ActivityLink[0]; - private static readonly IEnumerable s_emptyEvents = new ActivityEvent[0]; + private static readonly IEnumerable s_emptyLinks = new DiagLinkedList(); + private static readonly IEnumerable s_emptyEvents = new DiagLinkedList(); #pragma warning restore CA1825 private static readonly ActivitySource s_defaultSource = new ActivitySource(string.Empty); private static readonly AsyncLocal s_current = new AsyncLocal(); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs index 8b3cdebadc1ead..8a207ae838ac4c 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs @@ -59,21 +59,18 @@ private ActivitySource(string name, string? version, IEnumerable 0) + s_allListeners.EnumWithAction((listener, source) => { - s_allListeners.EnumWithAction((listener, source) => + Func? shouldListenTo = listener.ShouldListenTo; + if (shouldListenTo != null) { - Func? shouldListenTo = listener.ShouldListenTo; - if (shouldListenTo != null) + var activitySource = (ActivitySource)source; + if (shouldListenTo(activitySource)) { - var activitySource = (ActivitySource)source; - if (shouldListenTo(activitySource)) - { - activitySource.AddListener(listener); - } + activitySource.AddListener(listener); } - }, this); - } + } + }, this); GC.KeepAlive(DiagnosticSourceEventSource.Log); } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagLinkedList.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagLinkedList.cs index 7a0ea5004e0fd4..439be3dc509783 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagLinkedList.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagLinkedList.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Text; namespace System.Diagnostics { @@ -149,6 +150,121 @@ public void AddFront(T value) public DiagEnumerator GetEnumerator() => new DiagEnumerator(_first); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private static void ActivityLinkToString(ref ActivityLink al, ref ValueStringBuilder vsb) + { + ActivityContext ac = al.Context; + + vsb.Append("("); + vsb.Append(ac.TraceId.ToHexString()); + vsb.Append(",\u200B"); + vsb.Append(ac.SpanId.ToHexString()); + vsb.Append(",\u200B"); + vsb.Append(ac.TraceFlags.ToString()); + vsb.Append(",\u200B"); + vsb.Append(ac.TraceState ?? "null"); + vsb.Append(",\u200B"); + vsb.Append(ac.IsRemote ? "true" : "false"); + + if (al.Tags is not null) + { + vsb.Append(",\u200B["); + string sep = ""; + foreach (KeyValuePair kvp in al.EnumerateTagObjects()) + { + vsb.Append(sep); + vsb.Append(kvp.Key); + vsb.Append(":\u200B"); + vsb.Append(kvp.Value?.ToString() ?? "null"); + sep = ",\u200B"; + } + + vsb.Append("]"); + } + vsb.Append(")"); + } + + private static void ActivityEventToString(ref ActivityEvent ae, ref ValueStringBuilder vsb) + { + vsb.Append("("); + vsb.Append(ae.Name); + vsb.Append(",\u200B"); + vsb.Append(ae.Timestamp.ToString("o")); + + if (ae.Tags is not null) + { + vsb.Append(",\u200B["); + string sep = ""; + foreach (KeyValuePair kvp in ae.EnumerateTagObjects()) + { + vsb.Append(sep); + vsb.Append(kvp.Key); + vsb.Append(":\u200B"); + vsb.Append(kvp.Value?.ToString() ?? "null"); + sep = ",\u200B"; + } + + vsb.Append("]"); + } + vsb.Append(")"); + } + + public override string ToString() + { + lock (this) + { + DiagNode? current = _first; + if (current is null) + { + return "[]"; + } + + var vsb = new ValueStringBuilder(stackalloc char[256]); + vsb.Append("["); + + if (typeof(T) == typeof(ActivityLink)) + { + while (current is not null) + { + ActivityLink al = (ActivityLink)(object)current.Value!; + ActivityLinkToString(ref al, ref vsb); + current = current.Next; + if (current is not null) + { + vsb.Append(",\u200B"); + } + } + } + else if (typeof(T) == typeof(ActivityEvent)) + { + while (current is not null) + { + ActivityEvent ae = (ActivityEvent)(object)current.Value!; + ActivityEventToString(ref ae, ref vsb); + current = current.Next; + if (current is not null) + { + vsb.Append(",\u200B"); + } + } + } + else + { + while (current is not null) + { + vsb.Append(current.Value?.ToString() ?? "null"); + current = current.Next; + if (current is not null) + { + vsb.Append(",\u200B"); + } + } + } + + vsb.Append("]"); + return vsb.ToString(); + } + } } internal struct DiagEnumerator : IEnumerator diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs index 435fde8804035e..ace36d95253df0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/ActivityTests.cs @@ -2422,6 +2422,100 @@ public void EnumerateEventTagsTest() } } + [Fact] + public void TestLinksToString() + { + Activity activity = new Activity("testLinksActivity"); + + Assert.Equal("[]", activity.Links.ToString()); + + activity.AddLink(new ActivityLink(new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded), new ActivityTagsCollection { ["alk1"] = "alv1", ["alk2"] = "alv2", ["alk3"] = null })); + activity.AddLink(new ActivityLink(new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None))); + + string formattedLinks = "["; + string linkSep = ""; + foreach (ActivityLink link in activity.Links) + { + ActivityContext ac = link.Context; + + formattedLinks += $"{linkSep}("; + formattedLinks += ac.TraceId.ToHexString(); + formattedLinks += ",\u200B"; + formattedLinks += ac.SpanId.ToHexString(); + formattedLinks += ",\u200B"; + formattedLinks += ac.TraceFlags.ToString(); + formattedLinks += ",\u200B"; + formattedLinks += ac.TraceState ?? "null"; + formattedLinks += ",\u200B"; + formattedLinks += ac.IsRemote ? "true" : "false"; + + if (link.Tags is not null) + { + formattedLinks += ",\u200B["; + string sep = ""; + foreach (KeyValuePair kvp in link.EnumerateTagObjects()) + { + formattedLinks += sep; + formattedLinks += kvp.Key; + formattedLinks += ":\u200B"; + formattedLinks += kvp.Value?.ToString() ?? "null"; + sep = ",\u200B"; + } + + formattedLinks += "]"; + } + formattedLinks += ")"; + linkSep = ",\u200B"; + } + formattedLinks += "]"; + + Assert.Equal(formattedLinks, activity.Links.ToString()); + } + + [Fact] + public void TestEventsToString() + { + Activity activity = new Activity("testLinksActivity"); + + Assert.Equal("[]", activity.Events.ToString()); + + activity.AddEvent(new ActivityEvent("TestEvent1", DateTime.UtcNow, new ActivityTagsCollection { { "E11", "EV1" }, { "E12", "EV2" } })); + activity.AddEvent(new ActivityEvent("TestEvent2", DateTime.UtcNow.AddSeconds(10), new ActivityTagsCollection { { "E21", "EV21" }, { "E22", "EV22" } })); + + string formattedEvents = "["; + string linkSep = ""; + + foreach (var e in activity.Events) + { + formattedEvents += $"{linkSep}("; + formattedEvents += e.Name; + formattedEvents += ",\u200B"; + formattedEvents += e.Timestamp.ToString("o"); + + if (e.Tags is not null) + { + formattedEvents += ",\u200B["; + string sep = ""; + foreach (KeyValuePair kvp in e.EnumerateTagObjects()) + { + formattedEvents += sep; + formattedEvents += kvp.Key; + formattedEvents += ":\u200B"; + formattedEvents += kvp.Value?.ToString() ?? "null"; + sep = ",\u200B"; + } + + formattedEvents += "]"; + } + + formattedEvents += ")"; + linkSep = ",\u200B"; + } + + formattedEvents += "]"; + Assert.Equal(formattedEvents, activity.Events.ToString()); + } + public void Dispose() { Activity.Current = null; diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs index 335642fdb1fd15..9963885aad5aa7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs @@ -977,7 +977,7 @@ public bool HasSameRules(TimeZoneInfo other) // "TimeZoneInfo" := TimeZoneInfo Data;[AdjustmentRule Data 1];...;[AdjustmentRule Data N] // // "TimeZoneInfo Data" := <_id>;<_baseUtcOffset>;<_displayName>; - // <_standardDisplayName>;<_daylightDispayName>; + // <_standardDisplayName>;<_daylightDisplayName>; // // "AdjustmentRule Data" := ;;; // [TransitionTime Data DST Start]