I am trying to log errors thrown during app startup to sentry. I am using ASP.NET Core 2.1.2, with Serilog and Sentry. If I use just sentry, it seems to work okay.
WebHost.CreateDefaultBuilder(args)
.UseSentry("dsn")
.UseStartup<Startup>()
.Build()
.Run();
However, if I add Serilog, the app does not send any events during startup, although it keeps working if an exception is thrown from a controller action.
WebHost.CreateDefaultBuilder(args)
.UseSerilog(
(hostingContext, loggerConfiguration) =>
loggerConfiguration
.Enrich.FromLogContext()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Sentry(s => {
s.MinimumBreadcrumbLevel = LogEventLevel.Debug;
s.MinimumEventLevel = LogEventLevel.Error;
}))
.UseSentry("dsn")
.UseStartup<Startup>()
.Build()
.Run();
I'm trying to log from both the ConfigureServices
and Configure
methods in the Startup class:
public class Startup
{
public Startup(IConfiguration configuration, ILogger<Startup> logger)
{
Configuration = configuration;
Logger = logger;
}
public IConfiguration Configuration { get; }
public ILogger<Startup> Logger { get; }
public void ConfigureServices(IServiceCollection services)
{
Logger.LogError(new Exception("something went wrong while configuring services"), "ERROR");
services
.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
Logger.LogError(new Exception("something went wrong while configuring app"), "ERROR");
}
}
I guess it has something to do with the order of the initialization of each logger, but I don't undertand the startup process enough to come up with a solution or at least an explanation. Any idea why this happens and how could I fix it?
Update
It seems that explicity adding the DNS to the serilog configuration also fixes the issue, although this seems to be against the samples in the sentry repo.
.UseSerilog(
(hostingContext, loggerConfiguration) =>
loggerConfiguration
.Enrich.FromLogContext()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.Sentry(s => {
s.MinimumBreadcrumbLevel = LogEventLevel.Debug;
s.MinimumEventLevel = LogEventLevel.Error;
s.Dsn = new Dsn(dsn);
}))
The ASP.NET Core logging infrastructure is built before the application request pipeline. For that reason, we can capture exceptions that happen during app start only via a Logging integration.
The reason that application initialization errors are captures when you use only Sentry.AspNetCore
is that this package depends on Sentry.Extensions.Logging
.
When you do UseSentry
, it internally hooks into the frameworks logging infrastructure.
Crashes in the Startup
class happen before the app is actually built (the completion of ConfigureServices
and Configure
are required to boot the app) so only using the Logging integration we're able to capture errors at that point.
Once you add Serilog
to your app, the default logging backend gets replace by it. That means that the hooks to the logging infrastructure done by Sentry.Extensions.Logging
no longer take effect now that Serilog
has taken over.
For that reason you need to add the package Sentry.Serilog
too.
In this case, with both packages added, you're able to capture bootstrapping errors only if you initialize the SDK via the Serilog
integration (as you did per your last comment). That's not done in the samples to avoid confusion (why do I need to provide my DSN twice?) but what happens under the hood is that the first initialization, done by Serilog is only really useful until the Startup
process is finished. Once Sentry.AspNetCore
is initialized, it'll close the first client created by the Serilog
integration and flush all events and add a new client to the main scope. This makes sure things like request data are also added to the event.