-
Notifications
You must be signed in to change notification settings - Fork 775
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
Asp.Net Core Requests Instrumentation - change to use new Activity API #692
Asp.Net Core Requests Instrumentation - change to use new Activity API #692
Conversation
@@ -60,11 +63,21 @@ public override void OnStartActivity(Activity activity, object payload) | |||
return; | |||
} | |||
|
|||
// Create a brand new activity using ActivitySource |
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.
I added this code into existing place itself, which does span population. Once we settle on approach, we'll be removing all the span populations and replace with activity.
|
||
OpenTelemetrySdk.EnableOpenTelemetry( | ||
(builder) => builder.AddActivitySource("Microsoft.AspNetCore") | ||
.UseConsoleActivityExporter(opt => opt.DisplayAsJson = opt.DisplayAsJson)); |
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.
for easily trying this out locally, i changed to use consoleexporter.
The Instrumention(Adapter) hook to OpenTelemetryBuilder is not implemented in this draft PR.
@@ -90,6 +103,25 @@ public override void OnStartActivity(Activity activity, object payload) | |||
span.PutHttpUserAgentAttribute(userAgent); | |||
span.PutHttpRawUrlAttribute(GetUri(request)); | |||
} | |||
|
|||
if (newActivity.IsAllDataRequested) |
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.
newActivity [](start = 16, length = 11)
this can be null
{ | ||
newActivity.DisplayName = path; | ||
} | ||
|
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.
you can write this block as
newActivity?.DisplayName = path;
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 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.
add
<LangVersion>7.3</LangVersion>
to the csproj
@@ -36,6 +36,9 @@ internal class HttpInListener : ListenerHandler | |||
private readonly bool hostingSupportsW3C = false; | |||
private readonly AspNetCoreInstrumentationOptions options; | |||
|
|||
// Create an ActivitySource here to re-create Activity. | |||
private ActivitySource activitySource = new ActivitySource("Microsoft.AspNetCore"); |
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.
ActivitySource [](start = 16, length = 14)
do we usually create only one instance of HttpInListener and use it the life time of the app/service? I am asking to know if we need to explicitly dispose this ActivitySource when it is not needed.
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 only one instance for life of app.
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.
good. we should be ok here then. thanks.
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 the existing implementation, we dispose the DiagnosticListeners when TracerFactory is disposed. Don't think we need it here.
@@ -114,8 +146,15 @@ public override void OnStopActivity(Activity activity, object payload) | |||
var response = context.Response; | |||
|
|||
span.PutHttpStatusCode(response.StatusCode, response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase); | |||
|
|||
// activity here will be the activity which we created using ActivitySource in the Start callback. |
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.
// activity here will be the activity which we created using ActivitySource in the Start callback. [](start = 15, length = 99)
how this magic is done? doesn't OnStopActivity is teh call back called from DiagnosticListener?
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.
diagnosticlistener callback itself doesn't give us activity. Instead we access it using Activity.Current.
https://github.com/open-telemetry/opentelemetry-dotnet/blob/master/src/OpenTelemetry/Instrumentation/DiagnosticSourceListener.cs#L40
Since we started a new activity() in the OnBegin, Activity.Current will now be the one we created in OnBegin. This will have its .Parent set to the one created by "old" instrumentation code using DiagnosticSource.StartActivity
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.
make sense. thanks for the clarification. I am not expecting DiagnsoticListener will notify on the stop event of the Activity created from ActivitySource, right?
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.
Correct. (In fact diagnosticlistener will only notify for diagnosticsource.write events, never for activity.start or stop. On listening side, we get Activity.Current to get the activity)
@@ -90,6 +103,28 @@ public override void OnStartActivity(Activity activity, object payload) | |||
span.PutHttpUserAgentAttribute(userAgent); | |||
span.PutHttpRawUrlAttribute(GetUri(request)); | |||
} | |||
|
|||
if (newActivity != null) |
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.
nit: Can probably combine these two if
blocks.
@cijothomas I'm trying to fully understand the second issue in the description. If I'm following correctly, our new If that is correct, could we possibly have the .NET guys flow the old |
The IsAllDataRequested is settable - and the adapter itself can call Sampler and set IsAllDataRequested. But this requires the adapter to have OT SDK reference, something which we'd like to avoid. The old Activity too fires ActivityListener - Its fired if we enable ActivityListener for it. The old Activity has "empty" ActivitySource name, so it can be enabled with a call like
Let me know if this makes sense. (I am still exploring this more, so may have additional insights later :)) |
@tarekgh any issue with making @cijothomas what is the desired benefit of avoiding the |
Perhaps we could wrap the DiagnosticListener of each instrumentation with a common/base one that takes care of the "sampling" issue before passing the activity to the instrumentation one. So instrumentation writers don't need to worry about that and we have common code to deal with the SDK. |
I was chatting with Tarek yesterday about making Kind settable. I believe the API Spec says Kind must be immutable after starting. (We did a quick search on spec about this, but didn't find that restriction, but we both recall seeing it somewhere) I was referring to the fact that instrumentationadapters will need to take dependency on OT.SDK. which can be avoided. ( I know our current instrumentation has SDK dependency, but @SergeyKanzhelev and I was trying to get rid of that dependency, and have instrumetation depend on only the API, which in our "new" case would be just DiagnosticSource |
Yup - once Activity.Kind issue is handled, this approach is what I am going to explore more. I tried this first :) (before the duplicate activity creation method proposed in this PR) |
Will send another PR with the alternate proposal. Will use reflection hack to set Kind temporarily. |
I took a look at this and it seems that Kind was always settable, later it was added as a parameter to span creation on the specs. Java has a setter for it since part of the spec was derived from its implementation and it existed before Kind being added to the creation of the span. Go doesn't but it seems that it is just because they added the |
Okay. If Kind is settable, then @tarekgh can help to get this addressed for next .NET preview. |
@pjanotti @cijothomas I recall in early discussions when I proposed the first draft of the Activity APIs, I had the Kind was settable. I recall I got some feedback which I don't recall from who exactly mentioning the Kind should not be settable after creating the Activity. I don't mind to make it settable again if no objection from anyone. |
{ | ||
if (request.Host.Port == 80 || request.Host.Port == 443) | ||
{ | ||
newActivity.AddTag("http.host", request.Host.Host); |
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.
Why do you not include the port for 80/443?
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.
just retaining the current behavior. Current behavior is probably because 80,443 are common and can be deducted from other info like url etc.
newActivity.AddTag("http.path", path); | ||
|
||
var userAgent = request.Headers["User-Agent"].FirstOrDefault(); | ||
newActivity.AddTag("http.user_agent", userAgent); |
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.
It's possible for FirstOrDefault
to return null so we should add null check too.
newActivity.AddTag("http.user_agent", userAgent); | |
if (!string.IsNullOrEmpty(userAgent)) | |
{ | |
newActivity.AddTag("http.user_agent", userAgent); | |
} |
Closing this in favor of the alternate proposal. |
Goal: Fixes 3-d from #684
This is very draft PR to discuss with community/.NET team about the issues with adapting "old" with "new" DiagnosticSource instrumentation.
"old" - means instrumentation using DiagnosticSource.StartActivity() - currently implemented by Asp.Net Core, SqlClient, HttpClient .Net Core etc.
"new" means instrumentation using ActivitySource.StartActivity().
As all the existing libraries are implemening the old instrumentation method, we need adapters to translate them into new way.
Conceptually, it involves the following:
Issues that need to be addressed:
The activity which is coming from the old instrumentation will have ActivityKind set to Internal (which is the default). This property is not mutable after activity start.
The activity from old instrumentation will not have any knowledge about sampling, and activity is always created and has activity.IsAllDataRequested = true all the time. The adapters will now need to call Samplers, and take action based on sampling outcome. But to do this, adapters will have to take dependency on OT SDK. (samplers are not part of API).
One proposal which solves the above 2 issues (but causes something else) is added in this PR. The proposal is:
Adapters keep listening to the old events in the current way.
Inside OnStart and OnEnd callbacks, adapters create a new activity using ActivitySource, and populate its tags. This new activity will conform to new usage pattern.
This obviously has the drawback of creating an additional activity. (bad perf , how bad I need to measure)
Opening this draft PR early to solicit feedback on this approach and alternates.