Last active
October 20, 2023 01:38
-
-
Save crystalgreen/568b5815d4a3efded5e2530145d3d538 to your computer and use it in GitHub Desktop.
Global Error Handler for .NET core generic host
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This demonstrates how to implement a global error handler without using try/catch in Main(). | |
| // Usage: Host.CreateDefaultBuilder().AddGlobalErrorHandler().Build(); | |
| // See also Generic Host doc: https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host | |
| using Microsoft.Extensions.Configuration; | |
| using Microsoft.Extensions.DependencyInjection; | |
| using Microsoft.Extensions.Hosting; | |
| using Microsoft.Extensions.Logging; | |
| using NLog; | |
| namespace Microsoft.Extensions.DependencyInjection | |
| { | |
| public static class HostBuilderExtensions | |
| { | |
| public static IHostBuilder AddGlobalErrorHandler(this IHostBuilder host) | |
| { | |
| // This exception handler prevents the app to stop with a huge exception stack trace. | |
| // All details are logged (if logging works) and by default console log does not show stack traces. | |
| return new ErrorHandlingHostBuilder(host); | |
| } | |
| class ErrorHandlingHostBuilder : IHostBuilder | |
| { | |
| private readonly IHostBuilder Inner; | |
| public ErrorHandlingHostBuilder(IHostBuilder inner) | |
| { | |
| this.Inner = inner; | |
| } | |
| public IDictionary<object, object> Properties => Inner.Properties; | |
| public IHost Build() | |
| { | |
| try | |
| { | |
| return new ErrorHandlingHost(Inner.Build()); | |
| } | |
| catch (Exception ex) | |
| { | |
| Logger.Fatal(ex, "Fatal error during host build phase."); | |
| throw; | |
| } | |
| } | |
| public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate) | |
| { | |
| Inner.ConfigureAppConfiguration(configureDelegate); | |
| return this; | |
| } | |
| public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate) | |
| { | |
| Inner.ConfigureContainer(configureDelegate); | |
| return this; | |
| } | |
| public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate) | |
| { | |
| Inner.ConfigureHostConfiguration(configureDelegate); | |
| return this; | |
| } | |
| public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate) | |
| { | |
| Inner.ConfigureServices(configureDelegate); | |
| return this; | |
| } | |
| public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) | |
| { | |
| Inner.UseServiceProviderFactory(factory); | |
| return this; | |
| } | |
| public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory) | |
| { | |
| Inner.UseServiceProviderFactory(factory); | |
| return this; | |
| } | |
| } | |
| class ErrorHandlingHost : IHost | |
| { | |
| private readonly IHost Inner; | |
| private bool HasError = false; | |
| public ErrorHandlingHost(IHost inner) | |
| { | |
| this.Inner = inner; | |
| } | |
| public IServiceProvider Services => Inner.Services; | |
| public void Dispose() | |
| { | |
| Inner?.Dispose(); | |
| } | |
| public async Task StartAsync(CancellationToken cancellationToken = default) | |
| { | |
| var lifetime = Inner.Services.GetService<IHostApplicationLifetime>(); | |
| try | |
| { | |
| await Inner.StartAsync(cancellationToken); | |
| } | |
| catch (Exception ex) | |
| { | |
| Logger.Fatal(ex, "Fatal error."); | |
| HasError = true; | |
| // Try smooth shutdown, calls StopAsync. If you remove this line, the host will not stop. | |
| lifetime.StopApplication(); | |
| } | |
| } | |
| public async Task StopAsync(CancellationToken cancellationToken = default) | |
| { | |
| try | |
| { | |
| await Inner.StopAsync(cancellationToken); | |
| if (HasError) | |
| Environment.ExitCode = 1; | |
| } | |
| catch (Exception ex) | |
| { | |
| Logger.Fatal(ex, "Fatal error during shutdown."); | |
| } | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment