public static void Main(string[] args)
        {
            Serilog.Debugging.SelfLog.Enable(Console.Error);

            Log.Logger = new LoggerConfiguration()
                         .Enrich.FromLogContext()
                         .WriteTo.Console()
                         .CreateBootstrapLogger();

            IDisposable dotNetRuntimeStats = null;

            try
            {
                dotNetRuntimeStats = DotNetRuntimeStatsBuilder.Default().StartCollecting();
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "An unhandled exception occurred during bootstrapping");
            }
            finally
            {
                dotNetRuntimeStats?.Dispose();
                Log.CloseAndFlush();
            }
        }
        public static void Main(string[] args)
        {
            Console.WriteLine("Enabling prometheus-net.DotNetStats...");
            DotNetRuntimeStatsBuilder.Default().StartCollecting();

            CreateHostBuilder(args).Build().Run();
        }
        public Measurer(IOptions <MeasurerConfiguration> measurerConfigurationOptions,
                        IOptions <CommonConfiguration> commonConfigurationOptions)
        {
            var measurerConfiguration = measurerConfigurationOptions.Value;

            _collector = DotNetRuntimeStatsBuilder.Default().StartCollecting();

            _heartbeatCounter = Prometheus.Metrics.CreateCounter("heartbeat_count", "heartbeat");

            _httpRequestCounter = Prometheus.Metrics.CreateCounter("request_total", "HTTP Requests Total",
                                                                   new CounterConfiguration
            {
                LabelNames = new[] { "path", "method", "status", "correlation-id" }
            });

            if (measurerConfiguration.UsePushMetricsServer)
            {
                //push model
                _pusher = new MetricPusher(new MetricPusherOptions
                {
                    Endpoint             = measurerConfiguration.PushMetricsServerEndpoint,
                    Job                  = "push-metrics-job",
                    Instance             = commonConfigurationOptions.Value.InstanceId,
                    IntervalMilliseconds = (long)measurerConfiguration.FlushPeriod.TotalMilliseconds
                });

                _pusher.Start();
            }
        }
        public void Start()
        {
            _collector = DotNetRuntimeStatsBuilder.Default().StartCollecting();


            _logger.LogInformation("LifetimeManager starting...");
            _client.Start();

            _logger.LogInformation("LifetimeManager sleep 2 second...");
            Thread.Sleep(2000);

            _logger.LogInformation("sessionReader - count: {Count}", _sessionReader.Count());
            _logger.LogInformation("registrationTokenReader - count: {Count}", _registrationTokenReader.Count());
            _logger.LogInformation("clientWalletReader - count: {Count}", _clientWalletReader.Count());
            _logger.LogInformation("assetsReader - count: {Count}", _assetsReader.Count());
            _logger.LogInformation("assetPairsReader - count: {Count}", _assetPairsReader.Count());
            _logger.LogInformation("personalDataReader - count: {Count}", _personalDataReader.Count());
            _logger.LogInformation("authDataReader - count: {Count}", _authDataReader.Count());
            _logger.LogInformation("authIndexNyIdDataReader - count: {Count}", _authIndexNyIdDataReader.Count());
            _logger.LogInformation("clientProfileDataReader - count: {Count}", _clientProfileDataReader.Count());
            _logger.LogInformation("orderBookDataReader - count: {Count}", _orderBookDataReader.Count());
            _logger.LogInformation("priceDataReader - count: {Count}", _priceDataReader.Count());


            _logger.LogInformation("LifetimeManager started");
        }
Example #5
0
        public static async Task Main(string[] args)
        {
            var service = new MyService();

            service.MetricsOptions.Mode = MetricsMode.Scrape;

            // For .NET Core 2.2+ based services, we highly recommend that you enable
            // collection of the .NET Runtime metrics as well to capture information
            // about threads, memory, exceptions, JIT statistics, etc.  You can do this
            // with just one more statement:

            service.MetricsOptions.GetCollector = () =>
                                                  DotNetRuntimeStatsBuilder
                                                  .Default()
                                                  .StartCollecting();

            // The line above collects all of the available runtime metrics.  You can
            // customize which metrics are collected using this commented line, but
            // we recommend collecting everything because you never know what you'll
            // need:

            //service.MetricsOptions.GetCollector = () =>
            //    DotNetRuntimeStatsBuilder
            //        .Customize()
            //        .WithContentionStats()
            //        .WithJitStats()
            //        .WithThreadPoolStats()
            //        .WithGcStats()
            //        .WithExceptionStats()
            //        .StartCollecting();

            await new MyService().RunAsync();
        }
