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
Instrumentation Adapters to support Activity API #701
Instrumentation Adapters to support Activity API #701
Conversation
var request = context.Request; | ||
|
||
// see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md | ||
var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/"; | ||
if (activity != 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.
activity [](start = 16, length = 8)
is it possible the Activity can be 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.
No. I didnt need the activity null check.
@@ -35,6 +36,7 @@ internal class HttpInListener : ListenerHandler | |||
private readonly PropertyFetcher beforeActionTemplateFetcher = new PropertyFetcher("Template"); | |||
private readonly bool hostingSupportsW3C = false; | |||
private readonly AspNetCoreInstrumentationOptions options; | |||
private readonly ActivitySampler sampler = new AlwaysOnActivitySampler(); |
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.
hardcoded sampler for just prototyping.
activity.DisplayName = path; | ||
} | ||
|
||
var samplingDecision = this.sampler.ShouldSample(activity.Context, activity.TraceId, default, path, activity.Kind, activity.Tags, activity.Links); |
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 is the part which makes me not 100% convinved this approach is right. All instrumentationadatpers now need to know about Samplers (which is SDK concept).
Instead of simply checking if activity.IsAllDataRequested, its now first setting IsAllDataRequested by running sampling algorithm
…hub.com/cijothomas/opentelemetry-dotnet into cijothomas/activity3adapter1a_alternate
…hub.com/cijothomas/opentelemetry-dotnet into cijothomas/activity3adapter1a_alternate
@@ -41,6 +43,16 @@ public SimpleActivityProcessor(ActivityExporter exporter) | |||
/// <inheritdoc /> | |||
public override void OnStart(Activity activity) | |||
{ | |||
// Perform sampling decision if source is empty. |
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 is not 100% correct, as the sampling decision is now made with the current ActivityKind (which would be Internal, the default value). Though none of the built-in Samplers makes use of ActivityKind, nothing prevents one from writing Sampler which takes Kind into consideration.
More better would be to "feed" this activity created outside of ActivitySource
, into the ActivityListener
's GetRequestedDataUsingContext
pipeline. That can be done in the instrumentationadapter itself, after setting the Kind to correct one, (and possibly enrich activity with more tags/links/events etc. if 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.
Thanks @CodeBlanch for proposing the idea of "feeding" an external activity to the GetRequestedDataUsingContext
pipeline.
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.
Did you present the idea to .NET team? Any feedback?
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.
@CodeBlanch we (the .NET team) will look at that to decide what is the best way can achieve this scenario. Thanks for your feedback, it is really helpful.
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.
Given that making sampling decision in SDK piece is going to be incorrect (as we have incorrect Kind), I'll move the sampling logic into the InstrumentationAdapters. Of course this forces Instrumentation to have dependency on Sampling - but doing this to make progress and uncover any other potential issues.
If everything else is good, and the only remaining issue is Instrumentation being aware of Samplers - we'll try to get a new API in activity source to run the GetRequestedDataUsingContext
.
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.
Given that making sampling decision in SDK piece is going to be incorrect (as we have incorrect Kind)
Is this to set the Kind? reflection can work here till we expose the Kind setter.
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.
We can't set the Kind to the correct one in the OT SDK as SDK doesn't know what is the actual kind. InstrumentationAdapter knows it, and it sets it.
Make Asp.Net instrumentation work with Activity API
…it tests and this is not planned to be in this repo as well.
src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
Show resolved
Hide resolved
@@ -39,6 +39,8 @@ public static class OpenTelemetryBuilderExtensions | |||
throw new ArgumentNullException(nameof(builder)); | |||
} | |||
|
|||
// Asp.Net Core is not instrumented with ActivitySource, hence |
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.
@cijothomas Question... if we listen for string.Empty in theory we'll get activities from a bunch of legacy DiagnosticSources flowing through our listener, do we need to filter out the stuff we don't care about somewhere?
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 are right. This will be followed up.
…hub.com/cijothomas/opentelemetry-dotnet into cijothomas/activity3adapter1a_alternate
}); | ||
OpenTelemetrySdk.Default.EnableOpenTelemetry( | ||
(builder) => builder.AddRequestInstrumentation().AddDependencyInstrumentation() | ||
.UseJaegerActivityExporter(o => |
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.
moved to jaeger as zipkin exporter with activity is not yet ready
[HttpGet] | ||
public IEnumerable<WeatherForecast> Get() | ||
{ | ||
var res = httpClient.GetStringAsync("http://google.com").Result; |
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 assume this is only for tests and not part of the final merge.
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 I can remove that. The testhttpClient example was doing something similar.
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.
To provide a little history, when I was building the .NET Framework instrumentation I added in those calls so I could do integration testing, basically. Make sure incoming & outgoing requests would show up in Jaeger/Zipkin correctly. In the end it made the samples more interesting so I left them in. 🤷♂️
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 see, what about adding a comment stating that it exists to also include outgoing on the sample?
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 adding a comment.
<ProjectReference Include="..\..\..\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" /> | ||
<ProjectReference Include="..\..\..\src\OpenTelemetry.Instrumentation.Dependencies\OpenTelemetry.Instrumentation.Dependencies.csproj" /> | ||
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" /> |
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 will create the Activity exporter for Zipkin this week or the next.
"Endpoint": "http://localhost:9411/api/v2/spans" | ||
"Jaeger": { | ||
"ServiceName": "jaeger-test", | ||
"Host": "localhost", |
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.
Side note: it feels strange that we need to set Jaeger Host and Port to what it should be default values. Anyway, to be addressed in a separate PR.
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 defaults look like they are in there:
opentelemetry-dotnet/src/OpenTelemetry.Exporter.Jaeger/JaegerExporterOptions.cs
Lines 35 to 40 in c7333e4
public string AgentHost { get; set; } = "localhost"; | |
/// <summary> | |
/// Gets or sets the Jaeger agent "compact thrift protocol" port. Default value: 6831. | |
/// </summary> | |
public int AgentPort { get; set; } = 6831; |
But IMO it's still worthwhile to show how you can change them via settings in the samples.
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 still worthwhile to show how you can change them via settings in the samples.
Makes sense, perhaps we should comment where appropriate so the code setting (again) the default values don't get copied and pasted everywhere.
|
||
TelemetrySpan span; | ||
if (this.options.TextFormat is TraceContextFormat) | ||
activity.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.
Due to the risk of high-cardinality on the path OTel spec suggests using the HTTP method instead. That said the path is much more useful in general and having an option to add it (more likely the absolute path) will be useful. There are other locations also using the path as DisplayName, but other correctly use the helper that gets the name per OTel spec. BTW, the link on the comment above is broken.
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.
Yes incoming requests were still using path as SpanName, hence I used it as activity.DisplayName.
This shouldn't block this PR, as we can (and should) address this separate from this PR.
{ | ||
public static OpenTelemetrySdk Default = new OpenTelemetrySdk(); |
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.
My vote would be for:
public class OpenTelemetrySdk : IDisposable
{
private OpenTelemetrySdk() {}
public static OpenTelemetrySdk EnableOpenTelemetry(Action<OpenTelemetryBuilder> configureOpenTelemetryBuilder)
}
Usage: OpenTelemetrySdk.EnableOpenTelemetry(...)
Over:
public class OpenTelemetrySdk : IDisposable
{
public static OpenTelemetrySdk Default = new OpenTelemetrySdk();
private OpenTelemetrySdk() {}
public OpenTelemetrySdk EnableOpenTelemetry(Action<OpenTelemetryBuilder> configureOpenTelemetryBuilder)
}
Usage: OpenTelemetrySdk.Default.EnableOpenTelemetry(...)
Reason: The Default doesn't seem to add anything and it's confusing because it is IDisposable. When I first saw it I thought I had to call OpenTelemetrySdk.Default.Dispose()
on shutdown. But really you need to dispose your instance, eg:
private OpenTelemetrySdk _OpenTelemetrySdk;
public void OnAppStart() => _OpenTelemetrySdk = OpenTelemetrySdk.EnableOpenTelemetry(...);
public void OnAppStop() => _OpenTelemetrySdk?.Dispose();
If we want to keep the default, the pattern should be like this?
public void OnAppStart() => OpenTelemetrySdk.Default.EnableOpenTelemetry(...);
public void OnAppStop() => _OpenTelemetrySdk.Default.Dispose();
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'm not 100% sure but I tend to like your approach @CodeBlanch but I think we can address it in a separate PR/discussion.
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.
@CodeBlanch shared another proposal which sounds very good! I will add it as a follow up PR.
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.
@cijothomas we have some follow-ups but it is in a good point to merge.
Goal: Fixes 3-a,c,d,e,f from #684
An alternate proposal for #692
There are quite a number of TODOs part of this PR. I want to address them outside of this PR, as this PR already got too much with all the instrumentationadapters!. Want to fix all instrumentionadapters so as to find any issues with Activity API and give .NET Team feedback on the same.
This approach does the following:
Adds
builder.AddActivitySource(string.Empty)
to get callbacks for Activities created by old instrumentations. (they do new Activity(), instead of using ActivitySource). The ActivitySource will be empty for the activities created using old approach, and hencestring.Empty
is subscribed to get them.Uses the same activity created by existing instrumentation, and adds tags to it by reading from payload.
The instrumentation now makes calls to Sampler, and set
IsAllDataRequested
and TraceFlags on Activity appropriately.Compared to #692 , this avoids creating a new duplicate Activity. But this now forces Instrumentation to have dependency on the SDK, to do sampling.
Major TODOs I'll address following this PR:
Kind will be settable once .NET ships new build. This PR currently uses reflection.
Avoid instrumentation being dependent on Sampling - its currently hardcoded. We are exploring if .NET can expose a method to run
GetRequestedDataUsingContext
pipeline (this is where we attach sampling logic) for activities created outside ActivitySource. If this happens, we'll simply switch to it. If not, we'll expose similar in OP.API, so instrumentation simply calls this method which internally can determine sampling. (Exact details to be designed as followup)Subscribing to ActivitySource String.Empty is probably too noisy. Need to design an alternative. Its possible to NOT subscribe to old Activity using ActivitySource, and instead make Instrumentation call the Start/Stop (or equivalent). (Exact details to be designed as followup)
Support ITextFormat for ActivityContext. This PR simply ignores this code path now.
Find a way to do filters. The current approach of httpclient/aspnet/core Filter mechanism is broken with this PR.
AzureClient instrumentation - kept it currently, but this will be moved outside of this repo.
Move the diagnosticsource listener code from SDK to API, so Instrumentation can depend on it by just referring to the OT API. (This issue is already existing, but listing here as well)
Cleanup ListenerHandle - there is code copying activity tags into span etc - they need to be removed once Activity replaces Span.
(tests doing some of the above are disabled until actual code fix is done)
I will open issues to track these separately and submit follow ups.