Skip to content

Commit 08b60af

Browse files
authored
Log registered IApiDescriptionProvider implementations on load (#57318)
* Log registered IApiDescriptionProvider implementations on load * Use internal API for new constructor
1 parent 15d22a9 commit 08b60af

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

src/Mvc/Mvc.ApiExplorer/src/ApiDescriptionGroupCollectionProvider.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33

44
using System.Linq;
55
using Microsoft.AspNetCore.Mvc.Infrastructure;
6+
using Microsoft.Extensions.Logging;
67

78
namespace Microsoft.AspNetCore.Mvc.ApiExplorer;
89

910
/// <inheritdoc />
10-
public class ApiDescriptionGroupCollectionProvider : IApiDescriptionGroupCollectionProvider
11+
public partial class ApiDescriptionGroupCollectionProvider : IApiDescriptionGroupCollectionProvider
1112
{
1213
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
1314
private readonly IApiDescriptionProvider[] _apiDescriptionProviders;
1415

1516
private ApiDescriptionGroupCollection? _apiDescriptionGroups;
17+
private readonly ILogger? _logger;
1618

1719
/// <summary>
1820
/// Creates a new instance of <see cref="ApiDescriptionGroupCollectionProvider"/>.
@@ -28,7 +30,15 @@ public ApiDescriptionGroupCollectionProvider(
2830
IEnumerable<IApiDescriptionProvider> apiDescriptionProviders)
2931
{
3032
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
31-
_apiDescriptionProviders = apiDescriptionProviders.OrderBy(item => item.Order).ToArray();
33+
_apiDescriptionProviders = [.. apiDescriptionProviders.OrderBy(item => item.Order)];
34+
}
35+
36+
internal ApiDescriptionGroupCollectionProvider(
37+
IActionDescriptorCollectionProvider actionDescriptorCollectionProvider,
38+
IEnumerable<IApiDescriptionProvider> apiDescriptionProviders,
39+
ILoggerFactory loggerFactory) : this(actionDescriptorCollectionProvider, apiDescriptionProviders)
40+
{
41+
_logger = loggerFactory.CreateLogger<ApiDescriptionGroupCollectionProvider>();
3242
}
3343

3444
/// <inheritdoc />
@@ -52,6 +62,10 @@ private ApiDescriptionGroupCollection GetCollection(ActionDescriptorCollection a
5262

5363
foreach (var provider in _apiDescriptionProviders)
5464
{
65+
if (_logger is not null)
66+
{
67+
Log.ApiDescriptionProviderExecuting(_logger, provider.GetType().Name, provider.GetType().Assembly.GetName().Name, provider.GetType().Assembly.GetName().Version?.ToString());
68+
}
5569
provider.OnProvidersExecuting(context);
5670
}
5771

@@ -67,4 +81,10 @@ private ApiDescriptionGroupCollection GetCollection(ActionDescriptorCollection a
6781

6882
return new ApiDescriptionGroupCollection(groups, actionDescriptors.Version);
6983
}
84+
85+
private static partial class Log
86+
{
87+
[LoggerMessage(2, LogLevel.Debug, "Executing API description provider '{ProviderName}' from assembly {ProviderAssembly} v{AssemblyVersion}.", EventName = "ApiDescriptionProviderExecuting")]
88+
public static partial void ApiDescriptionProviderExecuting(ILogger logger, string providerName, string? providerAssembly, string? assemblyVersion);
89+
}
7090
}

src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/EndpointMethodInfoApiExplorerServiceCollectionExtensions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.AspNetCore.Mvc.ApiExplorer;
66
using Microsoft.AspNetCore.Mvc.Infrastructure;
77
using Microsoft.Extensions.DependencyInjection.Extensions;
8+
using Microsoft.Extensions.Logging;
89

910
namespace Microsoft.Extensions.DependencyInjection;
1011

@@ -21,7 +22,10 @@ public static IServiceCollection AddEndpointsApiExplorer(this IServiceCollection
2122
{
2223
// Try to add default services in case MVC services aren't added.
2324
services.TryAddSingleton<IActionDescriptorCollectionProvider, DefaultActionDescriptorCollectionProvider>();
24-
services.TryAddSingleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>();
25+
services.TryAddSingleton<IApiDescriptionGroupCollectionProvider>(sp => new ApiDescriptionGroupCollectionProvider(
26+
sp.GetRequiredService<IActionDescriptorCollectionProvider>(),
27+
sp.GetServices<IApiDescriptionProvider>(),
28+
sp.GetRequiredService<ILoggerFactory>()));
2529

2630
services.TryAddEnumerable(
2731
ServiceDescriptor.Transient<IApiDescriptionProvider, EndpointMetadataApiDescriptionProvider>());

src/Mvc/Mvc.ApiExplorer/src/DependencyInjection/MvcApiExplorerMvcCoreBuilderExtensions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
using System.Diagnostics.CodeAnalysis;
55
using Microsoft.AspNetCore.Mvc.ApiExplorer;
6+
using Microsoft.AspNetCore.Mvc.Infrastructure;
67
using Microsoft.Extensions.DependencyInjection.Extensions;
8+
using Microsoft.Extensions.Logging;
79

810
namespace Microsoft.Extensions.DependencyInjection;
911

@@ -30,7 +32,10 @@ public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder)
3032
[RequiresUnreferencedCode("MVC does not currently support trimming or native AOT.", Url = "https://aka.ms/aspnet/trimming")]
3133
internal static void AddApiExplorerServices(IServiceCollection services)
3234
{
33-
services.TryAddSingleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>();
35+
services.TryAddSingleton<IApiDescriptionGroupCollectionProvider>(sp => new ApiDescriptionGroupCollectionProvider(
36+
sp.GetRequiredService<IActionDescriptorCollectionProvider>(),
37+
sp.GetServices<IApiDescriptionProvider>(),
38+
sp.GetRequiredService<ILoggerFactory>()));
3439
services.TryAddEnumerable(
3540
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
3641
}

src/Mvc/test/Mvc.FunctionalTests/ApiExplorerTest.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,19 @@ public async Task ApiAction_ForActionWithVoidResponseType(string path, Type type
15521552
});
15531553
}
15541554

1555+
[Fact]
1556+
public async Task ApiExplorer_LogsInvokedDescriptionProvidersOnStartup()
1557+
{
1558+
// Arrange & Act
1559+
var response = await Client.GetAsync("http://localhost/ApiExplorerHttpMethod/All");
1560+
1561+
// Assert
1562+
Assert.Contains(TestSink.Writes, w => w.EventId.Name?.Equals("ApiDescriptionProviderExecuting", StringComparison.Ordinal) == true);
1563+
Assert.Contains(TestSink.Writes, w => w.LoggerName.Equals("Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionGroupCollectionProvider", StringComparison.Ordinal));
1564+
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));
1565+
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));
1566+
}
1567+
15551568
private IEnumerable<string> GetSortedMediaTypes(ApiExplorerResponseType apiResponseType)
15561569
{
15571570
return apiResponseType.ResponseFormats

0 commit comments

Comments
 (0)