Example #6
0
        /// <summary>
        /// The program entrypoint.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static async Task Main(string[] args)
        {
            NeonService.Initialize();

            try
            {
                Service = new Service(KubeService.NeonSsoSessionProxy);

                Service.MetricsOptions.Mode         = MetricsMode.Scrape;
                Service.MetricsOptions.GetCollector =
                    () =>
                {
                    return(DotNetRuntimeStatsBuilder
                           .Default()
                           .StartCollecting());
                };

                Environment.Exit(await Service.RunAsync());
            }
            catch (Exception e)
            {
                // We really shouldn't see exceptions here but let's log something
                // just in case.  Note that logging may not be initialized yet so
                // we'll just output a string.

                Console.Error.WriteLine(NeonHelper.ExceptionError(e));
                Environment.Exit(-1);
            }
        }
Example #7
0
        public static IDisposable CreateCollector()
        {
            _logger.LogInformation($"Configuring prometheus-net.DotNetRuntime: will recycle event listeners every {_options.RecycleEvery} ({_options.RecycleEvery.TotalSeconds:N0} seconds).");

            var builder = DotNetRuntimeStatsBuilder.Default();

            if (!_options.UseDefaultMetrics)
            {
                builder = DotNetRuntimeStatsBuilder.Customize()
                          .WithContentionStats(CaptureLevel.Informational)
                          .WithGcStats(CaptureLevel.Verbose)
                          .WithThreadPoolStats(CaptureLevel.Informational)
                          .WithExceptionStats(CaptureLevel.Errors)
                          .WithJitStats()
                          .WithKestrelStats(CaptureLevel.Informational);
            }

            builder
#if NET5_0
            .RecycleCollectorsEvery(_options.RecycleEvery)
#endif
            .WithErrorHandler(ex => _logger.LogError(ex, "Unexpected exception occurred in prometheus-net.DotNetRuntime"));

            if (_options.UseDebuggingMetrics)
            {
                _logger.LogInformation("Using debugging metrics.");
                builder.WithDebuggingMetrics(true);
            }

            _logger.LogInformation("Starting prometheus-net.DotNetRuntime...");

            return(builder
                   .StartCollecting());
        }
Example #8
0
        static void Main(string[] args)
        {
            var p = new Prometheus.MetricServer(12203);

            p.Start();

            var collector = DotNetRuntimeStatsBuilder.Default().StartCollecting();


            var tasks = Enumerable.Range(1, 2_000_000)
                        .Select(_ => Task.Run(() => 1))
                        .ToArray();

            var b  = new byte[1024 * 1000];
            var b2 = new byte[1024 * 1000];
            var b3 = new byte[1024 * 1000];

            Task.WaitAll(tasks);

            Console.WriteLine("Done");
            Console.ReadLine();

            return;

            BenchmarkRunner.Run <TestBenchmark>(
                DefaultConfig.Instance
                .With(
                    Job.Core
                    .WithLaunchCount(1)
                    .WithIterationTime(TimeInterval.FromMilliseconds(200))
                    .With(Platform.X64)
                    .With(Jit.RyuJit)
                    )
                );
        }
        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (!_options.Enabled)
                return Task.CompletedTask;

            _logger.LogInformation("Configuring prometheus-net.DotNetRuntime");

            var builder = DotNetRuntimeStatsBuilder.Default();

            if (_options.Verbose)
            {
                builder = DotNetRuntimeStatsBuilder.Customize()
                    .WithContentionStats(CaptureLevel.Informational)
                    .WithGcStats(CaptureLevel.Verbose)
                    .WithThreadPoolStats(CaptureLevel.Informational)
                    .WithExceptionStats(CaptureLevel.Errors)
                    .WithJitStats();
            }

            builder.WithErrorHandler(ex =>
                _logger.LogError(ex, "Unexpected exception occurred in prometheus-net.DotNetRuntime"));

            if (_options.Debug)
            {
                _logger.LogInformation("Using debugging metrics");
                builder.WithDebuggingMetrics(true);
            }

            _logger.LogInformation("Starting prometheus-net.DotNetRuntime");

            _metrics = builder.StartCollecting();

            return Task.CompletedTask;
        }
 protected override DotNetRuntimeStatsBuilder.Builder GetStatsBuilder()
 {
     return(DotNetRuntimeStatsBuilder.Default()
            .WithThreadPoolSchedulingStats(sampleRate: SampleEvery.OneEvent)
            .WithJitStats(SampleEvery.OneEvent)
            .WithContentionStats(SampleEvery.OneEvent));
 }
 public async Task Default_registers_all_expected_stats()
 {
     // arrange
     using (DotNetRuntimeStatsBuilder.Default().StartCollecting())
     {
         await Assert_Expected_Stats_Are_Present_In_Registry(GetDefaultRegistry());
     }
 }
