1

I have a .NET 6 BackgroundService which I am trying to deploy on an Azure Container Instance. The instance crashes as soon as it is started, but no logs are dumped in my Serilog's Seq sink. I know I have to implement the crash logging on the program's startup, and with the help of a previously asked excellent question, I have done that, which works partially. The relevant snippet from my startup class is below:

public sealed class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    private static IHostBuilder CreateHostBuilder(string[] args)
    {
        var environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");

        var configurationBuilder = new ConfigurationBuilder();
        var configuration = configurationBuilder.Build();

        using (var logger = new LoggerConfiguration()
                            .Enrich.WithProperty("Application", configuration.GetValue<string>("ApplicationName"))
                            .Enrich.WithProperty("ApplicationHost", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)
                            .Enrich.WithProperty("Environment", environmentName)
                            .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
                                .WithDefaultDestructurers()
                                .WithDestructurers(new[] { new DbUpdateExceptionDestructurer() }))
                            .ReadFrom.Configuration(configuration)
                            .CreateLogger())
        {
            logger.Information("************ STARTING MY WORKER ************"); // this is present in the logs

            try
            {
                return Host.CreateDefaultBuilder(args)
                        .ConfigureServices((ctx, services) =>
                        {
                            services.AddSingleton(Log.Logger);

                            services.AddHostedService<MyWorker>();
                            //services.AddSingleton<CustomClass>(); // <-- uncommenting this is the ACTUAL fix for my crash
                        })
                        .ConfigureLogging((ctx, loggingBuilder) =>
                        {
                            loggingBuilder.AddSerilog(logger, dispose: true);
                        });
            }
            catch (Exception ex)
            {
                Log.Logger.Error("------ Log.Logger method ------");
                logger.Fatal(ex, ex.Message);
                throw;
            }
        }
    }
}

Only the 1st startup message i.e. "STARTING MY WORKER" is printed in my Seq sink, but no log statements from my catch handler are printed there. What am I missing?

user1451111
  • 1,735
  • 3
  • 18
  • 30
  • 1
    I guess, there is no exception in `Host.CreateDefaultBuilder(args)`. But then builder is used to build a host, host runs, and crashes. You need to wrap `CreateHostBuilder(args).Build().Run();` into `try-catch` as well. – Vlad DX Feb 23 '23 at 00:43
  • @VladDX will the logger be accessible and work the same way in `Main()` method? – user1451111 Feb 23 '23 at 00:55
  • To troubleshoot Serilog, add this line as the first line in your Main function to have SeriLog log any of its errors to the console window.... `Serilog.Debugging.SelfLog.Enable(msg => Console.WriteLine(msg));` – quaabaam Feb 23 '23 at 01:23

2 Answers2

3

As @Vlad DX noted most probably your exception is happening not when building the host.

Add unhandled exception handler for your app domain to log exceptions which will happen further down the line (I use static Serilog Logger in this example) :

AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) =>
{
    Log.Logger.Error(e.ExceptionObject as Exception, "UnhandledException");
};
Michael
  • 1,027
  • 10
  • 22
0

As suggested by Vlad in the very 1st comment, I had to surround the CreateHostBuilder(args).Build().Run(); into a try-catch block. I changed my initial code (the same which is posted in my question), into the following and now the startup exceptions are logged in Serilog's Seq sink.

public sealed class Program
{
    public static void Main(string[] args)
    {
        var environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
        var configurationBuilder = new ConfigurationBuilder();
        var configuration = configurationBuilder.Build();

        using (var logger = new LoggerConfiguration()
                            .Enrich.WithProperty("Application", configuration.GetValue<string>("ApplicationName"))
                            .Enrich.WithProperty("ApplicationHost", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)
                            .Enrich.WithProperty("Environment", environmentName)
                            .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
                                .WithDefaultDestructurers()
                                .WithDestructurers(new[] { new DbUpdateExceptionDestructurer() }))
                            .ReadFrom.Configuration(configuration)
                            .CreateLogger())
        {
            logger.Information("************** STARTING MY WORKER **************");

            try
            {
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                logger.Fatal(ex, ex.Message);
            }
        }
    }

    private static IHostBuilder CreateHostBuilder(string[] args)
    {
        var environmentName = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");

        var configurationBuilder = new ConfigurationBuilder();
        var configuration = configurationBuilder.Build();

        using (var logger = new LoggerConfiguration()
                            .Enrich.WithProperty("Application", configuration.GetValue<string>("ApplicationName"))
                            .Enrich.WithProperty("ApplicationHost", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name)
                            .Enrich.WithProperty("Environment", environmentName)
                            .Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
                                .WithDefaultDestructurers()
                                .WithDestructurers(new[] { new DbUpdateExceptionDestructurer() }))
                            .ReadFrom.Configuration(configuration)
                            .CreateLogger())
        {
            return Host.CreateDefaultBuilder(args)
                        .ConfigureServices((ctx, services) =>
                        {
                            services.AddSingleton(Log.Logger);

                            services.AddHostedService<MyWorker>();
                            //services.AddSingleton<CustomClass>(); // <-- uncommenting this is the ACTUAL fix for my crash
                        })
                        .ConfigureLogging((ctx, loggingBuilder) =>
                        {
                            loggingBuilder.AddSerilog(logger, dispose: true);
                        });
        }
    }
}

The throw statement is not needed. Also, I am aware that there is some code duplication for configuration buiding and also in both using blocks but that is irrelevant to this question.

user1451111
  • 1,735
  • 3
  • 18
  • 30