Skip to content

Instantly share code, notes, and snippets.

@crystalgreen
Last active October 20, 2023 01:38
Show Gist options
  • Select an option

  • Save crystalgreen/568b5815d4a3efded5e2530145d3d538 to your computer and use it in GitHub Desktop.

Select an option

Save crystalgreen/568b5815d4a3efded5e2530145d3d538 to your computer and use it in GitHub Desktop.
Global Error Handler for .NET core generic host
// 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