-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Create an activity more often than before #9361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
61099ef
30f00cc
fe8cf3a
bb1968a
f23d856
b582b74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,13 +51,13 @@ public void BeginRequest(HttpContext httpContext, ref HostingApplication.Context | |
var diagnosticListenerEnabled = _diagnosticListener.IsEnabled(); | ||
var loggingEnabled = _logger.IsEnabled(LogLevel.Critical); | ||
|
||
if (loggingEnabled || (diagnosticListenerEnabled && _diagnosticListener.IsEnabled(ActivityName, httpContext))) | ||
{ | ||
context.Activity = StartActivity(httpContext); | ||
} | ||
|
||
if (diagnosticListenerEnabled) | ||
{ | ||
if (_diagnosticListener.IsEnabled(ActivityName, httpContext)) | ||
{ | ||
context.Activity = StartActivity(httpContext); | ||
} | ||
if (_diagnosticListener.IsEnabled(DeprecatedDiagnosticsBeginRequestKey)) | ||
{ | ||
startTimestamp = Stopwatch.GetTimestamp(); | ||
|
@@ -68,16 +68,10 @@ public void BeginRequest(HttpContext httpContext, ref HostingApplication.Context | |
// 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. | ||
context.Scope = _logger.RequestScope(httpContext, correlationId); | ||
context.Scope = _logger.RequestScope(httpContext, context.Activity.Id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @SergeyKanzhelev @lmolkova should we split this out into span id and trace id in the logs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what loggers are typically doing. Like in OpenCensus: https://github.com/census-instrumentation/opencensus-specs/blob/c3fd41b28fed96ceffc8c35af280d74cacea3ab2/trace/LogCorrelation.md#key-names-for-tracing-data or Elastic: elastic/ecs#99 |
||
|
||
if (_logger.IsEnabled(LogLevel.Information)) | ||
{ | ||
|
@@ -90,7 +84,6 @@ public void BeginRequest(HttpContext httpContext, ref HostingApplication.Context | |
LogRequestStarting(httpContext); | ||
} | ||
} | ||
|
||
context.StartTimestamp = startTimestamp; | ||
} | ||
|
||
|
@@ -138,13 +131,13 @@ public void RequestEnd(HttpContext httpContext, Exception exception, HostingAppl | |
} | ||
|
||
} | ||
} | ||
|
||
var activity = context.Activity; | ||
// Always stop activity if it was started | ||
if (activity != null) | ||
{ | ||
StopActivity(httpContext, activity); | ||
} | ||
var activity = context.Activity; | ||
// Always stop activity if it was started | ||
if (activity != null) | ||
{ | ||
StopActivity(httpContext, activity); | ||
} | ||
|
||
if (context.EventLogEnabled && exception != null) | ||
|
@@ -261,7 +254,7 @@ private Activity StartActivity(HttpContext httpContext) | |
// 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) | ||
string[] baggage = httpContext.Request.Headers.GetCommaSeparatedValues(CorrelationContextHeaderName); | ||
if (baggage != StringValues.Empty) | ||
if (baggage.Length > 0) | ||
{ | ||
foreach (var item in baggage) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
using System.Net.Http; | ||
using System.Net.Http.Headers; | ||
using System.Threading.Tasks; | ||
using System.Xml.Linq; | ||
using Microsoft.AspNetCore.Hosting; | ||
using Microsoft.AspNetCore.Mvc.Formatters.Xml; | ||
using Microsoft.AspNetCore.Mvc.Testing; | ||
|
@@ -222,20 +223,18 @@ public async Task ProblemDetails_IsSerialized() | |
// Arrange | ||
using (new ActivityReplacer()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could get rid of this type |
||
{ | ||
var expected = "<problem xmlns=\"urn:ietf:rfc:7807\">" + | ||
"<status>404</status>" + | ||
"<title>Not Found</title>" + | ||
"<type>https://tools.ietf.org/html/rfc7231#section-6.5.4</type>" + | ||
$"<traceId>{Activity.Current.Id}</traceId>" + | ||
"</problem>"; | ||
|
||
// Act | ||
var response = await Client.GetAsync("/api/XmlDataContractApi/ActionReturningClientErrorStatusCodeResult"); | ||
|
||
// Assert | ||
await response.AssertStatusCodeAsync(HttpStatusCode.NotFound); | ||
var content = await response.Content.ReadAsStringAsync(); | ||
XmlAssert.Equal(expected, content); | ||
var root = XDocument.Parse(content).Root; | ||
Assert.Equal("404", root.Element(root.Name.Namespace.GetName("status"))?.Value); | ||
Assert.Equal("Not Found", root.Element(root.Name.Namespace.GetName("title"))?.Value); | ||
Assert.Equal("https://tools.ietf.org/html/rfc7231#section-6.5.4", root.Element(root.Name.Namespace.GetName("type"))?.Value); | ||
// Activity is not null | ||
Assert.NotNull(root.Element(root.Name.Namespace.GetName("traceId"))?.Value); | ||
} | ||
} | ||
|
||
|
@@ -266,22 +265,21 @@ public async Task ValidationProblemDetails_IsSerialized() | |
// Arrange | ||
using (new ActivityReplacer()) | ||
{ | ||
var expected = "<problem xmlns=\"urn:ietf:rfc:7807\">" + | ||
"<status>400</status>" + | ||
"<title>One or more validation errors occurred.</title>" + | ||
$"<traceId>{Activity.Current.Id}</traceId>" + | ||
"<MVC-Errors>" + | ||
"<State>The State field is required.</State>" + | ||
"</MVC-Errors>" + | ||
"</problem>"; | ||
|
||
// Act | ||
var response = await Client.GetAsync("/api/XmlDataContractApi/ActionReturningValidationProblem"); | ||
|
||
// Assert | ||
await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); | ||
var content = await response.Content.ReadAsStringAsync(); | ||
XmlAssert.Equal(expected, content); | ||
var root = XDocument.Parse(content).Root; | ||
|
||
Assert.Equal("400", root.Element(root.Name.Namespace.GetName("status"))?.Value); | ||
Assert.Equal("One or more validation errors occurred.", root.Element(root.Name.Namespace.GetName("title"))?.Value); | ||
var mvcErrors = root.Element(root.Name.Namespace.GetName("MVC-Errors")); | ||
Assert.NotNull(mvcErrors); | ||
Assert.Equal("The State field is required.", mvcErrors.Element(root.Name.Namespace.GetName("State"))?.Value); | ||
// Activity is not null | ||
Assert.NotNull(root.Element(root.Name.Namespace.GetName("traceId"))?.Value); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
_logger.IsEnabled(LogLevel.Critical)
the right level to create an activity? (As generally it will always be on at critical or above)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory, there should always be an activity (I need to run some perf tests to see how this affects the default scenarios). This basically replaces the current request id. The current logic assumes if you're going to log a a scope, then the activity should be created.
We added this but there's kinda a chicken and egg problem as we need to make the activity to be a part of the scope before knowing if the scope if there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may also need to be another explicit way to turn it on or off outside logging but that's sorta why this is a draft.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resetable Activity on the connection using something like: #6895
Currently problematic as Activity doesn't support being cleared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The server would need to own activity creation in that case. I'm not sure we'll come up with anything reasonable in the 3.0 time frame for #6895. I think coupling it to logging is the best bet mostly because the data is for the logging system and that scenario has a bunch of allocations already (the scope, the messages being logged etc).
Maybe the log level needs to be adjusted to something like information but the category is this internal category (which might be a bit weird).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dotnet/extensions#1422
Then something like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only helps when scopes are off. We could just add a
DisableActivityCreation
to theWebHostOptions
if we wanted to remove the allocation in benchmarks.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anyway to do an inversion? Say for example AppInisights is set to sample 5% of activities rather than 100% is there a way to ask it "will you consume this activity" and create based on that; rather than create it 100% of the time and throw it away unobserved 95% of the time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes there's a plan to support sampling but this feature needs to work with logging when app insights isn't on so that it's possible to correlate logs without too much setup.
See #9340 for the sampling PR. The activity still needs to be created though (so there's a chicken and egg problem)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@benaadams we will need an
Activity
not only to track something, but also to propagate the decision that we decided NOT to track something. So head-based sampling would work across the components.