Example #12
0
 public static void Main(string[] args)
 {
     DotNetRuntimeStatsBuilder.Default().WithErrorHandler(e =>
     {
         Console.WriteLine(e.ToString());
     }).StartCollecting();
     CreateWebHostBuilder(args).Build().Run();
 }
Example #13
0
 public static void Main(string[] args)
 {
     DotNetRuntimeStatsBuilder.Default().WithErrorHandler(e =>
     {
         Console.WriteLine(e.ToString());
     }).StartCollecting();
     Serilog.Debugging.SelfLog.Enable(Console.Error);
     CreateHostBuilder(args).Build().Run();
 }
        public async Task Default_registers_all_expected_stats_to_a_custom_registry()
        {
            // arrange
            var registry = NewRegistry();

            using (DotNetRuntimeStatsBuilder.Default().StartCollecting(registry))
            {
                await Assert_Expected_Stats_Are_Present_In_Registry(registry);
            }
        }
Example #15
0
        public async Task Debugging_Metrics_Works_Correctly()
        {
            // arrange
            var registry = NewRegistry();

            using (DotNetRuntimeStatsBuilder.Default().WithDebuggingMetrics(true).StartCollecting(registry))
            {
                await Assert_Expected_Stats_Are_Present_In_Registry(registry, shouldContainDebug : true);
            }
        }
