diff --git a/src/Http/Routing/src/EndpointMiddleware.cs b/src/Http/Routing/src/EndpointMiddleware.cs index 0e11e560b7e2..98384f483e76 100644 --- a/src/Http/Routing/src/EndpointMiddleware.cs +++ b/src/Http/Routing/src/EndpointMiddleware.cs @@ -50,6 +50,7 @@ public Task Invoke(HttpContext httpContext) } } + EndpointMiddlewareEventSource.Log.ExecutingEndpoint(endpoint.DisplayName); Log.ExecutingEndpoint(_logger, endpoint); try @@ -62,10 +63,13 @@ public Task Invoke(HttpContext httpContext) } catch (Exception exception) { + EndpointMiddlewareEventSource.Log.ExecutedEndpoint(endpoint.DisplayName); + EndpointMiddlewareEventSource.Log.FailedEndpoint(endpoint.DisplayName); Log.ExecutedEndpoint(_logger, endpoint); return Task.FromException(exception); } + EndpointMiddlewareEventSource.Log.ExecutedEndpoint(endpoint.DisplayName); Log.ExecutedEndpoint(_logger, endpoint); return Task.CompletedTask; } @@ -77,6 +81,12 @@ static async Task AwaitRequestTask(Endpoint endpoint, Task requestTask, ILogger try { await requestTask; + EndpointMiddlewareEventSource.Log.ExecutedEndpoint(endpoint.DisplayName); + } + catch + { + EndpointMiddlewareEventSource.Log.FailedEndpoint(endpoint.DisplayName); + throw; } finally { diff --git a/src/Http/Routing/src/EndpointMiddlewareEventSource.cs b/src/Http/Routing/src/EndpointMiddlewareEventSource.cs new file mode 100644 index 000000000000..4875c6a7c7fc --- /dev/null +++ b/src/Http/Routing/src/EndpointMiddlewareEventSource.cs @@ -0,0 +1,101 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Concurrent; +using System.Diagnostics.Tracing; +using System.Threading; + +namespace Microsoft.AspNetCore.Routing +{ + internal class EndpointMiddlewareEventSource : EventSource + { + public static readonly EndpointMiddlewareEventSource Log = new EndpointMiddlewareEventSource(); + + private readonly ConcurrentDictionary _endpointCounters = new ConcurrentDictionary(); + + internal EndpointMiddlewareEventSource() + : base("Microsoft.AspNetCore.Routing") + { + + } + + [Event(1, Level = EventLevel.Informational)] + public void ExecutingEndpoint(string endpoint) + { + var endpointCounter = _endpointCounters.GetOrAdd(endpoint, key => new EndpointCounter(key, this)); + endpointCounter.Executing(); + WriteEvent(3, endpoint); + } + + [Event(2, Level = EventLevel.Informational)] + public void ExecutedEndpoint(string endpoint) + { + var endpointCounter = _endpointCounters.GetOrAdd(endpoint, key => new EndpointCounter(key, this)); + endpointCounter.Executed(); + WriteEvent(2, endpoint); + } + + [Event(3, Level = EventLevel.Error)] + public void FailedEndpoint(string endpoint) + { + var endpointCounter = _endpointCounters.GetOrAdd(endpoint, key => new EndpointCounter(key, this)); + endpointCounter.Failed(); + WriteEvent(3, endpoint); + } + + + private class EndpointCounter + { + private readonly IncrementingPollingCounter _requestsPerSecondCounter; + private readonly PollingCounter _totalRequestsCounter; + private readonly PollingCounter _failedRequestsCounter; + private readonly PollingCounter _currentRequestsCounter; + + private long _totalRequests; + private long _currentRequests; + private long _failedRequests; + + public EndpointCounter(string endpoint, EventSource eventSource) + { + _requestsPerSecondCounter = new IncrementingPollingCounter($"{endpoint}:requests-per-second", eventSource, () => _totalRequests) + { + DisplayName = $"{endpoint}:Request Rate", + DisplayRateTimeScale = TimeSpan.FromSeconds(1) + }; + + _totalRequestsCounter = new PollingCounter($"{endpoint}:total-requests", eventSource, () => _totalRequests) + { + DisplayName = $"{endpoint}:Total Requests", + }; + + _currentRequestsCounter = new PollingCounter($"{endpoint}:current-requests", eventSource, () => _currentRequests) + { + DisplayName = $"{endpoint}:Current Requests" + }; + + _failedRequestsCounter = new PollingCounter($"{endpoint}:failed-requests", eventSource, () => _failedRequests) + { + DisplayName = $"{endpoint}:Failed Requests" + }; + + } + + public void Executing() + { + Interlocked.Increment(ref _totalRequests); + Interlocked.Increment(ref _currentRequests); + } + + public void Executed() + { + Interlocked.Decrement(ref _currentRequests); + } + + public void Failed() + { + Interlocked.Increment(ref _failedRequests); + } + } + } +}