serilog-extensions-hosting
serilog-extensions-hosting copied to clipboard
UseSerilog Infinite loop: ReadFrom.Services + KinesisFirehoseSink
If you use the Two-stage initialization
from here:
https://github.com/serilog/serilog-aspnetcore#two-stage-initialization
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
and configure a KinesisFirehoseSink like so...
public void ConfigureServices(IServiceCollection services)
{
services.AddAWSService<IAmazonKinesisFirehose>();
services.AddSingleton(new KinesisFirehoseSinkOptions("LoggingStream"));
services.AddTransient<ILogEventSink, KinesisFirehoseSink>();
}
You end up in an infinite loop because the IAmazonKinesisFirehose
factory is looking for an ILoggerFactory
.
I am experiencing a similar issue when using .AddAzureClients
from the Microsoft.Extensions.Azure
NuGet package. Did you figure out how to work around this?
I just ditched the two-stage init. I came to the conclusion it's not that important to me and creates more issues than it solves. Initialization changes infrequently, errors outside development are rare, and when they do happen I make sure they fail spectacularly and if need be I can look at a file log on the host. I don't need them sent to kinesis.
I believe I bump into the same issue - custom service that needs ILogger<T>
dependency:
Sample
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<FooService>();
builder.Host.UseSerilog((context, services, configuration) =>
{
var service = services.GetRequiredService<FooService>();
});
builder.Build().Run();
public class FooService
{
private readonly ILogger<FooService> _logger;
public FooService(ILogger<FooService> logger)
=> _logger = logger;
}
Setup
NET 6
, web application scaffolded by VS, the following packages added:
-
Serilog.AspNetCore 6.1.0
Issue
This gets stuck in an infinite loop like OP described. In me specific case thread dies after 252 iterations and the app never starts...
Shouldn't a bootstrap logger be used initially in such case - this would've solved the isssue I believe?
Addional info
The same exact thing happens when using pre-net6 configuration via GenericHost
:
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
.UseSerilog((context, services, configuration) =>
{
var service = services.GetRequiredService<FooService>();
})
.Build()
.Run();
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<FooService>();
}
public void Configure(IApplicationBuilder app) { }
}
Workaround (kinda)
Well, this is far far from ideal, but I've come up with builing GenericHost
first, resolving services from there and then building another GenericHost
this time with .UseSerilog()
and use built ServiceProvider there.
Note: this solution is for pre-net6
Program.cs
but I could imagine a similar approach could be taken withWebApplicationBuilder
Note: this causes a noticable performace hit on application startup, so use with caution
// ...
var serviceProvider = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
.Build()
.Services;
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
.UseSerilog((context, _, configuration) =>
{
var service = serviceProvider.GetRequiredService<FooService>();
})
.Build()
.Run();
// ...