Resolving ILogger Service Resolution Issues in Microsoft.Extensions.Logging

Dec 01, 2025 · Programming · 22 views · 7.8

Keywords: Dependency Injection | ILogger | .NET Core

Abstract: This article provides an in-depth analysis of the 'Unable to resolve service for type Microsoft.Extensions.Logging.ILogger' error commonly encountered in .NET Core applications using dependency injection. It explains the distinction between ILogger and ILogger<T>, presents comprehensive solutions for manual ILogger service registration, and discusses best practices and considerations. Complete code examples and configuration instructions are included to help developers thoroughly understand and resolve such dependency injection issues.

Problem Background and Error Analysis

During .NET Core application development, many developers encounter a common dependency injection error: System.InvalidOperationException: 'Unable to resolve service for type Microsoft.Extensions.Logging.ILogger'. This error typically occurs when attempting to obtain an ILogger instance through constructor injection, even when logging services have been properly configured.

Difference Between ILogger and ILogger<T>

In the Microsoft.Extensions.Logging library, ILogger<T> is the default registered generic interface, while the plain ILogger interface is no longer automatically registered. This design decision is based on considerations of type safety and contextual clarity. ILogger<T> provides better type information, enabling log records to include the caller's type context.

The following code demonstrates a typical misconfiguration:

var services = new ServiceCollection()
    .AddLogging(logging => logging.AddConsole())
    .BuildServiceProvider();

And the incorrect injection approach:

private readonly ILogger _logger;

public MyClass(ILogger logger)
{
    _logger = logger;
}

Manual Registration of ILogger Service

To resolve this issue, the ILogger service must be manually registered in the dependency injection container. Here is the complete solution:

public void ConfigureServices(IServiceCollection services)
{
    // Build a temporary service provider to obtain ILogger<T> instance
    var serviceProvider = services.BuildServiceProvider();
    
    // Get ILogger<ApplicationLog> instance
    var logger = serviceProvider.GetService<ILogger<ApplicationLog>>();
    
    // Register ILogger as a singleton service
    services.AddSingleton(typeof(ILogger), logger);
    
    // Other service configurations...
}

Here, a dedicated logging class is defined:

public class ApplicationLog
{
    // This class is specifically for providing logging context
}

Implementation Principle Analysis

The core of this solution lies in leveraging the existing ILogger<T> registration to create an ILogger instance. Since ILogger<T> implements the ILogger interface, it can be directly assigned. Through the AddSingleton(typeof(ILogger), logger) method, we register this instance as a globally available ILogger service.

Alternative Solutions and Best Practices

Although manually registering ILogger resolves the problem, the recommended approach is to use ILogger<T> directly:

public class MyClass
{
    private readonly ILogger<MyClass> _logger;

    public MyClass(ILogger<MyClass> logger)
    {
        _logger = logger;
    }

    public void MyFunc()
    {
        _logger.Log(LogLevel.Error, "My Message");
    }
}

This approach offers several advantages:

Considerations and Potential Issues

When using the manual registration method, the following points should be noted:

  1. Ensure all necessary logging services are registered before calling BuildServiceProvider()
  2. Consider service lifecycle management to avoid circular dependencies
  3. For production environments, using ILogger<T> is recommended for better maintainability
  4. Pay attention to the order of service registration to ensure correct dependency relationships

Practical Application Scenarios

This solution has application value in various scenarios, particularly when dealing with legacy code or third-party libraries. For instance, when using libraries like LettuceEncrypt, the need to manually register ILogger frequently arises.

Here is a complete console application example:

class Program
{
    static void Main(string[] args)
    {
        var services = new ServiceCollection()
            .AddLogging(logging => logging.AddConsole())
            .AddTransient<MyService>()
            .BuildServiceProvider();

        var myService = services.GetService<MyService>();
        myService.DoSomething();
    }
}

public class MyService
{
    private readonly ILogger<MyService> _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    public void DoSomething()
    {
        _logger.LogInformation("Performing some operation");
    }
}

Conclusion

By understanding the difference between ILogger and ILogger<T>, and mastering the method of manual service registration, developers can effectively resolve logging service resolution issues in dependency injection. Although a manual registration solution is provided, it is recommended to prioritize the use of ILogger<T> in new projects for better development experience and code quality.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.