Blazor HotReload throws `System.NullReferenceException` when using Serilog
See original GitHub issueIs there an existing issue for this?
- I have searched the existing issues
Describe the bug
When using the serilog browser-console sink, the application throws System.NullReferenceException
. The author of the library already tried to fix it but it looks like it’s a problem with Blazor itself.
Here is a reference to the issue in the browser-console sink repository https://github.com/serilog/serilog-sinks-browserconsole/issues/20
Expected Behavior
We should be able to add a log and start the application without getting a null reference exception.
Steps To Reproduce
- Create a new Blazor project.
- Add serilog browser-console sink package.
- Update the entry point so it looks like this:
// This is the line that makes Blazor throw the exception
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.BrowserConsole(
restrictedToMinimumLevel: LogEventLevel.Information,
outputTemplate: "[{Level:u3}] {Message:lj}{NewLine}{Exception}",
CultureInfo.InvariantCulture)
.CreateLogger();
try
{
WebAssemblyHostBuilder builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>(selector: "#app");
builder.RootComponents.Add<HeadOutlet>(selector: "head::after");
builder.Logging.AddSerilog();
builder.Services.AddServices();
WebAssemblyHost app = builder.Build();
await app.RunAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, messageTemplate: "An exception occurred while creating the WASM host");
throw;
}
Exceptions (if any)
[ FTL ] An exception occurred while creating the WASM host System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.InitializeAsync() at Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost.RunAsyncCore(CancellationToken cancellationToken, WebAssemblyCultureProvider cultureProvider) at Program.<Main>$(String[] args) in D:\workspace\projects\Hephaestus\src\presentation\Hephaestus.WebApp\Program.cs:line 43 (anonymous) @ blazor.webassembly.js:1 beginInvokeJSFromDotNet @ blazor.webassembly.js:1 Gt @ blazor.webassembly.js:1 Ii @ dotnet.7.0.0.k4xetpuqcq.js:5 _mono_wasm_invoke_js_blazor @ dotnet.7.0.0.k4xetpuqcq.js:14 $func313 @ 009924a6:0x1d4a3 $func283 @ 009924a6:0x1c8d1 $func221 @ 009924a6:0xdfdd $func220 @ 009924a6:0xce8f $func8112 @ 009924a6:0x1a1fcc $func2053 @ 009924a6:0x859fe $func2058 @ 009924a6:0x86066 $func2085 @ 009924a6:0x88125 $mono_wasm_invoke_method_ref @ 009924a6:0x9bcb Module._mono_wasm_invoke_method_ref @ dotnet.7.0.0.k4xetpuqcq.js:14 _Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_EndInvokeJS @ _Microsoft_AspNetCore_Components_WebAssembly__Microsoft_AspNetCore_Components_WebAssembly_Services_DefaultWebAssemblyJSRuntime_EndInvokeJS:26 endInvokeJSFromDotNet @ blazor.webassembly.js:1 (anonymous) @ blazor.webassembly.js:1 Promise.then (async) beginInvokeJSFromDotNet @ blazor.webassembly.js:1 Gt @ blazor.webassembly.js:1 Ii @ dotnet.7.0.0.k4xetpuqcq.js:5 _mono_wasm_invoke_js_blazor @ dotnet.7.0.0.k4xetpuqcq.js:14 $func313 @ 009924a6:0x1d4a3 $func283 @ 009924a6:0x1c8d1 $func221 @ 009924a6:0xdfdd $func220 @ 009924a6:0xce8f $func8112 @ 009924a6:0x1a1fcc $func2053 @ 009924a6:0x859fe $func2051 @ 009924a6:0x85974 $func1394 @ 009924a6:0x68446 $func313 @ 009924a6:0x1d45c $func283 @ 009924a6:0x1c8d1 $func221 @ 009924a6:0xdfdd $func220 @ 009924a6:0xce8f $func8112 @ 009924a6:0x1a1fcc $func2053 @ 009924a6:0x859fe $func2058 @ 009924a6:0x86066 $func2085 @ 009924a6:0x88125 $mono_wasm_invoke_method_ref @ 009924a6:0x9bcb Module._mono_wasm_invoke_method_ref @ dotnet.7.0.0.k4xetpuqcq.js:14 _Hephaestus_WebApp__entrypoint @ _Hephaestus_WebApp__entrypoint:26 (anonymous) @ dotnet.7.0.0.k4xetpuqcq.js:5 ji @ dotnet.7.0.0.k4xetpuqcq.js:5 callEntryPoint @ blazor.webassembly.js:1 Vt @ blazor.webassembly.js:1 await in Vt (async) (anonymous) @ blazor.webassembly.js:1 (anonymous) @ blazor.webassembly.js:1 blazor.webassembly.js:1
System.AggregateException: One or more errors occurred. (Object reference not set to an instance of an object.)
—> System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.InitializeAsync() at Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyHost.RunAsyncCore(CancellationToken cancellationToken, WebAssemblyCultureProvider cultureProvider) at Program.<Main>$(String[] args) in D:\workspace\projects\Hephaestus\src\presentation\Hephaestus.WebApp\Program.cs:line 48 — End of inner exception stack trace —
.NET Version
net7.0
Anything else?
No response
Issue Analytics
- State:
- Created 9 months ago
- Comments:7 (4 by maintainers)
The workout around works like a charm, thank you for the investigation @MackinnonBuck! I also added an update in the original issue in the browser console repo. I’m not sure if you are going to track the next steps in the same ticket, at least from my side the issue is resolved and can be closed.
Summary
The root of the problem is that
Serilog.Sinks.BrowserConsole
creates and uses its ownIJSRuntime
instance (from its own implementation) when one isn’t provided to LoggerConfigurationBrowserConsoleExtensions.BrowserConsole().Details
Since the
IJSRuntime
instance created by Serilog is different from theIJSRuntime
used by Blazor, it will have its own state tracking pending JS invocations,DotNetObjectReference
instances, etc.The
JSRuntime
base implementation tracks pending JS invocations using along
ID that increments for each JS interop call. In this particular case, the invocation IDs between the twoJSRuntime
instances conflict and cause the crash:JSRuntime
, Serilog kicks off its first two interop calls toconsole.info
with IDs 2 and 3 respectively.JSRuntime
instance to asynchronously invoke a dynamicimport
for theblazor-hotreload.js
module, intending to store the result as anIJSObjectReference
. This invocation has an ID of 2.import()
call. The return value can’t be deserialized into anIJSObjectReference
, so it becomesnull
on the .NET side, causing theNullReferenceException
.Workaround
This can be worked around by passing the
IJSRuntime
fromapp.Services
into the call toLoggerConfigurationBrowserConsoleExtensions.BrowserConsole()
.Next steps
Ultimately, the specific issue at hand is a Serilog bug. From Blazor’s perspective, all bets are off as soon as a library interferes with its internal state in a manner other than through the public API. That said, we could look into adding extra checks to catch this case rather than failing silently.