Is there a way to stream blazor logs to the client and display them in real time on a web page?

Viewed 1051

I am trying to figure out how to configure Blazor and Serilog to be able to render a component that will display the log output on a razor page. I have searched extensively for an example specifically using Blazor/Asp.Net Core and have come up short.

If I have the following in my Startup:

   myServiceCollection.AddLogging(l =>
    {
        l.ClearProviders();
        l.AddConsole();
        l.SetMinimumLevel(LogLevel.Debug);
    });

And then in some other component I inject an ILogger how would I, instead of logging to the console, instead redirect the logs to some <textarea> or <p> that I have on the razor component in question? I basically just want to set up a log viewer page or be able to display the output of the logs on any arbitrary component.

While I have not found a specific example doing exactly what I am asking, there are an abundance of examples of Blazor chat apps using SignalR to do real-time chatting between users. My thought was that I could repurpose that and instead of having a chat app between users, I am just "chatting" between the server and the client where the messages are the outputted log lines. Would this be the best approach?

1 Answers

You could create a custom logger. I managed to get this to work with this tutorial chat-app

Startup.cs

loggerFactory.AddProvider(
    new SignalRLoggerProvider(
        new SignalRLoggerConfiguration
        {
            HubContext = serviceProvider.GetService<IHubContext<BlazorChatSampleHub>>(),
            LogLevel = LogLevel.Information
        }));


public class SignalRLoggerProvider : ILoggerProvider
{
    private readonly SignalRLoggerConfiguration _config;
    private readonly ConcurrentDictionary<string, SignalRLogger> _loggers = new ConcurrentDictionary<string, SignalRLogger>();

    public SignalRLoggerProvider(SignalRLoggerConfiguration config)
    {
        _config = config;
    }

    public ILogger CreateLogger(string categoryName)
    {
        return _loggers.GetOrAdd(categoryName, name => new SignalRLogger(name, _config));
    }

    public void Dispose()
    {
        _loggers.Clear();
    }
}

public class SignalRLogger : ILogger
{
    private readonly string _name;
    private readonly SignalRLoggerConfiguration _config;
    public SignalRLogger(string name, SignalRLoggerConfiguration config)
    {
        _name = name;
        _config = config;
    }

    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel == _config.LogLevel;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
                        Exception exception, Func<TState, Exception, string> formatter)
    {
        if (!IsEnabled(logLevel))
        {
            return;
        }

        if (_config.EventId == 0 || _config.EventId == eventId.Id)
        {
            this._config.HubContext?.Clients.Group(_config.GroupName).SendAsync("Broadcast", "LOGGER", $"{DateTimeOffset.UtcNow:T}-UTC : {formatter(state, exception)}");
        }
    }
}

Proof of concept

Related