-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Log unhandled exceptions to custom logger #19606
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
Log unhandled exceptions to custom logger #19606
Conversation
|
||
module.printErr = line => { | ||
console.error(`WASM: ${line}`); | ||
console.error(line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the WASM:
prefix because it serves no purpose. Developers who call Console.WriteLine
should be able to choose exactly what string gets logged, without the framework enforcing an arbitrary prefix.
if (!(exception is null)) | ||
{ | ||
formattedMessage += FormatException(exception); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit weird that the logger does this. Does the ConsoleLogger do the same thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would look at what we do in ASP.NET Core and strive for consistency with what is there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Changing as per #19606 (comment)
// Replace the default logger with a custom one that wraps it | ||
var originalLoggerDescriptor = builder.Services.Single(d => d.ServiceType == typeof(ILoggerFactory)); | ||
builder.Services.AddSingleton<ILoggerFactory>(services => | ||
{ | ||
var originalLogger = (ILoggerFactory)Activator.CreateInstance( | ||
originalLoggerDescriptor.ImplementationType, | ||
new object[] { services }); | ||
return new PrependMessageLoggerFactory("Custom logger", originalLogger); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need APIs to configure this in the WebAssemblyHostbuilder? It seems cumbersome to do at the moment and I can already think of people wanting to configure things like application insights and other telemetry tools here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed offline. Will file an issue to consider this as a future enhancement.
The right thing to do in the longer term will be to replicate all the logic from LoggerFactory
and Logger
in https://github.com/dotnet/extensions/blob/master/src. The reason we're not using that package's implementation directly is because of size concerns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #19737
|
||
// Since this is where generic unhandled errors get routed, we want the category | ||
// name to be something generic, not "WebAssemblyRenderer". | ||
_logger = loggerFactory.CreateLogger(".NET"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that this is a bit too generic .NET
. Maybe we can use something more specific, like Blazor
.
Is the problem that we have multiple logging sources comming from different places? What do we do on server-side Blazor. Can we be consistent with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Blazor Server, we do this:
_loggerFactory.CreateLogger<RemoteRenderer>()); |
It makes a bit more sense for Blazor Server because you potentially have a lot of different things going on within your server-side ASP.NET Core process, so differentiating things from a circuit versus some other form of request handling is useful.
It doesn't make so much sense for Blazor WebAssembly because our programming model doesn't include other ways of running code.
However on balance I'm now siding with your consistency point, and will change this to loggerFactory.CreateLogger<WebAssemblyRenderer>()
. It is a bit weird for that name to be what you have to use to filter these kinds of error messages, but theoretically you might run other code outside the Blazor rendering process, in which case it is a relevant differentiator.
{ | ||
_jsRuntime = (IJSInProcessRuntime)services.GetRequiredService<IJSRuntime>(); | ||
} | ||
|
||
public void AddProvider(ILoggerProvider provider) | ||
{ | ||
// No-op |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that this is not part of this PR, but shouldn't we implement this factory properly?
I believe the existing pattern has extension methods on the factory to add different providers. I'm fine if we file a follow-up issue for this, but I don't want it to get lost in the ether
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably also needs to respect login levels comming from config (as asp.net core does)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #19737
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm signing-off as the changes are an improvement and we don't have to wait for this to be in preview2.
I've left some comments regarding more deep questions. I would ask that you file a bug so that we can discuss them in more depth and address them as part of either preview3 or preview4 work.
{ | ||
Console.Error.WriteLine(exception); | ||
} | ||
_logger.LogCritical(exception, "Unhandled exception"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason this isn't the same pattern as the server: https://github.com/dotnet/aspnetcore/blob/master/src/Components/Server/src/Circuits/RemoteRenderer.cs#L388-L391?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
I'm changing WebAssemblyConsoleLogger
and WebAssemblyLoggerFactory
to be as similar to the server-side implementations as I think makes sense. There are still some deliberate differences:
- In Blazor Server, the severity of an unhandled component rendering exception is "warning", because it's not fatal to the server (only to the circuit). In Blazor WebAssembly, the severity for this is "critical" because it marks the point where the whole environment enters an undefined state and we show the gold "error occurred" bar. It makes sense for these to be different because the logging is done from the point of view of a bigger environment on the server.
- The exact details of message formatting are mostly the same, but not precisely. This is because the browser logger (1) doesn't want to see trailing newlines and (2) has its own built-in behavior around colorization and highlighting different severity levels.
- The WebAssembly implementations still don't support configuring log levels, because (1) the configuration feature isn't available yet, and (2) it's out of scope for this PR, and tracked by Support more logger features #19737. So it's still hardcoded to "warning" level and above.
if (!(exception is null)) | ||
{ | ||
formattedMessage += FormatException(exception); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
43a6530
to
0ada0f5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great addition!
2b869b3
to
2f86865
Compare
…ssemblyConsoleLogger.
… as possible to server-side implementations
2f86865
to
288783b
Compare
... and make
WebAssemblyConsoleLogger
use the browser'sconsole.*
APIs properly, e.g., soLogLevel.Error
translates into a call toconsole.error
(and so on, for other log levels).I said this would be a 20 minute fix. Lols. It took half the day.
I still think this is low-risk enough to put into preview 2. There's not much reason why we'd benefit from keeping it back until preview 3.
The effect of sending unhandled exceptions to the registered
ILoggerFactory
is that developers can now use this as a centralised place for sending unhandled exception info to a backend collection service.