Dependency injection ILoggerFactory, ILogger in middleware, logger not executing

2.9k views Asked by At

Trying to build a middleware that would log information for every request. ASP.NET Core 3.1 Learning some basics

I'm trying to understand some weird behavior. This is my constructor for middleware:

public ContextRequestLoggingMiddleware( RequestDelegate next, ILoggerFactory loggerFactory ) {
    this._next = next;
    _logger = loggerFactory.CreateLogger( "ContextRequestLoggingMiddleware" );            
}

Using this works as expected. Message is logged on every request:

public async Task InvokeAsync (HttpContext context ) {
    _logger.LogInformation( "test from middleware" );
    await _next( context );    
}

When I use code below:

_logger = loggerFactory.CreateLogger<ContextRequestLoggingMiddleware>();

Execution goes over _logger.LogInformation but it seems like it is not executed, nothing is logged, no error, no problem, just jumps to next line of code. Looking into _logger with WatchList I do not see any difference between those two approaches.

The same behavior where nothing is logged, and no error is encountered if I use ILogger<T>:

public ContextRequestLoggingMiddleware( RequestDelegate next, 
    ILogger<ContextRequestLoggingMiddleware> logger ) {
    this._next = next;
    _logger = logger;
}

I cannot inject plain ILogger as it throws a runtime error:

Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'Microsoft.AspNetCore.Builder.ContextRequestLoggingMiddleware'.

Again inspecting object I do not see difference between different approaches I used. So why is this? I would consider ILogger<T> as best choice, but why it does not work here? It does work for me in Controller. Thank you for explanation.

For context, my CreateHostBuilder:

public static IHostBuilder CreateHostBuilder( string[] args ) =>
    Host.CreateDefaultBuilder( args )
        .ConfigureLogging( ( hostContext, loggingBuilder ) => {
            loggingBuilder.ClearProviders();
            loggingBuilder.AddConfiguration( hostContext.Configuration.GetSection( "Logging" ) );
            loggingBuilder.AddCustomFileLogger();
        }
        ).ConfigureWebHostDefaults( webBuilder => {
            webBuilder.UseStartup<Startup>();
        } );

And part of my Startup class:

public void ConfigureServices( IServiceCollection services ) {
    services.AddControllers();
    services.AddDbContext<NPAContext>( options =>
         options.UseSqlServer( Configuration.GetConnectionString( cnnDB ) ) );
    services.AddAuthentication( options => {
        options.DefaultAuthenticateScheme = ApiKeyAuthenticationOptions.DefaultScheme;
        options.DefaultChallengeScheme = ApiKeyAuthenticationOptions.DefaultScheme;
    } )
        .AddApiKeySupport( options => { } );
    
    services.AddSingleton<List<JobEventDTO>>();            
}

public void Configure( IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory ) {
    if ( env.IsDevelopment() ) {
        app.UseDeveloperExceptionPage();
    }
    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseContextRequestLoggingMiddleware();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints( endpoints => {
        endpoints.MapControllers();
    } );
}

Edit Resolution: I made a mistake, and when building class ContextRequestLoggingMiddleware I placed it under NameSpace Microsoft.AspNetCore.Builder. With this, the only way how to get logger to work in MiddleWare was passing ILoggerFactory, and creating logger, but it worked only one way

_logger = loggerFactory.CreateLogger( "ContextRequestLoggingMiddleware" )

Once I placed the class under different NameSpace, other approaches started working as well

_logger = loggerFactory.CreateLogger<ContextRequestLoggingMiddleware>()

And also passing ILogger<ContextRequestLoggingMiddleware> now works

So building class as below now works for me. Interesting how change of namespace makes it work.

namespace NPARetrieval.Middleware {
public class ContextRequestLoggingMiddleware {
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;

    public ContextRequestLoggingMiddleware( RequestDelegate next, ILogger<ContextRequestLoggingMiddleware> logger ) {
        this._next = next;
        _logger = logger;
    }


    public async Task InvokeAsync (HttpContext context ) {
        _logger.LogInformation( "test from middleware" );
        await _next( context );    
    }
}

}

0

There are 0 answers