Skip to content

Commit 8fdc829

Browse files
authored
Propagators Support (#55419)
1 parent fce412b commit 8fdc829

File tree

8 files changed

+1095
-1
lines changed

8 files changed

+1095
-1
lines changed

src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,20 @@ public sealed class ActivityListener : IDisposable
253253
public System.Diagnostics.SampleActivity<string>? SampleUsingParentId { get { throw null; } set { throw null; } }
254254
public System.Diagnostics.SampleActivity<ActivityContext>? Sample { get { throw null; } set { throw null; } }
255255
public void Dispose() { throw null; }
256-
}
256+
}
257+
public abstract class TextMapPropagator
258+
{
259+
public delegate void PropagatorGetterCallback(object? carrier, string fieldName, out string? fieldValue, out System.Collections.Generic.IEnumerable<string>? fieldValues);
260+
public delegate void PropagatorSetterCallback(object? carrier, string fieldName, string fieldValue);
261+
public abstract System.Collections.Generic.IReadOnlyCollection<string> Fields { get; }
262+
public abstract void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter);
263+
public abstract void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState);
264+
public abstract System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, string?>>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter);
265+
public static TextMapPropagator Current { get; set; }
266+
public static TextMapPropagator CreateDefaultPropagator() { throw null; }
267+
public static TextMapPropagator CreatePassThroughPropagator() { throw null; }
268+
public static TextMapPropagator CreateNoOutputPropagator() { throw null; }
269+
}
257270
}
258271

259272
namespace System.Diagnostics.Metrics

