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.
This commit is contained in:
parent
017f409fe4
commit
18b81bacce
|
|
@ -24,6 +24,8 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
|
||||
private const string RequestIdHeaderName = "Request-Id";
|
||||
private const string CorrelationContextHeaderName = "Correlation-Context";
|
||||
private const string TraceParentHeaderName = "traceparent";
|
||||
private const string TraceStateHeaderName = "tracestate";
|
||||
|
||||
private readonly DiagnosticListener _diagnosticListener;
|
||||
private readonly ILogger _logger;
|
||||
|
|
@ -49,19 +51,12 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
var diagnosticListenerEnabled = _diagnosticListener.IsEnabled();
|
||||
var loggingEnabled = _logger.IsEnabled(LogLevel.Critical);
|
||||
|
||||
// If logging is enabled or the diagnostic listener is enabled, try to get the correlation
|
||||
// id from the header
|
||||
StringValues correlationId = default;
|
||||
if (diagnosticListenerEnabled || loggingEnabled)
|
||||
{
|
||||
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out correlationId);
|
||||
}
|
||||
|
||||
if (diagnosticListenerEnabled)
|
||||
{
|
||||
if (_diagnosticListener.IsEnabled(ActivityName, httpContext))
|
||||
{
|
||||
context.Activity = StartActivity(httpContext, correlationId);
|
||||
context.Activity = StartActivity(httpContext);
|
||||
}
|
||||
if (_diagnosticListener.IsEnabled(DeprecatedDiagnosticsBeginRequestKey))
|
||||
{
|
||||
|
|
@ -73,6 +68,12 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
// To avoid allocation, return a null scope if the logger is not on at least to some degree.
|
||||
if (loggingEnabled)
|
||||
{
|
||||
// Get the request ID (first try TraceParent header otherwise Request-ID header
|
||||
if (!httpContext.Request.Headers.TryGetValue(TraceParentHeaderName, out var correlationId))
|
||||
{
|
||||
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out correlationId);
|
||||
}
|
||||
|
||||
// Scope may be relevant for a different level of logging, so we always create it
|
||||
// see: https://github.com/aspnet/Hosting/pull/944
|
||||
// Scope can be null if logging is not on.
|
||||
|
|
@ -240,12 +241,22 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private Activity StartActivity(HttpContext httpContext, StringValues requestId)
|
||||
private Activity StartActivity(HttpContext httpContext)
|
||||
{
|
||||
var activity = new Activity(ActivityName);
|
||||
|
||||
if (!httpContext.Request.Headers.TryGetValue(TraceParentHeaderName, out var requestId))
|
||||
{
|
||||
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out requestId);
|
||||
}
|
||||
|
||||
if (!StringValues.IsNullOrEmpty(requestId))
|
||||
{
|
||||
activity.SetParentId(requestId);
|
||||
if (httpContext.Request.Headers.TryGetValue(TraceStateHeaderName, out var traceState))
|
||||
{
|
||||
activity.TraceStateString = traceState;
|
||||
}
|
||||
|
||||
// We expect baggage to be empty by default
|
||||
// Only very advanced users will be using it in near future, we encourage them to keep baggage small (few items)
|
||||
|
|
@ -280,4 +291,4 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
|||
_diagnosticListener.StopActivity(activity, new { HttpContext = httpContext });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
|
@ -267,6 +267,44 @@ namespace Microsoft.AspNetCore.Hosting.Tests
|
|||
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key2" && pair.Value == "value2");
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void ActivityTraceParentAndTraceStateFromHeaders()
|
||||
{
|
||||
var diagnosticSource = new DiagnosticListener("DummySource");
|
||||
var hostingApplication = CreateApplication(out var features, diagnosticSource: diagnosticSource);
|
||||
|
||||
diagnosticSource.Subscribe(new CallbackDiagnosticListener(pair => { }),
|
||||
s =>
|
||||
{
|
||||
if (s.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
features.Set<IHttpRequestFeature>(new HttpRequestFeature()
|
||||
{
|
||||
Headers = new HeaderDictionary()
|
||||
{
|
||||
{"traceparent", "00-0123456789abcdef0123456789abcdef-0123456789abcdef-01"},
|
||||
{"tracestate", "TraceState1"},
|
||||
{"Correlation-Context", "Key1=value1, Key2=value2"}
|
||||
}
|
||||
});
|
||||
hostingApplication.CreateContext(features);
|
||||
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
|
||||
Assert.Equal(ActivityIdFormat.W3C, Activity.Current.IdFormat);
|
||||
Assert.Equal("0123456789abcdef0123456789abcdef", Activity.Current.TraceId.ToHexString());
|
||||
Assert.Equal("0123456789abcdef", Activity.Current.ParentSpanId.ToHexString());
|
||||
Assert.Equal("TraceState1", Activity.Current.TraceStateString);
|
||||
|
||||
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key1" && pair.Value == "value1");
|
||||
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key2" && pair.Value == "value2");
|
||||
}
|
||||
|
||||
|
||||
private static void AssertProperty<T>(object o, string name)
|
||||
{
|
||||
Assert.NotNull(o);
|
||||
|
|
|
|||
Loading…
Reference in New Issue