diff --git a/src/Mvc/Mvc.ApiExplorer/src/ApiDescriptionGroupCollectionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/ApiDescriptionGroupCollectionProvider.cs
index b0eb899a42e2..5bd7bce0cc10 100644
--- a/src/Mvc/Mvc.ApiExplorer/src/ApiDescriptionGroupCollectionProvider.cs
+++ b/src/Mvc/Mvc.ApiExplorer/src/ApiDescriptionGroupCollectionProvider.cs
@@ -3,16 +3,18 @@
using System.Linq;
using Microsoft.AspNetCore.Mvc.Infrastructure;
+using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.ApiExplorer;
///
-public class ApiDescriptionGroupCollectionProvider : IApiDescriptionGroupCollectionProvider
+public partial class ApiDescriptionGroupCollectionProvider : IApiDescriptionGroupCollectionProvider
{
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
private readonly IApiDescriptionProvider[] _apiDescriptionProviders;
private ApiDescriptionGroupCollection? _apiDescriptionGroups;
+ private readonly ILogger? _logger;
///
/// Creates a new instance of .
@@ -28,7 +30,15 @@ public ApiDescriptionGroupCollectionProvider(
IEnumerable apiDescriptionProviders)
{
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
- _apiDescriptionProviders = apiDescriptionProviders.OrderBy(item => item.Order).ToArray();
+ _apiDescriptionProviders = [.. apiDescriptionProviders.OrderBy(item => item.Order)];
+ }
+
+ internal ApiDescriptionGroupCollectionProvider(
+ IActionDescriptorCollectionProvider actionDescriptorCollectionProvider,
+ IEnumerable apiDescriptionProviders,
+ ILoggerFactory loggerFactory) : this(actionDescriptorCollectionProvider, apiDescriptionProviders)
+ {
+ _logger = loggerFactory.CreateLogger();
}
///
@@ -52,6 +62,10 @@ private ApiDescriptionGroupCollection GetCollection(ActionDescriptorCollection a
foreach (var provider in _apiDescriptionProviders)
{
+ if (_logger is not null)
+ {
+ Log.ApiDescriptionProviderExecuting(_logger, provider.GetType().Name, provider.GetType().Assembly.GetName().Name, provider.GetType().Assembly.GetName().Version?.ToString());
+ }
provider.OnProvidersExecuting(context);
}
@@ -67,4 +81,10 @@ private ApiDescriptionGroupCollection GetCollection(ActionDescriptorCollection a
return new ApiDescriptionGroupCollection(groups, actionDescriptors.Version);
}
+
+ private static partial class Log
+ {
+ [LoggerMessage(2, LogLevel.Debug, "Executing API description provider '{ProviderName}' from assembly {ProviderAssembly} v{AssemblyVersion}.", EventName = "ApiDescriptionProviderExecuting")]
+ public static partial void ApiDescriptionProviderExecuting(ILogger logger, string providerName, string? providerAssembly, string? assemblyVersion);
+ }
}
diff --git a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/EndpointMethodInfoApiExplorerServiceCollectionExtensions.cs b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/EndpointMethodInfoApiExplorerServiceCollectionExtensions.cs
index f4648bab0df8..6144ddeb1769 100644
--- a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/EndpointMethodInfoApiExplorerServiceCollectionExtensions.cs
+++ b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/EndpointMethodInfoApiExplorerServiceCollectionExtensions.cs
@@ -5,6 +5,7 @@
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.DependencyInjection;
@@ -21,7 +22,10 @@ public static IServiceCollection AddEndpointsApiExplorer(this IServiceCollection
{
// Try to add default services in case MVC services aren't added.
services.TryAddSingleton();
- services.TryAddSingleton();
+ services.TryAddSingleton(sp => new ApiDescriptionGroupCollectionProvider(
+ sp.GetRequiredService(),
+ sp.GetServices(),
+ sp.GetRequiredService()));
services.TryAddEnumerable(
ServiceDescriptor.Transient());
diff --git a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs
index 35ef2e0c3275..6135d91a59b0 100644
--- a/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs
+++ b/src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs
@@ -3,7 +3,9 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
+using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
namespace Microsoft.Extensions.DependencyInjection;
@@ -30,7 +32,10 @@ public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder)
[RequiresUnreferencedCode("MVC does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")]
internal static void AddApiExplorerServices(IServiceCollection services)
{
- services.TryAddSingleton();
+ services.TryAddSingleton(sp => new ApiDescriptionGroupCollectionProvider(
+ sp.GetRequiredService(),
+ sp.GetServices(),
+ sp.GetRequiredService()));
services.TryAddEnumerable(
ServiceDescriptor.Transient());
}
diff --git a/src/Mvc/test/Mvc.FunctionalTests/ApiExplorerTest.cs b/src/Mvc/test/Mvc.FunctionalTests/ApiExplorerTest.cs
index 0ed76f604947..71d1ab1cc1ee 100644
--- a/src/Mvc/test/Mvc.FunctionalTests/ApiExplorerTest.cs
+++ b/src/Mvc/test/Mvc.FunctionalTests/ApiExplorerTest.cs
@@ -1552,6 +1552,19 @@ public async Task ApiAction_ForActionWithVoidResponseType(string path, Type type
});
}
+ [Fact]
+ public async Task ApiExplorer_LogsInvokedDescriptionProvidersOnStartup()
+ {
+ // Arrange & Act
+ var response = await Client.GetAsync("http://localhost/ApiExplorerHttpMethod/All");
+
+ // Assert
+ Assert.Contains(TestSink.Writes, w => w.EventId.Name?.Equals("ApiDescriptionProviderExecuting", StringComparison.Ordinal) == true);
+ Assert.Contains(TestSink.Writes, w => w.LoggerName.Equals("Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionGroupCollectionProvider", StringComparison.Ordinal));
+ Assert.Contains(TestSink.Writes, w => w.Message.Equals("Executing API description provider 'DefaultApiDescriptionProvider' from assembly Microsoft.AspNetCore.Mvc.ApiExplorer v9.0.0.0.", StringComparison.Ordinal));
+ Assert.Contains(TestSink.Writes, w => w.Message.Equals("Executing API description provider 'JsonPatchOperationsArrayProvider' from assembly Microsoft.AspNetCore.Mvc.NewtonsoftJson v42.42.42.42.", StringComparison.Ordinal));
+ }
+
private IEnumerable GetSortedMediaTypes(ApiExplorerResponseType apiResponseType)
{
return apiResponseType.ResponseFormats