I'm currently working on a small demo project for a dotnet console application using the generic hosting for logging and configuration. In the context of writing this demo / proof of concept app I ran into the problem, that the log messages to the console stopped being written, but the program itself finished his work without a problem.
The console app here should do batch processing and is not intended as a long running service. We intend to find out if (and how) the items can be processed in a parallel manner. Therefore we created this demo program. In this demo program, we intentionally decided NOT to use async calls. First, to make it as linear as possible and, second, to make it as understandable as possible. In upcomming iterations we will add / remove such features, depending on the paths we explore and the runtime behaviour.
It's my first experience with the generic hosting in dotnet 6( ). I would like to benefit from the out of the box features like DI, configuration and logging.
Now my questions:
- For a batch processing console application in dotnet: is the generic hosting environment the right approach?
- For batch processing is
IHostedService
the right way? Should I rather useBackgroundService
? - Ideally: How do I control the application so it waits for the
console logger queue
to finish its job? - Here's desperate me asking: What am I doing wrong? Which detail do I miss?
Here's the very simplified setup:
Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using var host = Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
.ConfigureServices((_, services) =>
{
services.AddHostedService<Worker>();
})
.Build();
host.Start();
// host.Run(); // could be used instead of host.Start();
Worker.cs
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
internal class Worker : IHostedService
{
private readonly ILogger<Worker> _log;
private readonly IHostApplicationLifetime _appLifetime;
public Worker2(ILogger<Worker> log,
IHostApplicationLifetime appLifetime
) => (_log, _appLifetime) = (log, appLifetime);
public Task StartAsync(CancellationToken cancellationToken)
{
_log.LogInformation("now load a list of item numbers");
var itemNumbers = Enumerable.Range(1, 1000);
foreach (var itemNumber in itemNumbers)
{
if (cancellationToken.IsCancellationRequested)
break;
_log.LogInformation("processing item nr {itemNumber}", itemNumber);
}
_log.LogInformation("I'm done here. good bye!");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
example output to console
info: Worker2[0]
now load a list of item numbers
info: Worker2[0]
processing item nr 1
info: Worker2[0]
processing item nr 2
info: Worker2[0]
processing item nr 3
...
info: Worker2[0]
processing item nr 22
info: Worker2[0]
processing item nr 23
info: Worker2[0]
processing item nr 24
Process finished with exit code 0.
When debugging this simple app, it shows, that its actually running through all 1000 items and it's also logging the last message ("I'm done here. good bye!"). Unfortunately, the logs from item nr. 25 and up are never written to the console.
What I found out:
- The application creates two main threads: one for the
worker
and one for theconsole logger queue
. - I have no control over the
console logger queue
thread - If I shutdown the
worker
(or it finishes) it shuts down the application and the application does not wait for theconsole logger queue
(and therefore kills it). - Using
BackgroundService
gives me the same result. - Using
host.Run()
instead ofhost.Start()
did work, although I had to manually stop the application, which is also not what I want. When it's done with its work, it should terminate.
Thank you in advance!
CodePudding user response:
I would wrap the app.Start in a try/catch/finally ... and depending on whether you're using something like Serilog ... call Log.CloseAndFlush() in the finally.
Similar question here: How can I flush all loggers in a dotnet core appliation just before doing a Environment.Exit()