tools/code/common/Hosting.cs (84 lines of code) (raw):
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace common;
public delegate ValueTask RunApplication(CancellationToken cancellationToken);
public static class HostingModule
{
/// <param name="applicationName">Will be used to set the logger name and the OpenTelemetry activity source name.</param>
/// <param name="configureRunApplication">Delegate that adds <see cref="common.RunApplication" /> to the builder services.</param>
/// <returns></returns>
public static async ValueTask RunHost(string[] arguments, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
using var host = GetHost(arguments, applicationName, configureRunApplication);
await StartHost(host);
await RunApplication(host);
}
private static IHost GetHost(string[] arguments, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
var builder = GetBuilder(arguments, applicationName, configureRunApplication);
return builder.Build();
}
private static HostApplicationBuilder GetBuilder(string[] arguments, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
var builder = Host.CreateApplicationBuilder(arguments);
ConfigureBuilder(builder, applicationName, configureRunApplication);
return builder;
}
private static void ConfigureBuilder(HostApplicationBuilder builder, string applicationName, Action<IHostApplicationBuilder> configureRunApplication)
{
ConfigureConfiguration(builder);
OpenTelemetryModule.Configure(builder, applicationName);
ConfigureLogging(builder, applicationName);
configureRunApplication(builder);
}
private static void ConfigureConfiguration(HostApplicationBuilder builder)
{
if (Assembly.GetEntryAssembly() is Assembly entryAssembly)
{
builder.Configuration.AddUserSecretsWithLowestPriority(entryAssembly);
}
builder.Configuration
.TryGetValue("CONFIGURATION_YAML_PATH")
.Iter(path => builder.Configuration.AddYamlFile(path));
}
private static void ConfigureLogging(HostApplicationBuilder builder, string applicationName)
{
builder.Services.TryAddSingleton(provider => GetCommonLogger(provider, applicationName));
}
private static ILogger GetCommonLogger(IServiceProvider provider, string applicationName)
{
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
return loggerFactory.CreateLogger(applicationName);
}
private static async ValueTask StartHost(IHost host)
{
var applicationLifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
var cancellationToken = applicationLifetime.ApplicationStopping;
await host.StartAsync(cancellationToken);
}
/// <summary>
/// Extracts the delegate <see cref="common.RunApplication"/> from the host's services and runs it.
/// </summary>
private static async ValueTask RunApplication(IHost host)
{
var applicationLifetime = host.Services.GetRequiredService<IHostApplicationLifetime>();
var cancellationToken = applicationLifetime.ApplicationStopping;
try
{
var runApplication = host.Services.GetRequiredService<RunApplication>();
await runApplication(cancellationToken);
}
catch (Exception exception)
{
var logger = host.Services.GetRequiredService<ILogger>();
logger.LogCritical(exception, "Application failed.");
Environment.ExitCode = -1;
throw;
}
finally
{
applicationLifetime.StopApplication();
}
}
}