/// <summary> /// The program's main start/entry point. Hold on to your butts .... here we go! /// </summary> /// <typeparam name="T">Startup class type.</typeparam> /// <param name="args">Optional command line arguments.</param> /// <returns>Task of this Main application run.</returns> public static async Task Main <TStartup>(string[] args) where TStartup : class { var options = new MainOptions <TStartup> { CommandLineArguments = args }; await Main(options); }
public static IHostBuilder CreateHostBuilder <TStartup>(MainOptions <TStartup> options) where TStartup : class { var hostBuilder = Host .CreateDefaultBuilder(options.CommandLineArguments) .ConfigureLogging((context, logging) => { // Don't want any of the default crap. logging.ClearProviders(); }) .UseSerilog(); var logger = new SerilogLoggerProvider(Log.Logger).CreateLogger(nameof(Program)); hostBuilder .ConfigureWebHostDefaults(webBuilder => { if (options.StartupActivation is null) { // Normal startup class with default constructor. webBuilder.UseStartup <TStartup>(); } else { // Use the custom startup activation function, instead. // REF: https://docs.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-5.0?view=aspnetcore-5.0#control-startup-class-activation // (Pro Tip: this is a great way to add logging, to Startup.cs !!! YES!!!! ) //webBuilder.UseStartup(c => new TStartup(c)); webBuilder.UseStartup(context => options.StartupActivation(context, logger)); // The startup class (activated, above) will be activated in _this_ assmebly and not the main host/app assembly. // This means that when things like 'MapControllers' tries to do an assembly scan (the default functionality) // in the host/app assembly, it will FAIL to find any. // As such, we actually need to really reset the main ApplicationKey to say it's for the provided startup class. // Hat tip to: @aarondandy, @buildstarted and @xt0rted var startupAssemblyName = options.StartupActivation.GetMethodInfo().DeclaringType !.GetTypeInfo().Assembly.GetName().Name; webBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName); } }); return(hostBuilder); }
/// <summary> /// The program's main start/entry point. Hold on to your butts .... here we go! /// </summary> /// <typeparam name="T">Startup class type.</typeparam> /// <param name="options">Options to help setup/configure your program.</param> /// <returns>Task of this Main application run.</returns> public static async Task Main <TStartup>(MainOptions <TStartup> options) where TStartup : class { try { if (options is null) { throw new ArgumentNullException(nameof(options)); } // Before we do _ANYTHING_ we need to have a logger so we can start // seeing what is going on ... good or bad. Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(GetConfigurationBuilder()) .Enrich.FromLogContext() .CreateLogger(); // Display any (optional) initial banner / opening text to define the start of this application now starting. if (!string.IsNullOrWhiteSpace(options.FirstLoggingInformationMessage)) { Log.Information(options.FirstLoggingInformationMessage); } if (options.LogAssemblyInformation) { var assembly = typeof(TStartup).Assembly; var assemblyDate = string.IsNullOrWhiteSpace(assembly.Location) ? "-- unknown --" : File.GetLastWriteTime(assembly.Location).ToString("u"); var assemblyInfo = $"Name: {assembly.GetName().Name} | Version: {assembly.GetName().Version} | Date: {assemblyDate}"; Log.Information(assemblyInfo); } var host = CreateHostBuilder(options).Build(); // Ok, now lets go and start! await host.RunAsync(); } catch (Exception exception) { const string errorMessage = "Something seriously unexpected has occurred while preparing the Host. Sadness :~("; // We might NOT have created a logger ... because we might be _trying_ to create the logger but // we have some bad setup-configuration-data and boom!!! No logger successfully setup/created. // So, if we do have a logger created, then use it. if (Log.Logger is Logger) { // TODO: Add metrics (like Application Insights?) to log telemetry failures -IF- Serilog can't do this adequately. Log.Logger.Fatal(exception, errorMessage); } else { // Nope - failed to create a logger and we have a serious error. So lets // just fall back to the Console and _hope_ someone can read/access that. Console.WriteLine(_explosion); Console.WriteLine(errorMessage); Console.WriteLine(); Console.WriteLine(); Console.WriteLine($"Error: {exception.Message}"); Console.WriteLine(); } } finally { var shutdownMessage = string.IsNullOrWhiteSpace(options.LastLoggingInformationMessage) ? "Application has now shutdown." : options.LastLoggingInformationMessage; // Again: did we successfully create a logger? if (Log.Logger is Logger) { Log.Information(shutdownMessage); // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) Log.CloseAndFlush(); } else { Console.WriteLine(shutdownMessage); } } }