Addresses #439
This commit is contained in:
Jass Bagga 2017-10-12 14:23:01 -07:00 коммит произвёл GitHub
Родитель c53fb8f10a
Коммит bdbe922b22
10 изменённых файлов: 236 добавлений и 50 удалений

Просмотреть файл

@ -1,44 +0,0 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Dispatcher
{
internal static class DispatcherLoggerExtensions
{
// Too many matches
private static readonly Action<ILogger, string, Exception> _ambiguousEndpoints = LoggerMessage.Define<string>(
LogLevel.Error,
new EventId(1, "AmbiguousEndpoints"),
"Request matched multiple endpoints resulting in ambiguity. Matching endpoints: {AmbiguousEndpoints}");
// Unique match found
private static readonly Action<ILogger, string, Exception> _endpointMatched = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(1, "EndpointMatched"),
"Request matched endpoint: {endpointName}");
private static readonly Action<ILogger, PathString, Exception> _noEndpointsMatched = LoggerMessage.Define<PathString>(
LogLevel.Debug,
new EventId(2, "NoEndpointsMatched"),
"No endpoints matched the current request path: {PathString}");
public static void AmbiguousEndpoints(this ILogger logger, string ambiguousEndpoints)
{
_ambiguousEndpoints(logger, ambiguousEndpoints, null);
}
public static void EndpointMatched(this ILogger logger, Endpoint endpoint)
{
_endpointMatched(logger, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void NoEndpointsMatched(this ILogger logger, PathString pathString)
{
_noEndpointsMatched(logger, pathString, null);
}
}
}

Просмотреть файл

@ -53,24 +53,28 @@ namespace Microsoft.AspNetCore.Dispatcher
feature.Values = context.Values; feature.Values = context.Values;
await context.ShortCircuit(httpContext); await context.ShortCircuit(httpContext);
_logger.RequestShortCircuitedDispatcherMiddleware(context);
return; return;
} }
if (context.Endpoint != null) if (context.Endpoint != null)
{ {
_logger.LogInformation("Matched endpoint {Endpoint}", context.Endpoint.DisplayName); _logger.EndpointMatchedDispatcherMiddleware(context.Endpoint);
feature.Endpoint = context.Endpoint; feature.Endpoint = context.Endpoint;
feature.Values = context.Values; feature.Values = context.Values;
feature.Handler = entry.HandlerFactory.CreateHandler(feature.Endpoint); feature.Handler = entry.HandlerFactory.CreateHandler(feature.Endpoint);
if (feature.Handler == null) if (feature.Handler == null)
{ {
_logger.HandlerNotCreated(entry);
throw new InvalidOperationException("Couldn't create a handler, that's bad."); throw new InvalidOperationException("Couldn't create a handler, that's bad.");
} }
break; break;
} }
_logger.NoEndpointsMatchedMatcher(entry.Matcher);
} }
await _next(httpContext); await _next(httpContext);

Просмотреть файл

