Не удалось разрешить ILogger из Microsoft.Extensions.Logging

Я настроил консольное приложение Main вот так

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

А потом я пытаюсь использовать его в другом классе, так

    private readonly ILogger _logger;

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

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

System.InvalidOperationException: "Не удается разрешить службу для типа" Microsoft.Extensions.Logging.ILogger "

Я попробовал решения здесь, но это не сработало для меня.

Редактировать На основе комментария Яакова ниже и этого комментария на Github, я могу решить это правильно, сделав это

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

Я бы предпочел иметь это в начальной BuildServiceProvider но похоже, что мне придется повторять это каждый раз, когда я хочу использовать регистратор (или создать свой собственный ILogger).

11 ответов

ILogger больше не регистрируется по умолчанию, но ILogger<T>является. Если вы все еще хотите использовать ILogger, вы можете зарегистрировать его вручную с помощью следующего (Startup.cs):

    public void ConfigureServices(IServiceCollection services)
    {
        var serviceProvider = services.BuildServiceProvider();
        var logger = serviceProvider.GetService<ILogger<AnyClass>>();
        services.AddSingleton(typeof(ILogger), logger);
        ...
     }

Где AnyClass может быть чем-то общим, например:

     public class ApplicationLogs
     {
     }

Так:

        public void ConfigureServices(IServiceCollection services)
        {
            var serviceProvider = services.BuildServiceProvider();
            var logger = serviceProvider.GetService<ILogger<ApplicationLog>>();
            services.AddSingleton(typeof(ILogger), logger);
            ...
         }

ILogger теперь разрешается через инъекцию конструктора

Я предполагаю, что вы используете шаблон по умолчанию для основного веб-приложения.net.
В вашем Startup.cs у вас должен быть такой метод: ↓↓↓↓↓↓↓

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });


        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        //Do the service register here and extra stuff you want
         services.AddLogging(config =>
        {
            config.AddDebug();
            config.AddConsole();
            //etc
        });

    }

Изменить: я написал простую программу для вас, чтобы показать, как она работает

public class MyClass
{

    private readonly ILogger<MyClass> _logger;


    public MyClass(ILogger<MyClass> logger)
    {
        _logger = logger;
    }
    public void MyFunc()
    {
        _logger.Log(LogLevel.Error, "My Message");
    }
}



public class Program
{
    public static void Main(string[] args)
    {
        var services = new ServiceCollection().AddLogging(logging => logging.AddConsole());
        services.AddSingleton<MyClass>();//Singleton or transient?!
        var s = services.BuildServiceProvider();
        var myclass = s.GetService<MyClass>();
     }
}

Редактировать: Вывод программы:

Для .NET Core 3.1 обязательно включите

Microsoft.Extensions.Logging

упаковать и настроить ведение журнала, позвонив

      IServiceCollection services = new ServiceCollection();
services.AddLogging();

Для тех, кто получает эту ошибку с помощью NLog, я обнаружил, что делаю следующую ошибку в своей инъекции зависимостей:

              private readonly ILogger _logger;

        public ErrorController(ILogger ilogger)
        {
            _logger = ilogger;
        }

Согласно документации , в .Net 5 нужно добавить в логгер тип самого Controller, чтобы избежать этой проблемы. Внедрение зависимостей как таковое исправило ошибку:

              private readonly ILogger<ErrorController> _logger;

        public ErrorController(ILogger<ErrorController> ilogger)
        {
            _logger = ilogger;
        }

После внесения вышеуказанного изменения это устранило мою проблему. НЕ ПЫТАЙТЕСЬ ЛЮБЫМ РЕШЕНИЕМ ЗДЕСЬ! Это только отправит вас в кроличью нору.

Надеюсь, это поможет кому-нибудь в будущем!

В.NET Core ILogger<T>автоматически регистрируется для вас. КакILogger<T> наследуется от ILogger, вы можете запросить экземпляр ILogger<T> из IServiceProvider.

Например:services.AddSingleton<ILogger>(svc => svc.GetRequiredService<ILogger<MyClassType>>());

