Skip to content

Commit 18b81ba

Browse files
vancemdavidfowl
authored andcommitted
Support for receiving ID via TraceParent HTTP header. (#8495)
- Also moved the fetching of ANY HTTP header fields until AFTER the most stringent - Check to see if logging is on (thus deferring work if the logger is sampling). - Added transfer of TraceState from http header to Activity - Added tests to insure that we were reading traceparent and tracestate headers as expected.
1 parent 017f409 commit 18b81ba

File tree

2 files changed

+60
-11
lines changed

2 files changed

+60
-11
lines changed

src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ internal class HostingApplicationDiagnostics
2424

2525
private const string RequestIdHeaderName = "Request-Id";
2626
private const string CorrelationContextHeaderName = "Correlation-Context";
27+
private const string TraceParentHeaderName = "traceparent";
28+
private const string TraceStateHeaderName = "tracestate";
2729

2830
private readonly DiagnosticListener _diagnosticListener;
2931
private readonly ILogger _logger;
@@ -49,19 +51,12 @@ public void BeginRequest(HttpContext httpContext, ref HostingApplication.Context
4951
var diagnosticListenerEnabled = _diagnosticListener.IsEnabled();
5052
var loggingEnabled = _logger.IsEnabled(LogLevel.Critical);
5153

52-
// If logging is enabled or the diagnostic listener is enabled, try to get the correlation
53-
// id from the header
54-
StringValues correlationId = default;
55-
if (diagnosticListenerEnabled || loggingEnabled)
56-
{
57-
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out correlationId);
58-
}
5954

6055
if (diagnosticListenerEnabled)
6156
{
6257
if (_diagnosticListener.IsEnabled(ActivityName, httpContext))
6358
{
64-
context.Activity = StartActivity(httpContext, correlationId);
59+
context.Activity = StartActivity(httpContext);
6560
}
6661
if (_diagnosticListener.IsEnabled(DeprecatedDiagnosticsBeginRequestKey))
6762
{
@@ -73,6 +68,12 @@ public void BeginRequest(HttpContext httpContext, ref HostingApplication.Context
7368
// To avoid allocation, return a null scope if the logger is not on at least to some degree.
7469
if (loggingEnabled)
7570
{
71+
// Get the request ID (first try TraceParent header otherwise Request-ID header
72+
if (!httpContext.Request.Headers.TryGetValue(TraceParentHeaderName, out var correlationId))
73+
{
74+
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out correlationId);
75+
}
76+
7677
// Scope may be relevant for a different level of logging, so we always create it
7778
// see: https://github.com/aspnet/Hosting/pull/944
7879
// Scope can be null if logging is not on.
@@ -240,12 +241,22 @@ private static void RecordRequestStartEventLog(HttpContext httpContext)
240241
}
241242

242243
[MethodImpl(MethodImplOptions.NoInlining)]
243-
private Activity StartActivity(HttpContext httpContext, StringValues requestId)
244+
private Activity StartActivity(HttpContext httpContext)
244245
{
245246
var activity = new Activity(ActivityName);
247+
248+
if (!httpContext.Request.Headers.TryGetValue(TraceParentHeaderName, out var requestId))
249+
{
250+
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out requestId);
251+
}
252+
246253
if (!StringValues.IsNullOrEmpty(requestId))
247254
{
248255
activity.SetParentId(requestId);
256+
if (httpContext.Request.Headers.TryGetValue(TraceStateHeaderName, out var traceState))
257+
{
258+
activity.TraceStateString = traceState;
259+
}
249260

250261
// We expect baggage to be empty by default
251262
// Only very advanced users will be using it in near future, we encourage them to keep baggage small (few items)
@@ -280,4 +291,4 @@ private void StopActivity(HttpContext httpContext, Activity activity)
280291
_diagnosticListener.StopActivity(activity, new { HttpContext = httpContext });
281292
}
282293
}
283-
}
294+
}

src/Hosting/Hosting/test/HostingApplicationTests.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -267,6 +267,44 @@ public void ActivityParentIdAndBaggeReadFromHeaders()
267267
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key2" && pair.Value == "value2");
268268
}
269269

270+
271+
[Fact]
272+
public void ActivityTraceParentAndTraceStateFromHeaders()
273+
{
274+
var diagnosticSource = new DiagnosticListener("DummySource");
275+
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
276+
277+
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair => { }),
278+
s =>
279+
{
280+
if (s.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn"))
281+
{
282+
return true;
283+
}
284+
return false;
285+
});
286+
287+
features.Set<IHttpRequestFeature>(new HttpRequestFeature()
288+
{
289+
Headers = new HeaderDictionary()
290+
{
291+
{"traceparent", "00-0123456789abcdef0123456789abcdef-0123456789abcdef-01"},
292+
{"tracestate", "TraceState1"},
293+
{"Correlation-Context", "Key1=value1, Key2=value2"}
294+
}
295+
});
296+
hostingApplication.CreateContext(features);
297+
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
298+
Assert.Equal(ActivityIdFormat.W3C, Activity.Current.IdFormat);
299+
Assert.Equal("0123456789abcdef0123456789abcdef", Activity.Current.TraceId.ToHexString());
300+
Assert.Equal("0123456789abcdef", Activity.Current.ParentSpanId.ToHexString());
301+
Assert.Equal("TraceState1", Activity.Current.TraceStateString);
302+
303+
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key1" && pair.Value == "value1");
304+
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key2" && pair.Value == "value2");
305+
}
306+
307+
270308
private static void AssertProperty<T>(object o, string name)
271309
{
272310
Assert.NotNull(o);

0 commit comments

Comments
 (0)