src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@
3939
<Compile Include="System\Diagnostics\ActivitySource.cs" />
4040
<Compile Include="System\Diagnostics\DiagnosticSourceActivity.cs" />
4141
<Compile Include="System\Diagnostics\DiagLinkedList.cs" />
42+
<Compile Include="System\Diagnostics\LegacyPropagator.cs" />
43+
<Compile Include="System\Diagnostics\NoOutputPropagator.cs" />
44+
<Compile Include="System\Diagnostics\PassThroughPropagator.cs" />
4245
<Compile Include="System\Diagnostics\RandomNumberGenerator.cs" />
46+
<Compile Include="System\Diagnostics\TextMapPropagator.cs" />
4347
<Compile Include="System\Diagnostics\Metrics\AggregationManager.cs" />
4448
<Compile Include="System\Diagnostics\Metrics\Aggregator.cs" />
4549
<Compile Include="System\Diagnostics\Metrics\AggregatorStore.cs" />
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Net;
5+
using System.Collections.Generic;
6+
using System.Collections.ObjectModel;
7+
8+
namespace System.Diagnostics
9+
{
10+
internal sealed class LegacyPropagator : TextMapPropagator
11+
{
12+
internal static TextMapPropagator Instance { get; } = new LegacyPropagator();
13+
14+
public override IReadOnlyCollection<string> Fields { get; } = new ReadOnlyCollection<string>(new[] { TraceParent, RequestId, TraceState, Baggage, CorrelationContext });
15+
16+
public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter)
17+
{
18+
if (activity is null || setter is null)
19+
{
20+
return;
21+
}
22+
23+
string? id = activity.Id;
24+
if (id is null)
25+
{
26+
return;
27+
}
28+
29+
if (activity.IdFormat == ActivityIdFormat.W3C)
30+
{
31+
setter(carrier, TraceParent, id);
32+
if (!string.IsNullOrEmpty(activity.TraceStateString))
33+
{
34+
setter(carrier, TraceState, activity.TraceStateString);
35+
}
36+
}
37+
else
38+
{
39+
setter(carrier, RequestId, id);
40+
}
41+
42+
InjectBaggage(carrier, activity.Baggage, setter);
43+
}
44+
45+
public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState)
46+
{
47+
if (getter is null)
48+
{
49+
traceId = null;
50+
traceState = null;
51+
return;
52+
}
53+
54+
getter(carrier, TraceParent, out traceId, out _);
55+
if (traceId is null)
56+
{
57+
getter(carrier, RequestId, out traceId, out _);
58+
}
59+
60+
getter(carrier, TraceState, out traceState, out _);
61+
}
62+
63+
public override IEnumerable<KeyValuePair<string, string?>>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter)
64+
{
65+
if (getter is null)
66+
{
67+
return null;
68+
}
69+
70+
getter(carrier, Baggage, out string? theBaggage, out _);
71+
72+
IEnumerable<KeyValuePair<string, string?>>? baggage = null;
73+
if (theBaggage is null || !TryExtractBaggage(theBaggage, out baggage))
74+
{
75+
getter(carrier, CorrelationContext, out theBaggage, out _);
76+
if (theBaggage is not null)
77+
{
78+
TryExtractBaggage(theBaggage, out baggage);
79+
}
80+
}
81+
82+
return baggage;
83+
}
84+
85+
internal static bool TryExtractBaggage(string baggageString, out IEnumerable<KeyValuePair<string, string?>>? baggage)
86+
{
87+
baggage = null;
88+
List<KeyValuePair<string, string?>>? baggageList = null;
89+
90+
if (string.IsNullOrEmpty(baggageString))
91+
{
92+
return true;
93+
}
94+
95+
int currentIndex = 0;
96+
97+
do
98+
{
99+
// Skip spaces
100+
while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab))
101+
{
102+
currentIndex++;
103+
}
104+
105+
if (currentIndex >= baggageString.Length)
106+
{
107+
break; // No Key exist
108+
}
109+
110+
int keyStart = currentIndex;
111+
112+
// Search end of the key
113+
while (currentIndex < baggageString.Length && baggageString[currentIndex] != Space && baggageString[currentIndex] != Tab && baggageString[currentIndex] != '=')
114+
{
115+
currentIndex++;
116+
}
117+
118+
if (currentIndex >= baggageString.Length)
119+
{
120+
break;
121+
}
122+
123+
int keyEnd = currentIndex;
124+
125+
if (baggageString[currentIndex] != '=')
126+
{
127+
// Skip Spaces
128+
while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab))
129+
{
130+
currentIndex++;
131+
}
132+
133+
if (currentIndex >= baggageString.Length)
134+
{
135+
break; // Wrong key format
136+
}
137+
138+
if (baggageString[currentIndex] != '=')
139+
{
140+
break; // wrong key format.
141+
}
142+
}
143+
144+
currentIndex++;
145+
146+
// Skip spaces
147+
while (currentIndex < baggageString.Length && (baggageString[currentIndex] == Space || baggageString[currentIndex] == Tab))
148+
{
149+
currentIndex++;
150+
}
151+
152+
if (currentIndex >= baggageString.Length)
153+
{
154+
break; // Wrong value format
155+
}
156+
157+
int valueStart = currentIndex;
158+
159+
// Search end of the value
160+
while (currentIndex < baggageString.Length && baggageString[currentIndex] != Space && baggageString[currentIndex] != Tab &&
161+
baggageString[currentIndex] != Comma && baggageString[currentIndex] != Semicolon)
162+
{
163+
currentIndex++;
164+
}
165+
166+
if (keyStart < keyEnd && valueStart < currentIndex)
167+
{
168+
baggageList ??= new();
169+
170+
// Insert in reverse order for asp.net compatability.
171+
baggageList.Insert(0, new KeyValuePair<string, string?>(
172+
WebUtility.UrlDecode(baggageString.Substring(keyStart, keyEnd - keyStart)).Trim(s_trimmingSpaceCharacters),
173+
WebUtility.UrlDecode(baggageString.Substring(valueStart, currentIndex - valueStart)).Trim(s_trimmingSpaceCharacters)));
174+
}
175+
176+
// Skip to end of values
177+
while (currentIndex < baggageString.Length && baggageString[currentIndex] != Comma)
178+
{
179+
currentIndex++;
180+
}
181+
182+
currentIndex++; // Move to next key-value entry
183+
} while (currentIndex < baggageString.Length);
184+
185+
baggage = baggageList;
186+
return baggageList != null;
187+
}
188+
}
189+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
6+
namespace System.Diagnostics
7+
{
8+
internal sealed class NoOutputPropagator : TextMapPropagator
9+
{
10+
internal static TextMapPropagator Instance { get; } = new NoOutputPropagator();
11+
12+
public override IReadOnlyCollection<string> Fields { get; } = LegacyPropagator.Instance.Fields;
13+
14+
public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter)
15+
{
16+
// nothing to do.
17+
}
18+
19+
public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState);
20+
21+
public override IEnumerable<KeyValuePair<string, string?>>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter);
22+
}
23+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
6+
namespace System.Diagnostics
7+
{
8+
internal sealed class PassThroughPropagator : TextMapPropagator
9+
{
10+
internal static TextMapPropagator Instance { get; } = new PassThroughPropagator();
11+
12+
public override IReadOnlyCollection<string> Fields { get; } = LegacyPropagator.Instance.Fields;
13+
14+
public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter)
15+
{
16+
if (setter is null)
17+
{
18+
return;
19+
}
20+
21+
GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable<KeyValuePair<string, string?>>? baggage);
22+
if (parentId is null)
23+
{
24+
return;
25+
}
26+
27+
setter(carrier, isW3c ? TraceParent : RequestId, parentId);
28+
29+
if (!string.IsNullOrEmpty(traceState))
30+
{
31+
setter(carrier, TraceState, traceState);
32+
}
33+
34+
if (baggage is not null)
35+
{
36+
InjectBaggage(carrier, baggage, setter);
37+
}
38+
}
39+
40+
public override void ExtractTraceIdAndState(object? carrier, PropagatorGetterCallback? getter, out string? traceId, out string? traceState) => LegacyPropagator.Instance.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState);
41+
42+
public override IEnumerable<KeyValuePair<string, string?>>? ExtractBaggage(object? carrier, PropagatorGetterCallback? getter) => LegacyPropagator.Instance.ExtractBaggage(carrier, getter);
43+
44+
private static void GetRootId(out string? parentId, out string? traceState, out bool isW3c, out IEnumerable<KeyValuePair<string, string?>>? baggage)
45+
{
46+
Activity? activity = Activity.Current;
47+
48+
while (activity?.Parent is Activity parent)
49+
{
50+
activity = parent;
51+
}
52+
53+
traceState = activity?.TraceStateString;
54+
parentId = activity?.ParentId ?? activity?.Id;
55+
isW3c = parentId is not null ? Activity.TryConvertIdToContext(parentId, traceState, out _) : false;
56+
baggage = activity?.Baggage;
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)