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 c9c4166fce7dc3..730996055026f2 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -427,13 +427,19 @@ public IEnumerable Links /// . public Enumerator EnumerateLinks() => new Enumerator(_links?.First); + /// + /// Enumerate the baggage attached to this Activity object and its ancestors without allocating. + /// + /// . + internal BaggageEnumerator EnumerateBaggage() => new BaggageEnumerator(this); + /// /// Returns the value of the key-value pair added to the activity with . /// Returns null if that key does not exist. /// public string? GetBaggageItem(string key) { - foreach (KeyValuePair keyValue in Baggage) + foreach (KeyValuePair keyValue in EnumerateBaggage()) if (key == keyValue.Key) return keyValue.Value; return null; @@ -1600,6 +1606,54 @@ public bool MoveNext() } } + /// + /// Enumerates the baggage stored on this Activity and its ancestors without allocating. + /// Mirrors the semantics of the property, which uses an iterator + /// (allocating a state machine on every call) to walk the same data. + /// + internal struct BaggageEnumerator + { + // The next Activity (if any) to search for a non-empty baggage list. Always points at an + // Activity whose own baggage list has not yet been consumed by this enumerator. + private Activity? _activity; + + // The remaining nodes of the baggage list currently being drained. + private DiagNode>? _next; + + internal BaggageEnumerator(Activity? activity) + { + _activity = activity; + _next = null; + Current = default; + } + + public KeyValuePair Current { get; private set; } + + public readonly BaggageEnumerator GetEnumerator() => this; + + public bool MoveNext() + { + while (_next is null) + { + if (_activity is null) + { + return false; + } + + BaggageLinkedList? baggage = _activity._baggage; + _activity = _activity.Parent; + if (baggage != null) + { + _next = baggage.First; + } + } + + Current = _next.Value; + _next = _next.Next; + return true; + } + } + private sealed class BaggageLinkedList : IEnumerable> { private DiagNode>? _first; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs index afc8e74c47e855..082fc9a42c4fb8 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DistributedContextPropagator.cs @@ -122,22 +122,25 @@ public static DistributedContextPropagator Current // internal stuff - internal static void InjectBaggage(object? carrier, IEnumerable> baggage, PropagatorSetterCallback setter) + internal static void InjectBaggage(object? carrier, Activity? activity, PropagatorSetterCallback setter) { - using (IEnumerator> e = baggage.GetEnumerator()) + if (activity is null) { - if (e.MoveNext()) - { - StringBuilder baggageList = new StringBuilder(); + return; + } - do - { - KeyValuePair item = e.Current; - baggageList.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(CommaWithSpace); - } while (e.MoveNext()); + Activity.BaggageEnumerator e = activity.EnumerateBaggage(); + if (e.MoveNext()) + { + StringBuilder baggageList = new StringBuilder(); + + do + { + KeyValuePair item = e.Current; + baggageList.Append(WebUtility.UrlEncode(item.Key)).Append('=').Append(WebUtility.UrlEncode(item.Value)).Append(CommaWithSpace); + } while (e.MoveNext()); - setter(carrier, CorrelationContext, baggageList.ToString(0, baggageList.Length - 2)); - } + setter(carrier, CorrelationContext, baggageList.ToString(0, baggageList.Length - 2)); } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs index 04a478fde94c40..5d2d5a28763e2e 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/LegacyPropagator.cs @@ -39,7 +39,7 @@ public override void Inject(Activity? activity, object? carrier, PropagatorSette setter(carrier, RequestId, id); } - InjectBaggage(carrier, activity.Baggage, setter); + InjectBaggage(carrier, activity, setter); } public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs index a8aec1bb72cfc0..f8cbd7a005ebb6 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/PassThroughPropagator.cs @@ -18,7 +18,7 @@ public override void Inject(Activity? activity, object? carrier, PropagatorSette return; } - GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable>? baggage); + GetRootId(out string? parentId, out string? traceState, out bool isW3c, out Activity? rootActivity); if (parentId is null) { return; @@ -31,17 +31,14 @@ public override void Inject(Activity? activity, object? carrier, PropagatorSette setter(carrier, TraceState, traceState); } - if (baggage is not null) - { - InjectBaggage(carrier, baggage, setter); - } + InjectBaggage(carrier, rootActivity, setter); } public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); public override IEnumerable>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter); - private static void GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable>? baggage) + private static void GetRootId(out string? parentId, out string? traceState, out bool isW3c, out Activity? rootActivity) { Activity? activity = Activity.Current; @@ -53,7 +50,7 @@ private static void GetRootId(out string? parentId, out string? traceState, out traceState = activity?.TraceStateString; parentId = activity?.ParentId ?? activity?.Id; isW3c = parentId is not null ? Activity.TryConvertIdToContext(parentId, traceState, isRemote: false, out _) : false; - baggage = activity?.Baggage; + rootActivity = activity; } } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs index a0d62305cc9e0a..c02c575bbce010 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/W3CPropagator.cs @@ -49,7 +49,7 @@ public override void Inject(Activity? activity, object? carrier, PropagatorSette InjectTraceState(traceState, carrier, setter); } - InjectW3CBaggage(carrier, activity.Baggage, setter); + InjectW3CBaggage(carrier, activity, setter); } public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) @@ -234,48 +234,46 @@ internal static void InjectTraceState(string traceState, object? carrier, Propag } } - internal static void InjectW3CBaggage(object? carrier, IEnumerable> baggage, PropagatorSetterCallback setter) + internal static void InjectW3CBaggage(object? carrier, Activity activity, PropagatorSetterCallback setter) { - using (IEnumerator> e = baggage.GetEnumerator()) + Activity.BaggageEnumerator e = activity.EnumerateBaggage(); + if (e.MoveNext()) { - if (e.MoveNext()) - { - ValueStringBuilder encodedBaggage = new ValueStringBuilder(stackalloc char[256]); + ValueStringBuilder encodedBaggage = new ValueStringBuilder(stackalloc char[256]); - int entriesCount = 0; - int lastGoodLength = 0; + int entriesCount = 0; + int lastGoodLength = 0; - do - { - KeyValuePair item = e.Current; + do + { + KeyValuePair item = e.Current; - if (EncodeBaggageKey(item.Key, ref encodedBaggage)) + if (EncodeBaggageKey(item.Key, ref encodedBaggage)) + { + encodedBaggage.Append(Space); + encodedBaggage.Append(Equal); + encodedBaggage.Append(Space); + if (!string.IsNullOrEmpty(item.Value)) { - encodedBaggage.Append(Space); - encodedBaggage.Append(Equal); - encodedBaggage.Append(Space); - if (!string.IsNullOrEmpty(item.Value)) - { - EncodeBaggageValue(item.Value, ref encodedBaggage); - } - encodedBaggage.Append(CommaWithSpace); - - entriesCount++; - - if (encodedBaggage.Length < MaxBaggageEncodedLength) - { - lastGoodLength = encodedBaggage.Length; - } + EncodeBaggageValue(item.Value, ref encodedBaggage); } - } while (e.MoveNext() && entriesCount < MaxBaggageEntriesToEmit && encodedBaggage.Length < MaxBaggageEncodedLength); + encodedBaggage.Append(CommaWithSpace); - if (lastGoodLength - 2 > 0) - { - setter(carrier, Baggage, encodedBaggage.AsSpan(0, lastGoodLength - 2).ToString()); + entriesCount++; + + if (encodedBaggage.Length < MaxBaggageEncodedLength) + { + lastGoodLength = encodedBaggage.Length; + } } + } while (e.MoveNext() && entriesCount < MaxBaggageEntriesToEmit && encodedBaggage.Length < MaxBaggageEncodedLength); - encodedBaggage.Dispose(); + if (lastGoodLength - 2 > 0) + { + setter(carrier, Baggage, encodedBaggage.AsSpan(0, lastGoodLength - 2).ToString()); } + + encodedBaggage.Dispose(); } }