Example #16
0
        public static void Main(string[] args)
        {
            var collector = DotNetRuntimeStatsBuilder.Default().StartCollecting();
            //var server = new MetricServer(hostname: "*", port: 1234);
            var server = new MetricServer(hostname: "*", port: 1234);

            server.Start();

            _timer = new Timer(LogNetworkAndGcInfo, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
            CreateHostBuilder(args).Build().Run();
        }
Example #17
0
        public async Task RuntimeMetrics()
        {
            // Verify that we can also expose .NET Runtime exposes metrics via the [GetCollector()] callback.

            var service     = new TestService();
            var metricPort  = NetHelper.GetUnusedTcpPort(IPAddress.Loopback);
            var metricsPath = "foo/";

            service.MetricsOptions.Mode         = MetricsMode.Scrape;
            service.MetricsOptions.Port         = metricPort;
            service.MetricsOptions.Path         = metricsPath;
            service.MetricsOptions.GetCollector = () => DotNetRuntimeStatsBuilder.Default().StartCollecting();

            var runTask = service.RunAsync();

            // Wait for the test service to do its thing.

            await service.ReadyToExitEvent.WaitAsync();

            using (var httpClient = new HttpClient())
            {
                var scrapedMetrics = await httpClient.GetStringAsync($"http://127.0.0.1:{metricPort}/{metricsPath}");

                var metrics = ParseMetrics(scrapedMetrics);

                // Verify the test counter.

                Assert.True(metrics["test_counter"] > 0);

                // Verify Neon logging counters.

                Assert.True(metrics[@"neon_log_events_total{level=""warn""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""critical""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""transient""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""sinfo""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""error""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""debug""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""serror""}"] > 0);
                Assert.True(metrics[@"neon_log_events_total{level=""info""}"] > 0);

                // Verify some .NET Runtime metrics

                Assert.True(metrics.ContainsKey("process_private_memory_bytes"));
                Assert.True(metrics.ContainsKey("dotnet_exceptions_total"));
                Assert.True(metrics.ContainsKey("dotnet_threadpool_num_threads"));
            }

            // Tell the service it can exit.

            service.CanExitEvent.Set();

            await runTask;
        }
Example #18
0
        public MetricsService(IOptionsMonitor <ServerOptions> options, ILogger <MetricsService> logger)
        {
            _logger = logger;

            _runtimeStatsRegistration = DotNetRuntimeStatsBuilder.Default().StartCollecting();

            var port = options.CurrentValue.MetricsPort;

            if (port != null)
            {
                _server = new KestrelMetricServer(_port = port.Value);
            }
        }
Example #19
0
        public static void Main(string[] args)
        {
            Activity.DefaultIdFormat      = ActivityIdFormat.W3C;
            Activity.ForceDefaultIdFormat = true;

#if !DEBUG
            new MetricServer(8000).Start();
#endif

            using var collector = DotNetRuntimeStatsBuilder.Default().StartCollecting();

            CreateHostBuilder(args).Build().Run();
        }
Example #20
0
        public static async Task Main(string[] args)
        {
            var service = new MyService();

            service.Dependencies.Uris.Add(new Uri("http://waitforthis.com"));

            service.MetricsOptions.Mode         = MetricsMode.Scrape;
            service.MetricsOptions.GetCollector = () =>
                                                  DotNetRuntimeStatsBuilder
                                                  .Default()
                                                  .StartCollecting();

            await new MyService().RunAsync();
        }
Example #21
0
        public async Task StartCollecting_Allows_A_New_Collector_To_Run_After_Disposing_A_Previous_Collector()
        {
            var registry = NewRegistry();

            using (DotNetRuntimeStatsBuilder.Default().StartCollecting(registry))
            {
                await Assert_Expected_Stats_Are_Present_In_Registry(registry);
            }

            using (DotNetRuntimeStatsBuilder.Default().StartCollecting(registry))
            {
                await Assert_Expected_Stats_Are_Present_In_Registry(registry);
            }
        }
Example #22
0
        public static async Task Main(string[] args)
        {
            DotNetRuntimeStatsBuilder.Default().WithErrorHandler(e =>
            {
                Console.WriteLine(e.ToString());
            }).StartCollecting();

            var host = BuildWebHost(args);
            await host.MigrateDbContextAsync <ApplicationDbContext>(async (context, services) =>
            {
                await DbInitializer.SeedAsync(context, services);
            });

            host.Run();
        }
Example #23
0
        public static async Task Main(string[] args)
        {
            DotNetRuntimeStatsBuilder.Default().WithErrorHandler(e =>
            {
                Console.WriteLine(e.ToString());
            }).StartCollecting();

            Serilog.Debugging.SelfLog.Enable(Console.Error);

            var host = CreateHostBuilder(args).Build();
            await host.MigrateDbContextAsync <ApplicationDbContext>(async (context, services) =>
            {
                await DbInitializer.SeedAsync(context, services);
            });

            host.Run();
        }
Example #24
0
        public static void Main(string[] args)
        {
            // Sets default settings to collect dotnet runtime specific metrics
            DotNetRuntimeStatsBuilder.Default().StartCollecting();

            //You can also set the specifics on what metrics you want to collect as below
            // DotNetRuntimeStatsBuilder.Customize()
            //     .WithThreadPoolSchedulingStats()
            //     .WithContentionStats()
            //     .WithGcStats()
            //     .WithJitStats()
            //     .WithThreadPoolStats()
            //     .WithErrorHandler(ex => Console.WriteLine("ERROR: " + ex.ToString()))
            //     //.WithDebuggingMetrics(true);
            //     .StartCollecting();

            CreateHostBuilder(args).Build().Run();
        }
Example #25
0
        /// <summary>
        /// The program entrypoint.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static async Task Main(string[] args)
        {
            NeonService.Initialize();

            var options = Parser.Default.ParseArguments <Options>(args).Value;

            try
            {
                Service = new Service(Service.ServiceName);

                if (!string.IsNullOrEmpty(options.ConfigFile))
                {
                    using (StreamReader reader = new StreamReader(new FileStream(options.ConfigFile, FileMode.Open, FileAccess.Read)))
                    {
                        Service.SetConfigFile(Service.ConfigFile, await reader.ReadToEndAsync());
                    }
                }

                if (!NeonHelper.IsDevWorkstation)
                {
                    Service.MetricsOptions.Mode         = MetricsMode.Scrape;
                    Service.MetricsOptions.GetCollector =
                        () =>
                    {
                        return(DotNetRuntimeStatsBuilder
                               .Default()
                               .StartCollecting());
                    };
                }

                Environment.Exit(await Service.RunAsync());
            }
            catch (Exception e)
            {
                // We really shouldn't see exceptions here but let's log something
                // just in case.  Note that logging may not be initialized yet so
                // we'll just output a string.

                Console.Error.WriteLine(NeonHelper.ExceptionError(e));
                Environment.Exit(-1);
            }
        }
        public async Task Default_registers_all_expected_stats()
        {
            // arrange
            using (DotNetRuntimeStatsBuilder.Default().StartCollecting())
            using (var metricServer = new MetricServer(12203))
            using (var client = new HttpClient())
            {
                metricServer.Start();

                // act + assert
                using (var resp = await client.GetAsync("http://localhost:12203/metrics"))
                {
                    var content = await resp.Content.ReadAsStringAsync();
                    
                    // Some basic assertions to check that the output of our stats collectors is present
                    Assert.That(content, Contains.Substring("dotnet_threadpool"));
                    Assert.That(content, Contains.Substring("dotnet_jit"));
                    Assert.That(content, Contains.Substring("dotnet_gc"));
                    Assert.That(content, Contains.Substring("dotnet_contention"));
                }
            }
        }
Example #27
0
        public void Init()
        {
            DiscoverTypes();

            ConfigurationManager.AddParts(GetExports <IConfigurationFactory>());

            // Have to migrate settings here as migration subsystem not yet initialised.
            MigrateNetworkConfiguration();
            NetManager = new NetworkManager(ConfigurationManager, LoggerFactory.CreateLogger <NetworkManager>());

            // Initialize runtime stat collection
            if (ConfigurationManager.Configuration.EnableMetrics)
            {
                DotNetRuntimeStatsBuilder.Default().StartCollecting();
            }

            var networkConfiguration = ConfigurationManager.GetNetworkConfiguration();

            HttpPort  = networkConfiguration.HttpServerPortNumber;
            HttpsPort = networkConfiguration.HttpsPortNumber;

            // Safeguard against invalid configuration
            if (HttpPort == HttpsPort)
            {
                HttpPort  = NetworkConfiguration.DefaultHttpPort;
                HttpsPort = NetworkConfiguration.DefaultHttpsPort;
            }

            CertificateInfo = new CertificateInfo
            {
                Path     = networkConfiguration.CertificatePath,
                Password = networkConfiguration.CertificatePassword
            };
            Certificate = GetCertificate(CertificateInfo);

            RegisterServices();

            _pluginManager.RegisterServices(ServiceCollection);
        }
Example #28
0
        public static int Main(string[] args)
        {
            var overrideArgs = Environment.GetEnvironmentVariable("AUTOCRANE_ARGS");

            if (args.Length < 1 && string.IsNullOrEmpty(overrideArgs))
            {
                Console.Error.WriteLine("Usage: AutoCrane <mode>");
                Console.Error.WriteLine("Available modes:");
                Console.Error.WriteLine("  orchestrate: Watches for AutoCraneDeployment CRDs, creates Kubernetes pods for apps.");
                Console.Error.WriteLine("  datadeployinit: Init container process to ensure data directories are set up before app runs.");
                Console.Error.WriteLine("  datadeploy: Sidecar which updates app to use new data folders when requested.");
                Console.Error.WriteLine("  datarepo: Downloads data at origin and makes available to cluster.");
                Console.Error.WriteLine("  getwatchdog: Tool to list watchdog status.");
                Console.Error.WriteLine("  postwatchdog: Tool to update watchdog status.");
                Console.Error.WriteLine("  testworkload: A webservice that can fail its watchdogs on demand.");
                Console.Error.WriteLine("  versionwatcher: Probes origin sources for new app and data versions.");
                Console.Error.WriteLine("  watchdogprober: Discovers probe endpoints through pod annotations, probes and sets watchdogs.");
                Console.Error.WriteLine("  watchdoghealthz: Surfaces pod's watchdog health through a /healthz endpoint.");
                Console.Error.WriteLine("  yaml: Generate yaml for installing into cluster.");
                return(1);
            }

            if (!string.IsNullOrEmpty(overrideArgs))
            {
                // args = overrideArgs.Split(' ').Select(i => i.Replace("%20", " ")).ToArray();
                args = overrideArgs.Split(' ');
            }

            var mode    = args[0].ToLowerInvariant();
            var newargs = args.Skip(1).ToArray();

            using var collector = DotNetRuntimeStatsBuilder.Default().StartCollecting();
            try
            {
                using var cts           = new CancellationTokenSource();
                Console.CancelKeyPress += (object?sender, ConsoleCancelEventArgs eventArgs) =>
                {
                    Console.Error.WriteLine("Handling Ctrl-C...");
                    cts.Cancel();
                    eventArgs.Cancel = false;
                };

                switch (mode)
                {
                case "orchestrate":
                    return(ConsoleWrapper <Orchestrator>(newargs, cts.Token));

                case "secretwriter":
                    return(ConsoleWrapper <SecretWriter>(newargs, cts.Token));

                case "getwatchdog":
                    return(ConsoleWrapper <GetWatchdogService>(newargs, cts.Token));

                case "postwatchdog":
                    return(ConsoleWrapper <PostWatchdogService>(newargs, cts.Token));

                case "watchdogprober":
                    return(ConsoleWrapper <WatchdogProber>(newargs, cts.Token));

                case "testworkload":
                    WebHosting.RunWebService <TestWorkload>(newargs);
                    return(0);

                case "watchdoghealthz":
                    WebHosting.RunWebService <WatchdogHealthz>(newargs);
                    return(0);

                case "yaml":
                    return(GenerateYaml.Run(newargs));

                case "datadeployinit":
                    return(ConsoleWrapper <DataDeployInit>(newargs, cts.Token));

                case "datadeploy":
                    WebHosting.RunWebService <DataDeployer>(newargs);
                    return(0);

                case "datarepo":
                    WebHosting.RunWebService <DataRepository>(newargs);
                    return(0);

                default:
                    Console.Error.WriteLine($"Mode {mode} not found");
                    return(1);
                }
            }
            catch (Exception e)
            {
                Console.Error.WriteLine($"Unhandled exception in main: {e}");
                return(1);
            }
        }
Example #29
0
        /// <summary>
        ///     Entrypoint.
        /// </summary>
        /// <param name="args">Command line arguments.</param>
        public static void Main(string[] args)
        {
            // populate the properties above so that we can override the default config file if needed, and to
            // check if the application is being run in command mode (run task and quit).
            EnvironmentVariables.Populate(prefix: EnvironmentVariablePrefix);
            Arguments.Populate(clearExistingValues: false);

            // if a user has used one of the arguments above, perform the requested task, then quit
            if (ShowVersion)
            {
                Log.Information(FullVersion);
                return;
            }

            if (ShowHelp || ShowEnvironmentVariables)
            {
                if (!NoLogo)
                {
                    PrintLogo(FullVersion);
                }

                if (ShowHelp)
                {
                    PrintCommandLineArguments(typeof(Options));
                }

                if (ShowEnvironmentVariables)
                {
                    PrintEnvironmentVariables(typeof(Options), EnvironmentVariablePrefix);
                }

                return;
            }

            if (GenerateCertificate)
            {
                GenerateX509Certificate(password: Cryptography.Random.GetBytes(16).ToBase62String(), filename: $"{AppName}.pfx");
                return;
            }

            // the application isn't being run in command mode. derive the application directory value
            // and defaults that are dependent upon it
            AppDirectory ??= DefaultAppDirectory;

            DefaultConfigurationFile   = Path.Combine(AppDirectory, $"{AppName}.yml");
            DefaultDownloadsDirectory  = Path.Combine(AppDirectory, "downloads");
            DefaultIncompleteDirectory = Path.Combine(AppDirectory, "incomplete");

            // the location of the configuration file might have been overriden by command line or envar.
            // if not, set it to the default.
            ConfigurationFile ??= DefaultConfigurationFile;

            // verify(create if needed) default application directories. if the downloads or complete
            // directories are overridden in config, those will be validated after the config is loaded.
            try
            {
                VerifyDirectory(AppDirectory, createIfMissing: true, verifyWriteable: true);
                VerifyDirectory(Path.Combine(AppDirectory, "data"), createIfMissing: true, verifyWriteable: true);
                VerifyDirectory(DefaultDownloadsDirectory, createIfMissing: true, verifyWriteable: true);
                VerifyDirectory(DefaultIncompleteDirectory, createIfMissing: true, verifyWriteable: true);
            }
            catch (Exception ex)
            {
                Log.Information($"Filesystem exception: {ex.Message}");
                return;
            }

            // load and validate the configuration
            try
            {
                Configuration = new ConfigurationBuilder()
                                .AddConfigurationProviders(EnvironmentVariablePrefix, ConfigurationFile)
                                .Build();

                Configuration.GetSection(AppName)
                .Bind(OptionsAtStartup, (o) => { o.BindNonPublicProperties = true; });

                if (OptionsAtStartup.Debug)
                {
                    Log.Information($"Configuration:\n{Configuration.GetDebugView()}");
                }

                if (!OptionsAtStartup.TryValidate(out var result))
                {
                    Log.Information(result.GetResultView());
                    return;
                }
            }
            catch (Exception ex)
            {
                Log.Information($"Invalid configuration: {(!OptionsAtStartup.Debug ? ex : ex.Message)}");
                return;
            }

            ConfigureGlobalLogger();
            Log = Serilog.Log.ForContext(typeof(Program));

            if (!OptionsAtStartup.Flags.NoLogo)
            {
                PrintLogo(FullVersion);
            }

            Log.Information("Version: {Version}", FullVersion);

            if (IsDevelopment)
            {
                Log.Warning("This is a Development build; YMMV");
            }

            if (IsCanary)
            {
                Log.Warning("This is a canary build");
                Log.Warning("Canary builds are considered UNSTABLE and may be completely BROKEN");
                Log.Warning($"Please report any issues here: {IssuesUrl}");
            }

            Log.Information("Invocation ID: {InvocationId}", InvocationId);
            Log.Information("Process ID: {ProcessId}", ProcessId);
            Log.Information("Instance Name: {InstanceName}", OptionsAtStartup.InstanceName);

            Log.Information("Using application directory {AppDirectory}", AppDirectory);
            Log.Information("Using configuration file {ConfigurationFile}", ConfigurationFile);

            RecreateConfigurationFileIfMissing(ConfigurationFile);

            if (!string.IsNullOrEmpty(OptionsAtStartup.Logger.Loki))
            {
                Log.Information("Forwarding logs to Grafana Loki instance at {LoggerLokiUrl}", OptionsAtStartup.Logger.Loki);
            }

            // bootstrap the ASP.NET application
            try
            {
                if (OptionsAtStartup.Feature.Prometheus)
                {
                    using var runtimeMetrics = DotNetRuntimeStatsBuilder.Default().StartCollecting();
                }

                var builder = WebApplication.CreateBuilder(args);

                builder.Configuration
                .AddConfigurationProviders(EnvironmentVariablePrefix, ConfigurationFile);

                builder.Host
                .UseSerilog();

                builder.WebHost
                .UseUrls()
                .UseKestrel(options =>
                {
                    Log.Information($"Listening for HTTP requests at http://{IPAddress.Any}:{OptionsAtStartup.Web.Port}/");
                    options.Listen(IPAddress.Any, OptionsAtStartup.Web.Port);

                    if (!OptionsAtStartup.Web.Https.Disabled)
                    {
                        Log.Information($"Listening for HTTPS requests at https://{IPAddress.Any}:{OptionsAtStartup.Web.Https.Port}/");
                        options.Listen(IPAddress.Any, OptionsAtStartup.Web.Https.Port, listenOptions =>
                        {
                            var cert = OptionsAtStartup.Web.Https.Certificate;

                            if (!string.IsNullOrEmpty(cert.Pfx))
                            {
                                Log.Information($"Using certificate from {cert.Pfx}");
                                listenOptions.UseHttps(cert.Pfx, cert.Password);
                            }
                            else
                            {
                                Log.Information($"Using randomly generated self-signed certificate");
                                listenOptions.UseHttps(X509.Generate(subject: AppName));
                            }
                        });
                    }
                });

                builder.Services
                .ConfigureAspDotNetServices()
                .ConfigureDependencyInjectionContainer();

                var app = builder.Build();

                app.ConfigureAspDotNetPipeline();

                if (OptionsAtStartup.Flags.NoStart)
                {
                    Log.Information("Qutting because 'no-start' option is enabled");
                    return;
                }

                app.Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Application terminated unexpectedly");
            }
            finally
            {
                Serilog.Log.CloseAndFlush();
            }
        }
        public static void Main(string[] args)
        {
            DotNetRuntimeStatsBuilder.Default().StartCollecting();

            CreateHostBuilder(args).Build().Run();
        }