Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blazor HotReload throws System.NullReferenceException when using Serilog #45536

Closed
1 task done
wtorricos opened this issue Dec 10, 2022 · 7 comments
Closed
1 task done
Assignees
Labels
area-blazor Includes: Blazor, Razor Components feature-hot-reload This issue is related to the Hot Reload feaature ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved

Comments

@wtorricos
Copy link

wtorricos commented Dec 10, 2022

Is 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 serilog/serilog-sinks-browserconsole#20

Expected Behavior

We should be able to add a log and start the application without getting a null reference exception.

Steps To Reproduce

  1. Create a new Blazor project.
  2. Add serilog browser-console sink package.
  3. 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.

$(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.

$(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

@TanayParikh TanayParikh added area-blazor Includes: Blazor, Razor Components feature-hot-reload This issue is related to the Hot Reload feaature labels Dec 12, 2022
@MackinnonBuck
Copy link
Member

Thanks for contacting us. Could you please provide us with a minimal repro project hosted as public GitHub repository? This will help us investigate what the root cause is. Thanks!

@MackinnonBuck MackinnonBuck added the Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. label Dec 12, 2022
@ghost
Copy link

ghost commented Dec 12, 2022

Hi @tico321. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@wtorricos
Copy link
Author

@MackinnonBuck Thanks for the quick reply. I created this project where you can reproduce the issue https://github.com/tico321/BlazorNullReferenceWithSerilog
I added some comments in the Program.cs class where I Identified some of the problematic lines.

@ghost ghost added Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. and removed Needs: Author Feedback The author of this issue needs to respond in order for us to continue investigating this issue. labels Dec 13, 2022
@mkArtakMSFT mkArtakMSFT added investigate and removed Needs: Attention 👋 This issue needs the attention of a contributor, typically because the OP has provided an update. labels Dec 13, 2022
@mkArtakMSFT
Copy link
Member

@MackinnonBuck can you please spend few hours to investigate this and summarize your findings as a comment? We'll decide when / how to tackle it later.

@MackinnonBuck
Copy link
Member

Summary

The root of the problem is that Serilog.Sinks.BrowserConsole creates and uses its own IJSRuntime instance (from its own implementation) when one isn't provided to LoggerConfigurationBrowserConsoleExtensions.BrowserConsole().

Details

Since the IJSRuntime instance created by Serilog is different from the IJSRuntime 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 a long ID that increments for each JS interop call. In this particular case, the invocation IDs between the two JSRuntime instances conflict and cause the crash:

  1. Using its own JSRuntime, Serilog kicks off its first two interop calls to console.info with IDs 2 and 3 respectively.
  2. Blazor uses its JSRuntime instance to asynchronously invoke a dynamic import for the blazor-hotreload.js module, intending to store the result as an IJSObjectReference. This invocation has an ID of 2.
  3. Serilog's first two JS invocations complete. The ID 2 invocation gets incorrectly interpreted as being the completion of Blazor's import() call. The return value can't be deserialized into an IJSObjectReference, so it becomes null on the .NET side, causing the NullReferenceException.

Workaround

This can be worked around by passing the IJSRuntime from app.Services into the call to LoggerConfigurationBrowserConsoleExtensions.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.

@wtorricos
Copy link
Author

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.

@mkArtakMSFT mkArtakMSFT added the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Dec 15, 2022
@ghost ghost added the Status: Resolved label Dec 15, 2022
@mkArtakMSFT mkArtakMSFT closed this as not planned Won't fix, can't repro, duplicate, stale Dec 15, 2022
@nblumhardt
Copy link

Thanks for checking this out! Just a quick question on:

as soon as a library interferes with its internal state in a manner other than through the public API

Is the Microsoft.JSInterop namespace intended to be internal/undocumented? I don't think we're relying on any nefarious reflection tricks these days :-) so it'd be great to get some indication of which API we should be steering clear of.

@ghost ghost locked as resolved and limited conversation to collaborators Jan 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components feature-hot-reload This issue is related to the Hot Reload feaature ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved
Projects
None yet
Development

No branches or pull requests

5 participants