Обратите внимание, что это вернет ILogger<MyClassType> везде, где у вас нет общего ILogger параметр конструктора, поэтому, если вам нужно более одного, рассмотрите возможность создания его специально для этого экземпляра с помощью AddSingleton (или временный / или ограниченный) implementationFactory override.

Как отмечено в комментарии, у принятого ответа есть проблема, заключающаяся в том, что BuildServiceProviderне следует использовать в, поскольку он создает еще одну копию каждого синглтона (фактически, всего контейнера DI). Вот альтернативный однострочный вариант, который позволяет избежать этой проблемы. В ConfigureServicesдобавьте строку

      services.AddSingleton<Microsoft.Extensions.Logging.ILogger>(provider => 
   provider.GetRequiredService<Microsoft.Extensions.Logging.ILogger<AnyClass>>());

где может быть любой класс, как описано в принятом ответе. Это перенаправляет разрешение ILogger к тому из ILogger<AnyClass>.

Как и в случае с принятым ответом, у этого есть тот недостаток, что у Serilog SourceContextбудет установлен на. Итак, если ваша конфигурация Serilog содержит outputTemplate это содержит {SourceContext}, в вашем журнале будет отображаться AnyClass вместо класса, содержащего оператор ведения журнала.

Этот ответ не обязательно для.net Core или какой-либо другой инъекции зависимостей, используемой плакатом. Я наткнулся на этот поиск способа исправить эту ошибку с помощью Autofac в веб-приложении форм asp.net. Ответ о том, что ведение журнала "не регистрируется по умолчанию", заставил меня добавить это в мои регистрации:

builder.RegisterType<LoggerFactory>().As<ILoggerFactory>().SingleInstance();
builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance();

И теперь у меня работает. Это второй раз, когда я искал этот ответ и оказался здесь, я забыл, как я его исправил в первый раз. Итак, я подумал, что отправлю свой ответ.

Принятый ответ (предоставленный @sam-shiles) идет в правильном направлении. Мне не нравится, что для этого требуется создание промежуточного поставщика услуг, и регистрация экземпляра синглтона, возможно, также нежелательна.

Итак, я предлагаю вместо этого зарегистрировать фабрику, которая использует ILoggerFactoryвместо. Создание пустого класса (например, AnyClassтоже не нужен).

      ...
services.AddTransient(provider =>
{
    var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
    const string categoryName = "Any";
    return loggerFactory.CreateLogger(categoryName);
});
...

На основе @ Barry Franklin ответа выше выяснил , как зарегистрировать родовой поставщик журналу для Microsoft.Extensions.DependencyInjection:

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

services.AddSingleton<ILoggerFactory, LoggerFactory>()
services.Add(ServiceDescriptor.Describe(typeof(ILogger<>),typeof(Logger<>),ServiceLifetime.Scoped))

Подход, который я использовал при попытке использовать ILogger (Поскольку он больше не вводится), как вводить ILoggerFactory, а затем, когда вашему коду требуется конкретный регистратор (скажем, это какая-то настраиваемая фабрика, вы newвстать, LoggerFactory.CreateLogger("Logger Name") - Ниже

Но в вашем случае, похоже, вам нужен ILogger<MyClass>

public class MyClass
{

    private readonly ILoggerFactory _loggerFactory;


    public MyClass(ILoggerFactory loggerFactory)
    {
        _loggerFactory = _loggerFactory;
    }
    public void MyFunc()
    {
       new MyNewThatsASpecialCase(_loggerFactory.CreateLogger("MyNewThatsASpecialCase"));
    }
}

Это то, что помогло мне заставить DI работать на ILogger.

Для .NET Core см. Https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0.

Конкретно:

      .ConfigureLogging(logging =>
            {
                logging.ClearProviders();
                logging.AddConsole();
            })

Справка:

      public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
    .ConfigureLogging(logging =>
    {
        logging.ClearProviders();
        logging.AddConsole();
    })
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });
Другие вопросы по тегам