Skip to content

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

Merged
merged 13 commits into from
Mar 13, 2020

Conversation

SteveSandersonMS
Copy link
Member

... and make WebAssemblyConsoleLogger use the browser's console.* APIs properly, e.g., so LogLevel.Error translates into a call to console.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.

@SteveSandersonMS SteveSandersonMS added area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly labels Mar 5, 2020

module.printErr = line => {
console.error(`WASM: ${line}`);
console.error(line);
Copy link
Member Author

@SteveSandersonMS SteveSandersonMS Mar 5, 2020

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);
}
Copy link
Member

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?

Copy link
Member

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

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)

Comment on lines +47 to +56
// 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);
});

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member Author

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");
Copy link
Member

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?

Copy link
Member Author

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
Copy link
Member

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

Copy link
Member

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)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #19737

Copy link
Member

@javiercn javiercn left a 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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/logging-unhandled-exceptions branch from 43a6530 to 0ada0f5 Compare March 10, 2020 09:49
@SteveSandersonMS
Copy link
Member Author

Although this was already approved, it's now been changed quite a lot since then so I want to give @javiercn and @pranavkm a chance to re-review before merging.

It's missed the boat for preview 2 so we should consider this a preview 3 thing now.

Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great addition!

@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/logging-unhandled-exceptions branch from 2b869b3 to 2f86865 Compare March 12, 2020 18:11
@SteveSandersonMS SteveSandersonMS force-pushed the stevesa/logging-unhandled-exceptions branch from 2f86865 to 288783b Compare March 13, 2020 09:31
@SteveSandersonMS SteveSandersonMS merged commit 9343d2b into blazor-wasm Mar 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants