Проблема в том, что LoggerProvider
создается до регистрации SignalR
концентраторов. Поэтому, когда поставщик регистратора создан, IServiceProvider
не был инициализирован, чтобы знать о каких-либо IHubContext<T>
объектах.
Единственный способ, который я могу найти, - это предоставить IServiceProvider
вашему * 1008. * и пусть он получает экземпляр IHubContext
, когда ему это нужно.
Ваш класс регистратора должен принять IServiceProvider
в своем конструкторе, а затем использовать этот объект для получения IHubContext
по требованию:
public class SignalRLogger : ILogger
{
private readonly IServiceProvider _sp;
public SignalRLogger(IServiceProvider sp)
{
_sp = sp;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var hub = _sp.GetService<IHubContext<LogHub>>();
if (hub != null)
{
var msg = $"[{logLevel}] {formatter(state, exception)}";
hub.Clients.All.SendAsync("ReceiveMessage", msg);
}
}
}
Мы должны проверить, что IHubContext
был получен от поставщика услуг, поскольку регистратор может быть вызван до того, как будет зарегистрирован концентратор. И это является одним из недостатков использования SignalR в качестве регистратора, потому что вы пропустите некоторые ранние сообщения, прежде чем можно будет получить доступ к HubContext
(но это может быть приемлемо для вас).
ПРИМЕЧАНИЕ. Это можно улучшить хранить хаб вместо того, чтобы получать его при каждом вызове журнала - но я оставлю это вам для реализации: -)
Теперь вам нужно изменить провайдера для предоставления этого параметра при создании регистратора:
public class SignalrRLoggerProvider : ILoggerProvider
{
private SignalRLogger _logger;
private readonly IServiceProvider _sp;
public SignalrRLoggerProvider(IServiceProvider sp)
{
_sp = sp;
}
public ILogger CreateLogger(string categoryName)
{
if (_logger == null)
{
_logger = new SignalRLogger(_sp);
}
return _logger;
}
}
Наконец, вам нужно предоставить IServiceProvider
поставщику регистратора:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(builder =>
{
var sp = builder.Services.BuildServiceProvider();
builder.AddProvider(new SignalrRLoggerProvider(sp));
});
Нам нужно вызвать метод BuildServiceProvider
, чтобы создать нужный нам интерфейс, поскольку он недоступен. в ConfigureLogging
вызове - но это даст нам правильный интерфейс для использования.