-
Notifications
You must be signed in to change notification settings - Fork 790
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
ActivitySourceAdapter sampling is internal-only #1761
ActivitySourceAdapter sampling is internal-only #1761
Conversation
…d listeners for sampling.
I ran into this real-world trying to provide a utility which listens to all the |
return new SampleResult(samplingResult, aco._samplerTags); | ||
} | ||
*/ | ||
private static Func<ActivitySource, Activity, ActivityContext, SampleResult> CreateCallSamplersFunc() |
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.
@tarekgh This was mega hard to put together 🤯 We don't have to expose anything in the public API, but if you could refactor the below block into a helper method that I could bind to it would be much easier and probably less fragile:
The main challenge with it as-is (primarily) is the closure. Have to hook into compiler-generated types to fire that.
Codecov Report
@@ Coverage Diff @@
## main #1761 +/- ##
==========================================
+ Coverage 83.14% 83.36% +0.21%
==========================================
Files 193 193
Lines 6005 6083 +78
==========================================
+ Hits 4993 5071 +78
Misses 1012 1012
|
@CodeBlanch could you explain what you are trying to do in general? why you want invoke all listeners? CC @noahfalk |
@tarekgh Activity created using ActivitySource.StartActivity gets passed to ActivityListener ShouldListenTo, ActivityStarted, ActivityStopped, & Sample callbacks. Activity created any other way gets passed to ActivityListener ShouldListenTo, ActivityStarted, ActivityStopped callbacks but NOT Sample callback. This was one of the gaps we talked about in our sessions last year. In order to get around it, we have this ActivitySourceAdapter class. Its purpose is to take those Activity objects created some other way (DiagnosticSource, for example) and feed them through for sampling. Before this PR, we just ran them through the OpenTelemetry Sampler. But that creates a weird situation where people using ActivityListener can participate in some of OpenTelemetry, but not all of it. This PR addresses that by running these outside Activity objects through all the listeners in the same way ActivitySource.StartActivity does. |
@CodeBlanch I think depending on the internals of the .NET implementation is fragile as we may (and likely) change it again when accommodating more APIs. I would say for this scenario, may be creating a new Activity using ActivitySource.StartActivity could be reasonable. Did you measure if this can be reasonable for the scenario you are talking about? |
@tarekgh Generally our goal is to have less allocations not more 😄 @cijothomas tried a bunch of different ways to make this work when he was building the ActivitySourceAdapter so I'll let him comment on that. I think if we had to decide between more allocations and supporting external sample interaction through ActivityListener, we would just drop the later. Long term we are working with aspnetcore team to use ActivitySource by default. Same thing will need to happen with HttpClient diagnostics. And then anything else still using DiagnosticSource, etc. It is a big, maybe impossible effort, but if we can do that, the ActivitySourceAdapter won't be needed. Our ultimate goal is to not need it. But really ActivitySourceAdapter and this whole migration effort (IMO) is to work around a strange behavior in the runtime. Let me phrase it as a question: What is the use case for Activity objects having two different flows through ActivityListener? That's basically why we need to do this. Today depending on how you create Activity, some will hit ActivityListener ShouldListenTo + ActivityStarted + ActivityStopped events while others (started through ActivitySource) hit ActivityListener ShouldListenTo + ActivityStarted + ActivityStopped + Sample events. If everything flowed the same, we wouldn't need the adapter. So the ask is: Can we modify runtime so all Activity objects flow through ActivityListener.Sample? Could we make that work in a backward-compatible way? I think so. If using ActivitySource, Sample behavior works as it does right now deciding whether or not to create Activity and then setting the alldatarequested & recorded flags. If using any other mechanism, Activity should always be created, sample result should only impact the alldatarequested & recorded flags. Just an idea 😄 Alternative idea would be for runtime to give us a method (on ActivitySource) to run an Activity instance through Sample and get back the decision. That's basically what this PR is doing. |
I am aware about this discussion but the question would be, does OTel is going to drop the support for the older versions of aspnet and Http Client?
I think the Sampling has to be done before creating the Activity object. This was the intention to avoid the allocation all together. In .NET 5.0 we were focusing more on the future direction. So old behavior just continue to work as it is but we enabled the new features and we didn't miss with the legacy support. As you pointed if users started to use ActivitySource then they wouldn't need to care about the legacy stuff. It is clear ActivityListener.Sample wil work with the activities created by ActivitySource. DiagnosticSource/Listener is the way to listen to old way created Activities and can be free to do whatever sampling they need to do with it.
I would say instead of complicating the whole thing by adding sampling support for already created Activities, we could just enable setting alldatarequested & recorded flags on the old created activities (even after starting it). this will have the same effect. Anyone interested to set these data there will just need to listen (using the DiagnosticsSource/Listener) and then set it. IMHO running the already created Activities to the sampler just to set these fields would be overkill. |
Interesting idea. I think we need to sample them because users could have registered all kinds of custom logic that will set those flags based on rules we won't have available? Still attempting to think through it 🤣 @cijothomas Any thoughts? |
@@ -80,7 +84,7 @@ public void Start(Activity activity, ActivityKind kind, ActivitySource source) | |||
|
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.
Could we achieve this by creating a temporary activity with the given source and use its TraceFlags and IsAllDataRequested information in conjunction with the tracer sampler information to update the activity correctly?
So something like below in the Start method would give us the information to decide the highest level of sampling for the activity.
var tempActivity = source.StartActivity("Test");
var isRecordedByOtherListeners = tempActivity.Recorded;
var isAllDataRequestedByOtherListeners = tempActivity.IsAllDataRequested;
tempActivity.Stop();
We would incur the cost of allocating a new activity object though.
Closing because |
The .NET API allows anyone to register an
ActivityListener
like this:The
Sample
call will apply to anyActivity
sent through whereShouldListenTo
resolves totrue
. In the case of multiple listeners, the highest level of sampling returned wins. This means users can participate in OpenTelemetry sampling if they so desire.ActivitySourceAdapter
however only runs the OpenTelemetry sampling.Changes
ActivitySourceAdapter
will now sample using all the registered listeners.Example
This test case is a good example of what this fix accomplishes:
opentelemetry-dotnet/test/OpenTelemetry.Tests/Trace/ActivitySourceAdapterTest.cs
Lines 270 to 290 in e962c8d
External ActivityListener impacting the OpenTelemetry sampling decision.
TODOs
CHANGELOG.md
updated for non-trivial changes