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

Are there any plans to support Blazor WASM projects? #79

Open
uhfath opened this issue May 13, 2024 · 8 comments
Open

Are there any plans to support Blazor WASM projects? #79

uhfath opened this issue May 13, 2024 · 8 comments

Comments

@uhfath
Copy link

uhfath commented May 13, 2024

In it's current state (4.1.0) there is an exception inside Dispose(bool disposing) method:

protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
SignalShutdown();
try
{
_runLoop.Wait();
}
catch (Exception ex)
{
// E.g. the task was canceled before ever being run, or internally failed and threw
// an unexpected exception.
WriteToSelfLog("caught exception during disposal", ex);
}
if (ReferenceEquals(_targetSink, this))
{
// The sink is being used in the obsolete inheritance-based mode.
return;
}
(_targetSink as IDisposable)?.Dispose();
}

This line:

Causes this:

PeriodicBatchingSink (Serilog.Sinks.Seq.Batched.BatchedSeqSink): caught exception during disposal
System.PlatformNotSupportedException: Cannot wait on monitors on this runtime.

This exception is output in Serilog's self log (when enabled).
In our project we wanted to use serilog-sinks-seq or serilog-sinks-http sinks to send logs to Seq server directly from browser, but they both depend on this project and both fail on this.

Just wanted to know if there are any plans to somehow workaround WASM limitations?

@nblumhardt
Copy link
Member

Hello! In your Blazor component, are you able to use await DisposeAsync() instead of Dispose() on your Logger, service collection, or host (whatever is responsible for shutting down Serilog)?

If you're using Log.CloseAndFlush(), does await Log.CloseAndFlushAsync() resolve it?

Thanks!

@uhfath
Copy link
Author

uhfath commented May 14, 2024

@nblumhardt actually we are using MEL and Serilog is configured like this:

public static IServiceCollection AddConfigurableSerilog(this IServiceCollection services, IConfiguration configuration) =>
    services
        .AddSerilog(new LoggerConfiguration()
            .ReadFrom.Configuration(configuration)
            .CreateLogger(),
        true)
    ;

So we do not use Serilog's shared logger or create it's ILogger instances manually.
We use MEL's ILogger<T>.

And there are some third party components which are not always async disposable so we have no control here either.

@nblumhardt
Copy link
Member

Where are you tearing down the service collection, though? It should be possible to async-dispose it, I believe this will use the non-async dispose for components that don't support IAsyncDisposable.

Logging to Seq (and most other network targets) is asynchronous for performance reasons; if you want to flush the background buffer on disposal then a network call is required, and in the browser these generally need to be async.

Definitely open to (and keen to) improve the default experience here, but I suspect there may not be many options available 🤔

@uhfath
Copy link
Author

uhfath commented May 15, 2024

It's not just the components that use logging but other services too. And not all of them are IAsyncDisposable.
Do you think if we could simply async/void in this case? With proper try/catch for logging exceptions, of course.

@nblumhardt
Copy link
Member

Using DisposeAsync() on your service collection (or await app.RunAsync() in your Program.cs, IIRC) should still call Dispose() on services that don't support IAsyncDisposable. If you try it and it doesn't work, letting me know what breaks should make it easier to pinpoint the problem/a solution. HTH!

@uhfath
Copy link
Author

uhfath commented May 15, 2024

We are using app.RunAsync() and more then that we dispose WebAssemblyHost manually through await host.DisposeAsync() but that doesn't make any difference since exceptions are thrown not at the end of the app lifecycle but on almost after every event logged.
Sorry for not making it clearer in the first post.

@nblumhardt
Copy link
Member

Ah, that's interesting. Are you somehow creating multiple instances of PeriodicBatchingSink? The sink shouldn't be being disposed after every logged event, this normally only happens when the root Logger is shut down. Could some other piece of code be somehow prematurely disposing the logger? (Accidental "transient" registration of the logger in an IoC container, etc. etc.?)

Providing the full call stack for the associated exception would narrow this down.

@uhfath
Copy link
Author

uhfath commented May 18, 2024

Good catch. After your comment I did some digging and found there were some pieces of code that created an ILoggerFactory, an ILogger<T> from it, output some logs and then disposed the former. After removing this code there are no exceptions.
So the question is how to properly create and dispose these? Interfaces do not implement IAsyncDisposable.
As for the call stack, it doesn't really help that much:

PeriodicBatchingSink (Serilog.Sinks.Seq.Batched.BatchedSeqSink): caught exception during disposal
System.PlatformNotSupportedException: Cannot wait on monitors on this runtime.
   at System.Threading.Monitor.ObjWait(Int32 millisecondsTimeout, Object obj)
   at System.Threading.Monitor.Wait(Object obj, Int32 millisecondsTimeout)
   at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.InternalWaitCore(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants