Streaming Blazor (ASP.Net Core) Logs to the Browser with a custom NLog Target and SignalR

Stewart Celani
5 min readApr 10, 2022

While developing in PHP and Node/React in the past I often dreamily wished I was able to see the servers log output in the browser or JavaScript console.

As a newcomer to C#, Blazor and ASP.NET Core I have set myself the goal of familiarising myself with the stack by building a small prototype/feature each week and formalising that knowledge by doing a quick write up in a blog.

Yesterday I was hooking up NLog in Blazor and wondering what I should build for my first post and the idea to see if I could make good on my dream of getting server log output into the browser came back to me.

As the wise philosopher Mr. LaBeouf once said…

“JUST DO IT”

We will walk through one approach on how to achieve this. It is a Blazor WebAssembly & Blazor Server compatible approach using a custom NLog target and SignalR hub.

We will cover a v1 prototype version which streams log output from the moment a client connects with no guarantee of delivery. If the browser connection drops and reconnects minutes later, all the log output that occurred on the server during that time will not be sent to the browser.

Adding a method to guarantee delivery and have the client & server resync data since the connection was lost would be something to add to a v2. I’ll likely cover this problem in a future post on building a SignalR chat app.

There is also an easier way to do this which is a Blazor Server only approach and utilises the existing SignalR connection established and managed by Blazor Server. I can do a post on this in the future if there is any interest.

In a real project you would implement your own authentication and authorization on the SignalR connections/hub, but, as this is a demo…

I’m walking through how I, personally, approached this problem. I can guarantee that there are better and more performant ways to achieve the same result. The reason I am writing this is to improve as a developer and I apologize in advance for any coding sins you are about to witness.

To get started we will create a new ASP.NET Core hosted Blazor WASM application.

dotnet new blazorwasm --hosted -o WasmLogToBrowser

Now add the packages we need and start the server:

cd .\WasmLogToBrowser\Client
dotnet add package Microsoft.AspNetCore.SignalR.Client
cd ..\Server
dotnet add package NLog
dotnet add package NLog.Web.AspNetCore
dotnet add package Microsoft.AspNetCore.SignalR.Client

Step 1: Hook Up NLog

Create Server\Logging\Logger.cs and then update Server\Program.cs:

We now have some simulated log output we can worry about getting into the browser:

Step 2: Create SignalR Hub

Create Server\Hubs\LoggingHub.cs and hook it up in Server\Program.cs:

Step 3: Create and connect a custom NLog Target to the LoggingHub

In order for our custom NLog target to log to the LoggingHub we will need to connect ourselves so we will create both a Server\Logging\LoggingHubTarget.cs and a connection class at Server\Logging\LoggingHubConnection.cs the target can use to establish the connection when the target is instantiated. Finally, we will add the target to Server\Logging\Logger.cs::Configure().

When we start the server now we should see a connection from the LoggingHubTarget come through in OnConnectedAsync:

The server is now broadcasting all NLog messages to clients connected to the SignalR hub at https://locahost:XXXX/hubs/logging.

Step 4: ServerConsole.razor, DebugService and hooking them up to the index page

First we need to create and hook up our Client\Services\DebugService.cs which is going to handle storing the log messages and (in the next step) toggling the console connection on/off.

Razor components will be able to inject DebugService to get access to the log messages and subscribe to the OnChange event within the DebugService to have Blazor re-render.

I’m a big fan of this approach to state management compared to React + Redux with the boilerplate and having to pass the entire state down the component tree.

Now that is out of the way we can implement our Client\Shared\ServerConsole.razor component that we will mount in Client\Shared\MainLayout.razor if DebugService.ConnectToServerConsole is true.

Finally, some styling tweaks in Client\wwwroot\css\app.css and then we can display the DebugService LogMessages list on the Client\Pages\Index.razor page:

Now when we run our app we get server console output!

This is what dreams are made of

Step 5: Make the console toggle-able and implement clearing the console

For the toggle we will clean up Client\Pages\Index.razor and implement the toggle in Client\Services\DebugService.cs. We will also set ConnectToServerConsole to false by default so that connecting to the server console is a manual thing like one might do in a debugging scenario.

Toggle and clear in action:

Note the ServerConsole.razor component unmounting/remounting in bottom right on toggle

Step 6: Simultaneously output logs to the JavaScript console

It’s nice to be able to access the logs from anywhere in our application but, for practical purposes, we would likely open and detach a devtools console.

Lets create a console logging helper class at Client\Library\JsConsole.cs, implement the functionality in Client\Services\DebugService.cs and hook the JsConsole up in Client\Program.cs.

We also need to change the OnReceiveLogMessage void in Client\Shared\ServerConsole.razor to an async Task now that AddLogMessage in the DebugService is async.

Now we have output in the console which we can view regardless of which page we’re on in the app:

And there you have it. That is one way to get server logs into the browser.

This is much more useful for Blazor Server projects as everything is happening on the server already. I can see this being very useful while an app is in development but you’d either want to make sure your auth on the LoggingHub is solid or else you’d want to remove the feature completely for production.

--

--