-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Initialize hosting trace with OTEL tags for sampling #62090
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
Conversation
It may make sense to actually have the aspnetcore/src/Shared/Diagnostics/ActivityCreator.cs Lines 13 to 22 in 6ab9fa6
|
What's the scenario where you want to set activity tags on creation? This seems like kind of niche feature and I'd prefer to see whether the problem can be solved with existing APIs. For example, there is a feature to get the current request activity, |
Is the scenario that you set a feature with tags earlier in the request. Tags could be different depending on the request. An activity is created very early in a request so the number of places to set it would be limited. Can you give an example of this being used? Also, what does the activity source do with the tags? |
I'm specifically using YARP as a reverse proxy for a large number of services. There are calls for many different host names. I want to make tracing sampling decision for those calls and would like to have separate buckets for each hostname. For example, in a given time window, I want a minimum number of traces to always be sampled and then start doing probabilistic sampling after that. As it is now, I can only get the base operation name (just The only way to get tags to be available at the time of making the sampling decision is for them to be passed in to the The flow is:
I don't know of any other way to get information available to the sampler than to pass the tags in on Create/StartActivity. This is extra work for every request, even those that end up not sampled, which is why it does nothing by default and would require a user to specifically opt in with their own implementation for specifically the data points they need in their particular scenario. For example, the vast majority of our services would not use this functionality; only our YARP usage would define an implementation to just add the host/ |
The OpenTelemetry spec has this SHOULD requirement for tags available at span creation time:
It's not free to add those so making it possible but explicitly opt-in seems like a good change. |
Right now tracing in ASP.NET Core doesn't follow the OTEL standard. When we do then those tags should be set immediately. It sounds like server address and port are what you want to use when making sampling decisions, right? The performance cost shouldn't be a concern. We would only call ActivitySource if someone is listening to the activity, so people who care about perf and aren't listening won't be impacted. |
For our specific use case we would only use Performance impact of this change would be initializing the feature if it's registered and doing the feature lookup if there's a listener:
|
/azp run |
Commenter does not have sufficient privileges for PR 62090 in repo dotnet/aspnetcore |
What are next steps on this to keep it from going stale? |
@JamesNK This continues to be a problem for us, preventing us from getting useful trace sampling on our YARP load balancer. I don't see any workaround we can do without this change or something else that updates how the Activity is created internally. Is there anything else that can help with this this considered for the .NET 10 timeframe? |
Summarizing my understanding:
Does that sound correct, @rkargMsft? It sounds like a compelling scenario. This is for a service which high enough traffic volume that simple probabilistic sampling is causing high-volume endpoints from drowning out the traffic of low-volume endpoints to the point where the latter do not get sampled. One follow-on question is, how should tags set at creation time interact with tags set after creation time (i.e, what is already there)? |
That sounds like an accurate summary. The existing tags added after Activity creation are made with
so if there's a Feature registered that sets a tag (like server.address ) then that will not get overwritten after sampling. It will remain with whatever value the Feature populated it with.
That seems like reasonable behavior as long as it's documented. |
An other option to consider could be hard coding an opinionated set of tags at Activity creation (like The downsides of that are
|
I don't want to add a new feature. I'd prefer that we followed what the standard proposes and makes the here about including some tags at creation time. The problem is there is a lot of the standard we don't follow around traces, and I doubt we'll have time in .NET 10 to switch to following it. |
If the cost of setting those tags (which should be minimal) is acceptable then those could get added. Would an incremental step be adding just:
Basically replace the Feature implementation in this PR with this: var tagsForCreation = new TagList();
if (_activitySource.HasListeners() && _context.Request.Host.HasValue)
{
var (host, port) = _context.Request.Host.HostAndPort;
tags.Add("server.address", host);
if (port is not null)
{
tags.Add("server.port", port);
}
else if (string.Equals("https", httpContext.Request.Scheme, StringComparison.OrdinalIgnoreCase))
{
tags.Add("server.port", 443);
}
else if (string.Equals("http", httpContext.Request.Scheme, StringComparison.OrdinalIgnoreCase))
{
tags.Add("server.port", 80);
}
} Setting the
If just host/port get added then it shouldn't be a breaking change later when the full OTel compliance gets implemented (since it would just be adding the method tag with the resolved value). There's also the opportunity to optimize the |
The internal HostAndPort property could be moved to a separate PR if desired. |
src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs
Outdated
Show resolved
Hide resolved
Is this direction acceptable? |
This seems like a good step forward. James is more aware of the OTel space so I'll leave the final call to @JamesNK. |
What's the next step to bring this to a resolution? |
This removes the need to expose a new Feature or make any other public changes.
7c5e970
to
9a45fbf
Compare
@rkargMsft I took a look at your PR, how it interacts with OTEL libraries, and how it aligns with our long term goal to set OTEL attributes on this activity by default. End result: I think populating these OTEL attributes on activity creation is a good approach. I don't think it will cause interop problems with OTEL libraries that add tags to the activity, but some more testing during RC period is required. So we might need to revert this (or default it to off and enable with app context switch) if problems come up. I've made some improvements to your PR. Adding more recommended created attributes, small perf optimizations, adding tests, adding an opt out switch incase this creates problems for anyone. Are you ok with these changes? |
private static readonly FrozenDictionary<string, string> KnownHttpMethods = FrozenDictionary.ToFrozenDictionary([ | ||
KeyValuePair.Create(HttpMethods.Connect, HttpMethods.Connect), | ||
KeyValuePair.Create(HttpMethods.Delete, HttpMethods.Delete), | ||
KeyValuePair.Create(HttpMethods.Get, HttpMethods.Get), | ||
KeyValuePair.Create(HttpMethods.Head, HttpMethods.Head), | ||
KeyValuePair.Create(HttpMethods.Options, HttpMethods.Options), | ||
KeyValuePair.Create(HttpMethods.Patch, HttpMethods.Patch), | ||
KeyValuePair.Create(HttpMethods.Post, HttpMethods.Post), | ||
KeyValuePair.Create(HttpMethods.Put, HttpMethods.Put), | ||
KeyValuePair.Create(HttpMethods.Trace, HttpMethods.Trace) | ||
], StringComparer.OrdinalIgnoreCase); |
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 QUERY
given #63260 went in yesterday?
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 list is based on the known values list at https://opentelemetry.io/docs/specs/semconv/registry/attributes/http

You're welcome to create an issue with OTEL folks to add QUERY to the standard. Then we'll add it here.
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.
This looks good. We can also run the RCs and provide feedback. |
In case this change causes problems or we decide that it's undesirable to partially support OTEL spec, @rkargMsft what do you think of defaulting it to off and using the AppContext switch to turn it on? i.e. in .NET 10 by default there are no creation tags and to get them you would add |
That should work just fine. |
/backport to release/10.0-rc1 |
Started backporting to release/10.0-rc1: https://github.com/dotnet/aspnetcore/actions/runs/17083435926 |
Feature for adding tags at Activity creation time
Summary of the changes
Adding
server.address
andserver.port
tags at Activity creation. This is to support using those tags when making sampling decisions as only tags provided in theCreateActivity
/StartActivity
are available at that time.Description
OpenTelemetry spec also says that
http.request.method
SHOULD be added but not including that in this PR due to the extra logic needed to compute that value.Fixes #50488