-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
Add Counters for Endpoints #21675
Add Counters for Endpoints #21675
Conversation
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.
Are tests needed?
endpointCounter.Failed(); | ||
WriteEvent(3, endpoint); | ||
} | ||
|
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: one empty line too much.
{ | ||
DisplayName = $"{endpoint}:Failed Requests" | ||
}; | ||
|
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: remove empty line
@@ -62,10 +63,13 @@ public Task Invoke(HttpContext httpContext) | |||
} | |||
catch (Exception exception) | |||
{ | |||
EndpointMiddlewareEventSource.Log.ExecutedEndpoint(endpoint.DisplayName); | |||
EndpointMiddlewareEventSource.Log.FailedEndpoint(endpoint.DisplayName); |
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.
May it be "better" to handle the executed within the failed?
So that here is just one method call, and in the internals of Log.FailedEndpoint
this case gets handled and has the potential to adapt it later if needed.
Absolutely. I will add tests after the team approved the design. |
[Event(1, Level = EventLevel.Informational)] | ||
public void ExecutingEndpoint(string endpoint) | ||
{ | ||
var endpointCounter = _endpointCounters.GetOrAdd(endpoint, key => new EndpointCounter(key, this)); |
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.
Looking up a dictionary multiple times (executing, executed, failed) for every call would have a performance impact. This code is on a very hot path.
I don't know the best way to mitigate but EventSource.OnEventCommand provides a hook to decide to enable counters. We should only be initializing counters when 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.
aspnetcore/src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelEventSource.cs
Lines 314 to 319 in 5a0c097
protected override void OnEventCommand(EventCommandEventArgs command) | |
{ | |
if (command.Command == EventCommand.Enable) | |
{ | |
// This is the convention for initializing counters in the RuntimeEventSource (lazily on the first enable command). | |
// They aren't disabled afterwards... |
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 problem is that even if the event source is disabled, we would still need to do the dictionary lookup to ensure EndpointCounter._totalRequests/_currentRequests/_failedRequests
are all accurate when the event source becomes enabled.
We can still override OnEventCommand to lazily initialize the PollingCounters, but we'd have to iterate _endpointCounters
dictionary when the event source becomes enabled to do so.
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.
Ah, right. We could add counters as metadata or a property on the endpoint. However endpoints are intended to be immutable and Ryan would murder us all 😄
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 was thinking the same thing. Problem with a property is that Endpoint is defined in another assembly and InternalsVisibleTo is bad. Putting it in Metadata doesn't work because it is immutable and Ryan would murder us. Retrieving the EndpointCounter from Metadata would involve a dictionary lookup anyway.
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 would be some other challenges, like an application's endpoints can change. For example, dropping a new Razor file into a directory would change that endpoint data source to include the new endpoint and the matcher would recompute the DFA graph. When that happens we'd need to keep the endpoint+counter instances together. Not difficult, but something to take into account.
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.
What should happen if someone wants to SetEndpoint
without Routing Middleware. Do they need to provide the counter data as well?
Correct me if I'm wrong, but Routing matcher only gives RouteEndpoint
not Endpoint
, and If we are going with getting counters from Routing matcher, can we instead add a property to RouteEndpoint
and since it's in the same assembly as there's no need to InternalVisibleTo
.
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.
Routing matching usual matches RouteEndpoint
but can return Endpoint
from a policy.
That's a good point about SetEndpoint. Matching is the primary way of setting endpoints, but not the only way. Matching providing the counter type wouldn't work.
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.
@davidfowl Thoughts?
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.
@noahfalk This is an example of where we want dimensions for metrics to avoid the dictionary workaround.
Thanks for trying this out @Kahbazi, we're going to wait for dimensions support in counters before doing this. |
Fixes #19027