簡體   English   中英

如何實現 ILogger 將消息發送到 SignalR 集線器?

[英]How to implement an ILogger to send messages to a SignalR Hub?

我想構建一個顯示最新日志消息的 LogView。 所以我建立了一個非常簡單的設置,但我在依賴注入失敗了。

這是我對實現的嘗試。 我跳過了非關鍵部分。

public class SignalRLogger : ILogger
{
    private readonly IHubContext<LogHub> _hub;

    public SignalRLogger(IHubContext<LogHub> hub)
    {
        _hub = hub;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        var msg = $"[{logLevel}] {formatter(state, exception)}";

        _hub.Clients.All.SendAsync("ReceiveMessage", msg);
    }
}

public class SignalrRLoggerProvider : ILoggerProvider
{
    private SignalRLogger _logger;

    public void Dispose()
    {
        _logger = null;
    }

    public ILogger CreateLogger(string categoryName)
    {
        if (_logger == null)
        {
            _logger = new SignalRLogger(????);
        }

        return _logger;
    }
}

我的問題基本上是我無法注入IHubContext ,我不知道如何解決這個問題

問題是LoggerProvider是在注冊SignalR集線器之前創建的。 因此,在創建記錄器提供程序時,尚未初始化IServiceProvider以了解任何IHubContext<T>對象。

我能找到解決它的唯一方法是將IServiceProvider提供給您的ILogger並讓它在需要時獲取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調用中不可用 - 但它會給我們正確的接口來使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM