user3230660
user3230660

Reputation:

How to capture and log startup errors

My goal in the code below is to create a root exception handler which will handle all otherwise unhandled errors. When I run my web app under IIS on Azure it immediately crashes. The log file is shown below. When I run the program at the command line I can see the error.

My questions are:

Why is the error not logged?

How can I write the handler such that all errors are logged?

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        string env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        string logRoot = null;

        if (env == "Development")
            logRoot = "c:\\serilog\\myDomain.Web\\log";
        else
            logRoot = "..\\..\\serilog\\myDomain.Web\\log";   // Create logs in D:\home\serilog


        Log.Logger = new LoggerConfiguration()
           .WriteTo.File(logRoot, rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information)
           .CreateLogger();

        try
        {
            Log.Information("myDomain.Web - Program.Main started.");
            Log.Information("Environment is: {env}", env);
            CreateHostBuilder(args).Build().Run();
        }
        catch (Exception ex)
        {
            Log.Error(ex.ToString());
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

Logfile

2021-01-07 15:58:44.833 +00:00 [INF] myDomain.Web - Program.Main started.

2021-01-07 15:58:45.009 +00:00 [INF] Environment is: null

Error (not logged)

D:\home\site\wwwroot>myDomain.Web.exe crit: Microsoft.AspNetCore.Hosting.Diagnostics[6] Application startup exception System.IO.DirectoryNotFoundException: D:\home\site\wwwroot\StaticHTML
at Microsoft.Extensions.FileProviders.PhysicalFileProvider..ctor(String root, ExclusionFilters filters) at Microsoft.Extensions.FileProviders.PhysicalFileProvider..ctor(String root) at LeaderAnalytics.myDomain.Web.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in D:\a\1\s\myDomain.Web\Startup.cs:line 57 at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)

Edit: Showing that error handler is called

The exception handler shown is the root exception handler. To demonstrate it should be handling the specific exception I cited I replaced my code with a throw statement (because my code works in dev but fails in prod) and observed the handler being called as shown in the image below.

throw new Exception("boo");
app.UseStaticFiles(new StaticFileOptions // this line fails in prod

Exception

Upvotes: 2

Views: 2786

Answers (3)

Nathan Smith
Nathan Smith

Reputation: 1813

If the process response code matters the initial example will return status 0 for success.

From cmd prompt run dotnet run then run echo %errorlevel%

The code below will log and re-throw the exception to maintane the behavior of returning a non 0 errorLevel when the process fails to start.

public static class ConfigureSerilogExtensions
{
    public static void SetupSerilogStartupLogger(Action action)
    {
        var logPath = "App_Data/logs/app.log";
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Information()
            .WriteTo.Console()
            .WriteTo.File(logPath, rollingInterval: RollingInterval.Day)
            .CreateLogger();

        try
        {
            Log.Information("Starting web host");
            action();
            Log.CloseAndFlush();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
            Log.CloseAndFlush();
            throw;
        }
    }
}

Usage

ConfigureSerilogExtensions.SetupSerilogStartupLogger(() => {
    var builder = WebApplication.CreateBuilder(args);
    builder.Host.UseSerilog();

    var app = builder.Build();
    app.UseSerilog();
    app.Run();
});

Upvotes: 0

Bhavin Varsur
Bhavin Varsur

Reputation: 327

It looks ok.but have you add dependency(DI) of log or relateable class of logging framework in your configureservice method in startup.cs?

like this : public void ConfigureServices(IServiceCollection services) { services.AddSingleton<ILogger, Logger>(); }

Upvotes: 0

user3230660
user3230660

Reputation:

I finally got it thanks to Nicholas comment above.

https://nblumhardt.com/2019/10/serilog-in-aspnetcore-3/

    public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog() // <- Add this line
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Upvotes: 1

Related Questions