diff --git a/Foundatio.sln b/Foundatio.sln index 322e7430..25fd365e 100644 --- a/Foundatio.sln +++ b/Foundatio.sln @@ -12,7 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution build\common.props = build\common.props tests\Directory.Build.props = tests\Directory.Build.props src\Directory.Build.props = src\Directory.Build.props - samples\Directory.Build.props = samples\Directory.Build.props NuGet.config = NuGet.config README.md = README.md .github\workflows\build-workflow.yml = .github\workflows\build-workflow.yml @@ -40,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Foundatio.MessagePack", "sr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Foundatio.DataProtection", "src\Foundatio.DataProtection\Foundatio.DataProtection.csproj", "{BD5CB540-DFF1-4E5F-8532-7B92935F93D4}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Foundatio.AppHost", "samples\Foundatio.AppHost\Foundatio.AppHost.csproj", "{7EABA9A2-C459-4A6A-B5AF-218E6A59E635}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -170,6 +171,18 @@ Global {BD5CB540-DFF1-4E5F-8532-7B92935F93D4}.Release|x64.Build.0 = Release|Any CPU {BD5CB540-DFF1-4E5F-8532-7B92935F93D4}.Release|x86.ActiveCfg = Release|Any CPU {BD5CB540-DFF1-4E5F-8532-7B92935F93D4}.Release|x86.Build.0 = Release|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Debug|x64.ActiveCfg = Debug|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Debug|x64.Build.0 = Debug|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Debug|x86.ActiveCfg = Debug|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Debug|x86.Build.0 = Debug|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Release|Any CPU.Build.0 = Release|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Release|x64.ActiveCfg = Release|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Release|x64.Build.0 = Release|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Release|x86.ActiveCfg = Release|Any CPU + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -178,6 +191,7 @@ Global {39AFF0C0-D64A-4D57-871F-17D9BF2E5A93} = {A1DFF80C-113F-4FEC-84BB-1E3790FB410F} {6A0F1A4B-C0D2-423C-9B9D-7E75B91B41EE} = {70515E66-DAF8-4D18-8F8F-8A2934171AA9} {99CBF75F-9204-4D7E-A792-4377C0291900} = {70515E66-DAF8-4D18-8F8F-8A2934171AA9} + {7EABA9A2-C459-4A6A-B5AF-218E6A59E635} = {A1DFF80C-113F-4FEC-84BB-1E3790FB410F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BD75F5C6-658B-40DF-A8F6-91C3EB76DAAA} diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props deleted file mode 100644 index faee085f..00000000 --- a/samples/Directory.Build.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - net8.0 - Exe - False - - \ No newline at end of file diff --git a/samples/Foundatio.AppHost/Foundatio.AppHost.csproj b/samples/Foundatio.AppHost/Foundatio.AppHost.csproj new file mode 100644 index 00000000..91dfcd62 --- /dev/null +++ b/samples/Foundatio.AppHost/Foundatio.AppHost.csproj @@ -0,0 +1,23 @@ + + + + + + Exe + net8.0 + enable + enable + true + 96cfcbc9-36fb-452f-9b99-0165197e1978 + False + + + + + + + + + + + diff --git a/samples/Foundatio.AppHost/Program.cs b/samples/Foundatio.AppHost/Program.cs new file mode 100644 index 00000000..916e0dfa --- /dev/null +++ b/samples/Foundatio.AppHost/Program.cs @@ -0,0 +1,9 @@ +using Projects; + +var builder = DistributedApplication.CreateBuilder(args); + +builder.AddProject("HostingSample") + .WithExternalHttpEndpoints() + .WithArgs("all"); + +builder.Build().Run(); diff --git a/samples/Foundatio.AppHost/Properties/launchSettings.json b/samples/Foundatio.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000..75277a87 --- /dev/null +++ b/samples/Foundatio.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17176;http://localhost:15249", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21196", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22113" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15249", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19180", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20234" + } + } + } +} diff --git a/samples/Foundatio.AppHost/appsettings.Development.json b/samples/Foundatio.AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/samples/Foundatio.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/Foundatio.AppHost/appsettings.json b/samples/Foundatio.AppHost/appsettings.json new file mode 100644 index 00000000..31c092aa --- /dev/null +++ b/samples/Foundatio.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj b/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj index 87276436..bd1dd06b 100644 --- a/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj +++ b/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj @@ -1,12 +1,24 @@ - + + + + net8.0 + False + + - + + + + + + - \ No newline at end of file + + diff --git a/samples/Foundatio.HostingSample/Program.cs b/samples/Foundatio.HostingSample/Program.cs index 3e0d0cfd..966e413a 100644 --- a/samples/Foundatio.HostingSample/Program.cs +++ b/samples/Foundatio.HostingSample/Program.cs @@ -4,166 +4,150 @@ using System.Threading; using System.Threading.Tasks; using Foundatio.Caching; -using Foundatio.Extensions.Hosting.Jobs; using Foundatio.Extensions.Hosting.Startup; +using Foundatio.Extensions.Hosting.Jobs; +using Foundatio.HostingSample; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Serilog; -using Serilog.Events; -namespace Foundatio.HostingSample; +Log.Logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateLogger(); -public class Program +try { - public static int Main(string[] args) + Log.Information("Starting web application"); + + bool all = args.Contains("all", StringComparer.OrdinalIgnoreCase); + bool sample1 = all || args.Contains("sample1", StringComparer.OrdinalIgnoreCase); + bool sample2 = all || args.Contains("sample2", StringComparer.OrdinalIgnoreCase); + bool everyMinute = all || args.Contains("everyMinute", StringComparer.OrdinalIgnoreCase); + bool evenMinutes = all || args.Contains("evenMinutes", StringComparer.OrdinalIgnoreCase); + + var builder = WebApplication.CreateBuilder(args); + + builder.AddServiceDefaults(); + + builder.Services.AddSerilog(); + + // shutdown the host if no jobs are running + builder.Services.AddJobLifetimeService(); + builder.Services.AddSingleton(_ => new InMemoryCacheClient()); + + // inserts a startup action that does not complete until the critical health checks are healthy + // gets inserted as 1st startup action so that any other startup actions don't run until the critical resources are available + builder.Services.AddStartupActionToWaitForHealthChecks("Critical"); + + builder.Services.AddHealthChecks().AddCheck("My Critical Resource", tags: ["Critical"]); + + // add health check that does not return healthy until the startup actions have completed + // useful for readiness checks + builder.Services.AddHealthChecks().AddCheckForStartupActions("Critical"); + + // this gets added automatically by any AddJob call, but we might not be running any jobs and we need it for doing dynamic jobs + builder.Services.AddJobScheduler(); + + if (everyMinute) + builder.Services.AddDistributedCronJob("* * * * *", j => j.Name(nameof(EveryMinuteJob))); + + if (evenMinutes) + builder.Services.AddCronJob("EvenMinutes", "*/2 * * * *", async sp => + { + var logger = sp.GetRequiredService>(); + if (logger.IsEnabled(LogLevel.Information)) + logger.LogInformation("EvenMinuteJob Run Thread={ManagedThreadId}", Thread.CurrentThread.ManagedThreadId); + + await Task.Delay(TimeSpan.FromSeconds(5)); + }); + + if (sample1) + builder.Services.AddJob("Sample1", sp => new Sample1Job(sp.GetRequiredService()), o => o.ApplyDefaults().WaitForStartupActions().InitialDelay(TimeSpan.FromSeconds(4))); + + if (sample2) { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information) - .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Warning) - .Enrich.FromLogContext() - .WriteTo.Console() - .CreateLogger(); - - try + builder.Services.AddHealthChecks().AddCheck("Sample2Job"); + builder.Services.AddJob(o => o.WaitForStartupActions()); + } + + // if you don't specify priority, actions will automatically be assigned an incrementing priority starting at 0 + builder.Services.AddStartupAction("Test1", async sp => + { + var logger = sp.GetRequiredService>(); + logger.LogTrace("Running startup 1 action"); + for (int i = 0; i < 3; i++) { - Log.Information("Starting host"); - CreateHostBuilder(args).Build().Run(); - return 0; + await Task.Delay(1000); + logger.LogTrace("Running startup 1 action..."); } - catch (Exception ex) + + logger.LogTrace("Done running startup 1 action"); + }); + + // then these startup actions will run concurrently since they both have the same priority + builder.Services.AddStartupAction(priority: 100); + builder.Services.AddStartupAction(priority: 100); + + builder.Services.AddStartupAction("Test2", async sp => + { + var logger = sp.GetRequiredService>(); + logger.LogTrace("Running startup 2 action"); + for (int i = 0; i < 2; i++) { - Log.Fatal(ex, "Host terminated unexpectedly"); - return 1; + await Task.Delay(1500); + logger.LogTrace("Running startup 2 action..."); } - finally - { - Log.CloseAndFlush(); + //throw new ApplicationException("Boom goes the startup"); + logger.LogTrace("Done running startup 2 action"); + }); - if (Debugger.IsAttached) - Console.ReadKey(); - } - } + //s.AddStartupAction("Boom", () => throw new ApplicationException("Boom goes the startup")); + + var app = builder.Build(); - public static IHostBuilder CreateHostBuilder(string[] args) + app.UseSerilogRequestLogging(); + + app.MapGet("/", () => "Foundatio!"); + + app.MapGet("/jobstatus", httpContext => { - bool all = args.Contains("all", StringComparer.OrdinalIgnoreCase); - bool sample1 = all || args.Contains("sample1", StringComparer.OrdinalIgnoreCase); - bool sample2 = all || args.Contains("sample2", StringComparer.OrdinalIgnoreCase); - bool everyMinute = all || args.Contains("everyMinute", StringComparer.OrdinalIgnoreCase); - bool evenMinutes = all || args.Contains("evenMinutes", StringComparer.OrdinalIgnoreCase); - - var builder = Host.CreateDefaultBuilder(args) - .UseSerilog() - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.Configure(app => - { - app.UseSerilogRequestLogging(); - - app.UseHealthChecks("/health"); - app.UseReadyHealthChecks("Critical"); - app.UseRouting(); - - app.UseEndpoints(e => - { - e.MapGet("/jobstatus", httpContext => - { - var jobManager = httpContext.RequestServices.GetRequiredService(); - var status = jobManager.GetJobStatus(); - return httpContext.Response.WriteAsJsonAsync(status); - }); - - e.MapGet("/runjob", async httpContext => - { - var jobManager = httpContext.RequestServices.GetRequiredService(); - await jobManager.RunJobAsync("EvenMinutes"); - await jobManager.RunJobAsync(); - }); - }); - - // this middleware will return Service Unavailable until the startup actions have completed - app.UseWaitForStartupActionsBeforeServingRequests(); - - // add mvc or other request middleware after the UseWaitForStartupActionsBeforeServingRequests call - }); - }) - .ConfigureServices(s => - { - // will shutdown the host if no jobs are running - s.AddJobLifetimeService(); - s.AddSingleton(_ => new InMemoryCacheClient()); - - // inserts a startup action that does not complete until the critical health checks are healthy - // gets inserted as 1st startup action so that any other startup actions dont run until the critical resources are available - s.AddStartupActionToWaitForHealthChecks("Critical"); - - s.AddHealthChecks().AddCheck("My Critical Resource", tags: ["Critical"]); - - // add health check that does not return healthy until the startup actions have completed - // useful for readiness checks - s.AddHealthChecks().AddCheckForStartupActions("Critical"); - - if (everyMinute) - s.AddDistributedCronJob("* * * * *"); - - if (evenMinutes) - s.AddCronJob("EvenMinutes", "*/2 * * * *", async sp => - { - var logger = sp.GetRequiredService>(); - if (logger.IsEnabled(LogLevel.Information)) - logger.LogInformation("EvenMinuteJob Run Thread={ManagedThreadId}", Thread.CurrentThread.ManagedThreadId); - - await Task.Delay(TimeSpan.FromSeconds(5)); - }); - - if (sample1) - s.AddJob("Sample1", sp => new Sample1Job(sp.GetRequiredService()), o => o.ApplyDefaults().WaitForStartupActions().InitialDelay(TimeSpan.FromSeconds(4))); - - if (sample2) - { - s.AddHealthChecks().AddCheck("Sample2Job"); - s.AddJob(o => o.WaitForStartupActions()); - } - - // if you don't specify priority, actions will automatically be assigned an incrementing priority starting at 0 - s.AddStartupAction("Test1", async sp => - { - var logger = sp.GetRequiredService>(); - logger.LogTrace("Running startup 1 action"); - for (int i = 0; i < 3; i++) - { - await Task.Delay(1000); - logger.LogTrace("Running startup 1 action..."); - } - - logger.LogTrace("Done running startup 1 action"); - }); - - // then these startup actions will run concurrently since they both have the same priority - s.AddStartupAction(priority: 100); - s.AddStartupAction(priority: 100); - - s.AddStartupAction("Test2", async sp => - { - var logger = sp.GetRequiredService>(); - logger.LogTrace("Running startup 2 action"); - for (int i = 0; i < 2; i++) - { - await Task.Delay(1500); - logger.LogTrace("Running startup 2 action..."); - } - //throw new ApplicationException("Boom goes the startup"); - logger.LogTrace("Done running startup 2 action"); - }); - - //s.AddStartupAction("Boom", () => throw new ApplicationException("Boom goes the startup")); - }); - - return builder; - } + var jobManager = httpContext.RequestServices.GetRequiredService(); + var status = jobManager.GetJobStatus(); + return httpContext.Response.WriteAsJsonAsync(status); + }); + + app.MapGet("/runjob", async httpContext => + { + var jobManager = httpContext.RequestServices.GetRequiredService(); + await jobManager.RunJobAsync("EvenMinutes"); + await jobManager.RunJobAsync(); + }); + + app.UseHealthChecks("/health"); + app.UseReadyHealthChecks("Critical"); + + // this middleware will return Service Unavailable until the startup actions have completed + app.UseWaitForStartupActionsBeforeServingRequests(); + + // add mvc or other request middleware after the UseWaitForStartupActionsBeforeServingRequests call + + app.Run(); +} +catch (Exception ex) +{ + Log.Fatal(ex, "Application terminated unexpectedly"); + return 1; } +finally +{ + Log.CloseAndFlush(); + + if (Debugger.IsAttached) + Console.ReadKey(); +} + +return 0; + diff --git a/samples/Foundatio.HostingSample/Properties/launchSettings.json b/samples/Foundatio.HostingSample/Properties/launchSettings.json index ae82a820..51e58647 100644 --- a/samples/Foundatio.HostingSample/Properties/launchSettings.json +++ b/samples/Foundatio.HostingSample/Properties/launchSettings.json @@ -1,8 +1,27 @@ { - "profiles": { - "Foundatio.HostingSample": { - "commandName": "Project", - "commandLineArgs": "all" + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "commandLineArgs": "all", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "jobstatus", + "applicationUrl": "http://localhost:5324", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "commandLineArgs": "all", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "jobstatus", + "applicationUrl": "https://localhost:7580;http://localhost:5324", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } } - } -} \ No newline at end of file +} diff --git a/samples/Foundatio.HostingSample/ServiceDefaults.cs b/samples/Foundatio.HostingSample/ServiceDefaults.cs new file mode 100644 index 00000000..fbd3284d --- /dev/null +++ b/samples/Foundatio.HostingSample/ServiceDefaults.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +public static class Extensions +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation() + .AddMeter("Foundatio"); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddSource("Foundatio"); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + return builder; + } +} diff --git a/src/Foundatio.Extensions.Hosting/Jobs/HostedJobService.cs b/src/Foundatio.Extensions.Hosting/Jobs/HostedJobService.cs index 99f5d8c2..7d9eca1a 100644 --- a/src/Foundatio.Extensions.Hosting/Jobs/HostedJobService.cs +++ b/src/Foundatio.Extensions.Hosting/Jobs/HostedJobService.cs @@ -52,7 +52,7 @@ private async Task ExecuteAsync(CancellationToken stoppingToken) try { - using var activity = FoundatioDiagnostics.ActivitySource.StartActivity("Job " + _jobOptions.Name); + using var activity = FoundatioDiagnostics.ActivitySource.StartActivity("Job: " + _jobOptions.Name); await runner.RunAsync(stoppingToken).AnyContext(); #if NET8_0_OR_GREATER diff --git a/src/Foundatio.Extensions.Hosting/Jobs/JobManager.cs b/src/Foundatio.Extensions.Hosting/Jobs/JobManager.cs index 87d46e78..1ef300d1 100644 --- a/src/Foundatio.Extensions.Hosting/Jobs/JobManager.cs +++ b/src/Foundatio.Extensions.Hosting/Jobs/JobManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Foundatio.Extensions.Hosting/Jobs/ScheduledJobService.cs b/src/Foundatio.Extensions.Hosting/Jobs/ScheduledJobService.cs index 85d11df3..04441f14 100644 --- a/src/Foundatio.Extensions.Hosting/Jobs/ScheduledJobService.cs +++ b/src/Foundatio.Extensions.Hosting/Jobs/ScheduledJobService.cs @@ -37,10 +37,18 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { foreach (var jobToRun in _jobManager.Jobs) { - using var activity = FoundatioDiagnostics.ActivitySource.StartActivity("Scheduled Job: " + jobToRun.Options.Name); + using var activity = FoundatioDiagnostics.ActivitySource.StartActivity("Job: " + jobToRun.Options.Name); if (await jobToRun.ShouldRunAsync()) + { await jobToRun.StartAsync(stoppingToken).AnyContext(); + } + else + { + // don't record trace if we didn't run the job and we started the root activity + if (activity is { Parent: null }) + activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded; + } } await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken).AnyContext(); diff --git a/src/Foundatio.Extensions.Hosting/Startup/StartupExtensions.cs b/src/Foundatio.Extensions.Hosting/Startup/StartupExtensions.cs index ff171865..e18695f1 100644 --- a/src/Foundatio.Extensions.Hosting/Startup/StartupExtensions.cs +++ b/src/Foundatio.Extensions.Hosting/Startup/StartupExtensions.cs @@ -54,6 +54,7 @@ await Task.WhenAll(startupActionGroup.Select(async a => { try { + using var activity = FoundatioDiagnostics.ActivitySource.StartActivity("Startup: " + a.Name); // ReSharper disable once AccessToDisposedClosure await a.RunAsync(startupActionsScope.ServiceProvider, shutdownToken).AnyContext(); }