@ -42,14 +42,15 @@ namespace Microsoft.AspNetCore.Dispatcher
var feature = httpContext.Features.Get<IDispatcherFeature>(); var feature = httpContext.Features.Get<IDispatcherFeature>();
if (feature.Handler != null) if (feature.Handler != null)
{ {
_logger.LogInformation("Executing endpoint {Endpoint}", feature.Endpoint.DisplayName); _logger.ExecutingEndpoint(feature.Endpoint);
try try
{ {
await feature.Handler(_next)(httpContext); await feature.Handler(_next)(httpContext);
} }
finally finally
{ {
_logger.LogInformation("Executed endpoint {Endpoint}", feature.Endpoint.DisplayName); _logger.ExecutedEndpoint(feature.Endpoint);
} }
return; return;

Просмотреть файл

@ -3,12 +3,25 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Dispatcher namespace Microsoft.AspNetCore.Dispatcher
{ {
public class HttpMethodEndpointSelector : EndpointSelector public class HttpMethodEndpointSelector : EndpointSelector
{ {
private object _lock;
private bool _servicesInitialized;
public HttpMethodEndpointSelector()
{
_lock = new object();
}
protected ILogger Logger { get; private set; }
public override async Task SelectAsync(EndpointSelectorContext context) public override async Task SelectAsync(EndpointSelectorContext context)
{ {
if (context == null) if (context == null)
@ -16,6 +29,8 @@ namespace Microsoft.AspNetCore.Dispatcher
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
EnsureServicesInitialized(context);
var snapshot = context.CreateSnapshot(); var snapshot = context.CreateSnapshot();
var fallbackEndpoints = new List<Endpoint>(); var fallbackEndpoints = new List<Endpoint>();
@ -25,16 +40,20 @@ namespace Microsoft.AspNetCore.Dispatcher
if (endpoint == null || endpoint.HttpMethod == null) if (endpoint == null || endpoint.HttpMethod == null)
{ {
// No metadata. // No metadata.
Logger.NoHttpMethodFound(context.Endpoints[i]);
fallbackEndpoints.Add(context.Endpoints[i]); fallbackEndpoints.Add(context.Endpoints[i]);
context.Endpoints.RemoveAt(i); context.Endpoints.RemoveAt(i);
} }
else if (string.Equals(endpoint.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(endpoint.HttpMethod, context.HttpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
{ {
// The request method matches the endpoint's HTTP method. // The request method matches the endpoint's HTTP method.
Logger.RequestMethodMatchedEndpointMethod(endpoint.HttpMethod, context.Endpoints[i]);
} }
else else
{ {
// Not a match. // Not a match.
Logger.RequestMethodDidNotMatchEndpointMethod(context.HttpContext.Request.Method, endpoint.HttpMethod, context.Endpoints[i]);
context.Endpoints.RemoveAt(i); context.Endpoints.RemoveAt(i);
} }
} }
@ -45,8 +64,11 @@ namespace Microsoft.AspNetCore.Dispatcher
if (context.Endpoints.Count == 0) if (context.Endpoints.Count == 0)
{ {
Logger.NoEndpointMatchedRequestMethod(context.HttpContext.Request.Method);
// Nothing matched, do the fallback. // Nothing matched, do the fallback.
context.RestoreSnapshot(snapshot); context.RestoreSnapshot(snapshot);
context.Endpoints.Clear(); context.Endpoints.Clear();
for (var i = 0; i < fallbackEndpoints.Count; i++) for (var i = 0; i < fallbackEndpoints.Count; i++)
@ -57,5 +79,27 @@ namespace Microsoft.AspNetCore.Dispatcher
await context.InvokeNextAsync(); await context.InvokeNextAsync();
} }
} }
protected void EnsureServicesInitialized(EndpointSelectorContext context)
{
if (Volatile.Read(ref _servicesInitialized))
{
return;
}
EnsureServicesInitializedSlow(context);
}
private void EnsureServicesInitializedSlow(EndpointSelectorContext context)
{
lock (_lock)
{
if (!Volatile.Read(ref _servicesInitialized))
{
var services = context.HttpContext.RequestServices;
Logger = services.GetRequiredService<ILoggerFactory>().CreateLogger(GetType());
}
}
}
} }
} }

Просмотреть файл

@ -0,0 +1,159 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Dispatcher
{
internal static class LoggerExtensions
{
// MatcherBase
private static readonly Action<ILogger, string, Exception> _ambiguousEndpoints = LoggerMessage.Define<string>(
LogLevel.Error,
new EventId(0, "AmbiguousEndpoints"),
"Request matched multiple endpoints resulting in ambiguity. Matching endpoints: {AmbiguousEndpoints}");
private static readonly Action<ILogger, PathString, Exception> _noEndpointsMatched = LoggerMessage.Define<PathString>(
LogLevel.Debug,
new EventId(1, "NoEndpointsMatched"),
"No endpoints matched the current request path '{PathString}'.");
private static readonly Action<ILogger, string, Exception> _requestShortCircuitedMatcherBase = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(2, "RequestShortCircuited_MatcherBase"),
"The current request '{RequestPath}' was short circuited.");
private static readonly Action<ILogger, string, Exception> _endpointMatchedMatcherBase = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(3, "EndpointMatched_MatcherBase"),
"Request matched endpoint '{endpointName}'.");
// DispatcherMiddleware
private static readonly Action<ILogger, Type, Exception> _handlerNotCreated = LoggerMessage.Define<Type>(
LogLevel.Error,
new EventId(0, "HandlerNotCreated"),
"A handler could not be created for '{MatcherType}'.");
private static readonly Action<ILogger, string, Exception> _requestShortCircuitedDispatcherMiddleware = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(1, "RequestShortCircuited_DispatcherMiddleware"),
"The current request '{RequestPath}' was short circuited.");
private static readonly Action<ILogger, string, Exception> _endpointMatchedDispatcherMiddleware = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(2, "EndpointMatched_DispatcherMiddleware"),
"Request matched endpoint '{endpointName}'.");
private static readonly Action<ILogger, IMatcher, Exception> _noEndpointsMatchedMatcher = LoggerMessage.Define<IMatcher>(
LogLevel.Debug,
new EventId(3, "NoEndpointsMatchedMatcher"),
"No endpoints matched matcher '{Matcher}'.");
//EndpointMiddleware
private static readonly Action<ILogger, string, Exception> _executingEndpoint = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(0, "ExecutingEndpoint"),
"Executing endpoint '{EndpointName}'.");
private static readonly Action<ILogger, string, Exception> _executedEndpoint = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(1, "ExecutedEndpoint"),
"Executed endpoint '{EndpointName}'.");
// HttpMethodEndpointSelector
private static readonly Action<ILogger, string, Exception> _noHttpMethodFound = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(0, "NoHttpMethodFound"),
"No HTTP method specified for endpoint '{EndpointName}'.");
private static readonly Action<ILogger, string, string, Exception> _requestMethodMatchedEndpointMethod = LoggerMessage.Define<string, string>(
LogLevel.Information,
new EventId(1, "RequestMethodMatchedEndpointMethod"),
"Request method matched HTTP method '{Method}' for endpoint '{EndpointName}'.");
private static readonly Action<ILogger, string, string, string, Exception> _requestMethodDidNotMatchEndpointMethod = LoggerMessage.Define<string, string, string>(
LogLevel.Information,
new EventId(2, "RequestMethodDidNotMatchEndpointMethod"),
"Request method '{RequestMethod}' did not match HTTP method '{EndpointMethod}' for endpoint '{EndpointName}'.");
private static readonly Action<ILogger, string, Exception> _noEndpointMatchedRequestMethod = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(3, "NoEndpointMatchedRequestMethod"),
"No endpoint matched request method '{Method}'.");
public static void AmbiguousEndpoints(this ILogger logger, string ambiguousEndpoints)
{
_ambiguousEndpoints(logger, ambiguousEndpoints, null);
}
public static void EndpointMatchedMatcherBase(this ILogger logger, Endpoint endpoint)
{
_endpointMatchedMatcherBase(logger, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void NoEndpointsMatched(this ILogger logger, PathString pathString)
{
_noEndpointsMatched(logger, pathString, null);
}
public static void RequestShortCircuitedMatcherBase(this ILogger logger, MatcherContext matcherContext)
{
var requestPath = matcherContext.HttpContext.Request.Path;
_requestShortCircuitedMatcherBase(logger, requestPath, null);
}
public static void EndpointMatchedDispatcherMiddleware(this ILogger logger, Endpoint endpoint)
{
_endpointMatchedDispatcherMiddleware(logger, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void RequestShortCircuitedDispatcherMiddleware(this ILogger logger, MatcherContext matcherContext)
{
var requestPath = matcherContext.HttpContext.Request.Path;
_requestShortCircuitedDispatcherMiddleware(logger, requestPath, null);
}
public static void HandlerNotCreated(this ILogger logger, MatcherEntry matcher)
{
var matcherType = matcher.GetType();
_handlerNotCreated(logger, matcherType, null);
}
public static void NoEndpointsMatchedMatcher(this ILogger logger, IMatcher matcher)
{
_noEndpointsMatchedMatcher(logger, matcher, null);
}
public static void ExecutingEndpoint(this ILogger logger, Endpoint endpoint)
{
_executingEndpoint(logger, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void ExecutedEndpoint(this ILogger logger, Endpoint endpoint)
{
_executedEndpoint(logger, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void NoHttpMethodFound(this ILogger logger, Endpoint endpoint)
{
_noHttpMethodFound(logger, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void RequestMethodMatchedEndpointMethod(this ILogger logger, string httpMethod, Endpoint endpoint)
{
_requestMethodMatchedEndpointMethod(logger, httpMethod, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void RequestMethodDidNotMatchEndpointMethod(this ILogger logger, string requestMethod, string endpointMethod, Endpoint endpoint)
{
_requestMethodDidNotMatchEndpointMethod(logger, requestMethod, endpointMethod, endpoint.DisplayName ?? "Unnamed endpoint", null);
}
public static void NoEndpointMatchedRequestMethod(this ILogger logger, string requestMethod)
{
_noEndpointMatchedRequestMethod(logger, requestMethod, null);
}
}
}

Просмотреть файл

@ -25,7 +25,6 @@ namespace Microsoft.AspNetCore.Dispatcher
private bool _selectorsInitialized; private bool _selectorsInitialized;
private readonly Func<object> _selectorInitializer; private readonly Func<object> _selectorInitializer;
public MatcherBase() public MatcherBase()
{ {
_lock = new object(); _lock = new object();
@ -137,6 +136,7 @@ namespace Microsoft.AspNetCore.Dispatcher
if (selectorContext.ShortCircuit != null) if (selectorContext.ShortCircuit != null)
{ {
context.ShortCircuit = selectorContext.ShortCircuit; context.ShortCircuit = selectorContext.ShortCircuit;
Logger.RequestShortCircuitedMatcherBase(context);
return; return;
} }
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.Dispatcher
case 1: case 1:
context.Endpoint = selectorContext.Endpoints[0]; context.Endpoint = selectorContext.Endpoints[0];
Logger.EndpointMatched(context.Endpoint); Logger.EndpointMatchedMatcherBase(context.Endpoint);
return; return;
default: default:

Просмотреть файл

@ -76,6 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Dispatcher
await SelectEndpointAsync(context, (Endpoint[])entry.Tag); await SelectEndpointAsync(context, (Endpoint[])entry.Tag);
if (context.ShortCircuit != null) if (context.ShortCircuit != null)
{ {
Logger.RequestShortCircuited(context);
return; return;
} }

Просмотреть файл

@ -2,12 +2,19 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using Microsoft.AspNetCore.Dispatcher;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Routing.Logging namespace Microsoft.AspNetCore.Routing.Logging
{ {
internal static class TreeRouterLoggerExtensions internal static class TreeRouterLoggerExtensions
{ {
// TreeMatcher
private static readonly Action<ILogger, string, Exception> _requestShortCircuited = LoggerMessage.Define<string>(
LogLevel.Information,
new EventId(3, "RequestShortCircuited"),
"The current request '{RequestPath}' was short circuited.");
private static readonly Action<ILogger, string, string, Exception> _matchedRoute; private static readonly Action<ILogger, string, string, Exception> _matchedRoute;
static TreeRouterLoggerExtensions() static TreeRouterLoggerExtensions()
@ -18,6 +25,12 @@ namespace Microsoft.AspNetCore.Routing.Logging
"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'."); "Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.");
} }
public static void RequestShortCircuited(this ILogger logger, MatcherContext matcherContext)
{
var requestPath = matcherContext.HttpContext.Request.Path;
_requestShortCircuited(logger, requestPath, null);
}
public static void MatchedRoute( public static void MatchedRoute(
this ILogger logger, this ILogger logger,
string routeName, string routeName,

Просмотреть файл

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Dispatcher namespace Microsoft.AspNetCore.Dispatcher
@ -82,8 +83,13 @@ namespace Microsoft.AspNetCore.Dispatcher
private (EndpointSelectorContext, EndpointSelector) CreateContextAndSelector(string httpMethod, List<Endpoint> endpoints) private (EndpointSelectorContext, EndpointSelector) CreateContextAndSelector(string httpMethod, List<Endpoint> endpoints)
{ {
// Add services to the collection
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging();
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
httpContext.Request.Method = httpMethod; httpContext.Request.Method = httpMethod;
httpContext.RequestServices = serviceCollection.BuildServiceProvider();
var selector = new HttpMethodEndpointSelector(); var selector = new HttpMethodEndpointSelector();
var selectors = new List<EndpointSelector>() var selectors = new List<EndpointSelector>()

Просмотреть файл

@ -11,6 +11,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" /> <PackageReference Include="Microsoft.AspNetCore.Http" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Logging" />
</ItemGroup> </ItemGroup>
</Project> </Project>