Search code examples
c#.netdependency-injectionattributesserilog

Unable to resolve service for when accessing Serilog ILogger in IResourceAttribute, ExceptionFilterAttribute


Implementing a new .NET 6 Web API, I have added SeriLog hoping to handle exceptions through a custom ExceptionFilterAttribute and general HTTPContext entry/exit event logging but I am getting an error when the API is called.

I added Serilog via Program.cs like so:

var logger = new LoggerConfiguration()
        .ReadFrom.Configuration(builder.Configuration)
        .Enrich.FromLogContext()
        .CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);

Logging via ILogger.LogInformation in the controller is fine but when I try and use ILogger from an Attribute or implementation of IResourceAttribute it throws a Dependency Injection exception.

I.e. when I introduce some Attributes that use the ILogger like so to the Program.cs:


builder.Services.AddControllers(options =>
{ 
    options.Filters.Add<MyErrorHandlingAttribute>();
});

The Attribute class looks like:

    public class MyErrorHandlingAttribute: ExceptionFilterAttribute
    {
        private readonly ILogger _logger;
        public MyErrorHandlingAttribute(ILogger logger)
        {
            _logger= logger?? throw new ArgumentNullException(nameof(logger));
        }
    }

Exception:

System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'MessageOrchestrator.Common.Attributes.MyErrorHandlingAttribute'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method2(Closure , IServiceProvider , Object[] ) at Microsoft.AspNetCore.Mvc.TypeFilterAttribute.CreateInstance(IServiceProvider serviceProvider) at Microsoft.AspNetCore.Mvc.Filters.DefaultFilterProvider.ProvideFilter(FilterProviderContext context, FilterItem filterItem) at Microsoft.AspNetCore.Mvc.Filters.DefaultFilterProvider.OnProvidersExecuting(FilterProviderContext context) at Microsoft.AspNetCore.Mvc.Filters.FilterFactory.CreateUncachedFiltersCore(IFilterProvider[] filterProviders, ActionContext actionContext, List`1 filterItems)
at Microsoft.AspNetCore.Mvc.Filters.FilterFactory.GetAllFilters(IFilterProvider[] filterProviders, ActionContext actionContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerCache.GetCachedResult(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Routing.ControllerRequestDelegateFactory.<>c__DisplayClass12_0.b__0(HttpContext context) at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext) --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Sorry am quite new to SeriLog and would be grateful for any advice.


Solution

  • When using dependency injection, you need to inject a concrete ILogger<T> in your constructor, like e.g. ILogger<ExceptionFilterAttribute>.

    From the documentation

    To create logs, use an ILogger<TCategoryName> object from dependency injection (DI).

    The ASP.NET Core web apps use ILogger<T> to automatically get an ILogger instance that uses the fully qualified type name of T as the category.

    Your code will then look like below.

    public class MyErrorHandlingAttribute: ExceptionFilterAttribute
    {
        private readonly ILogger _logger;
        public MyErrorHandlingAttribute(ILogger<ExceptionFilterAttribute> logger)
        {
            _logger= logger ?? throw new ArgumentNullException(nameof(logger));
        }
    }