From 5e34ab74ec2c1e0c771ac8aa109c9e23afa6ef79 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Mon, 14 Apr 2025 15:35:28 -0400 Subject: [PATCH 1/6] Add Aspire projects and dependencies --- .vscode/launch.json | 18 +++ Directory.Packages.props | 13 ++ NServiceBusTutorial.sln | 30 +++++ docker-compose.development.yml | 10 -- .../NServiceBusTutorial.AspireHost.csproj | 32 +++++ src/NServiceBusTutorial.AspireHost/Program.cs | 21 ++++ .../Properties/launchSettings.json | 29 +++++ .../appsettings.json | 9 ++ .../Data/AppDbContext.cs | 5 +- .../Queries/ListContributorsQueryService.cs | 2 +- .../Data/SeedData.cs | 12 +- .../Extensions.cs | 118 ++++++++++++++++++ ...NServiceBusTutorial.ServiceDefaults.csproj | 22 ++++ src/NServiceBusTutorial.Web/Program.cs | 3 +- 14 files changed, 300 insertions(+), 24 deletions(-) delete mode 100644 docker-compose.development.yml create mode 100644 src/NServiceBusTutorial.AspireHost/NServiceBusTutorial.AspireHost.csproj create mode 100644 src/NServiceBusTutorial.AspireHost/Program.cs create mode 100644 src/NServiceBusTutorial.AspireHost/Properties/launchSettings.json create mode 100644 src/NServiceBusTutorial.AspireHost/appsettings.json create mode 100644 src/NServiceBusTutorial.ServiceDefaults/Extensions.cs create mode 100644 src/NServiceBusTutorial.ServiceDefaults/NServiceBusTutorial.ServiceDefaults.csproj diff --git a/.vscode/launch.json b/.vscode/launch.json index 29787f7..4668dab 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -89,6 +89,24 @@ }, } }, + { + "name": "Aspire: Run All (Debug)", + "type": "coreclr", + "preLaunchTask": "dotnet: build (Debug)", + "request": "launch", + "program": "${workspaceFolder}/src/NServiceBusTutorial.AspireHost/bin/Debug/net9.0/NServiceBusTutorial.AspireHost.dll", + "args": [], + "cwd": "${workspaceFolder}/src/NServiceBusTutorial.AspireHost/bin/Debug/net9.0", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart", + "env": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21007", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22245", + "ASPNETCORE_URLS": "https://localhost:17143" + }, + }, ], "compounds": [ { diff --git a/Directory.Packages.props b/Directory.Packages.props index a43026c..3144b2f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,4 +44,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/NServiceBusTutorial.sln b/NServiceBusTutorial.sln index 4ae58bb..638e6fb 100644 --- a/NServiceBusTutorial.sln +++ b/NServiceBusTutorial.sln @@ -41,6 +41,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NServiceBusTutorial.Worker" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBusTutorial.Saga", "src\NServiceBusTutorial.Saga\NServiceBusTutorial.Saga.csproj", "{B171BFFE-FE6D-4EA1-AB48-334072F89B40}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBusTutorial.AspireHost", "src\NServiceBusTutorial.AspireHost\NServiceBusTutorial.AspireHost.csproj", "{6002A3B9-6FB4-4E0F-89FB-044AB2655434}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NServiceBusTutorial.ServiceDefaults", "src\NServiceBusTutorial.ServiceDefaults\NServiceBusTutorial.ServiceDefaults.csproj", "{6DC1366E-F471-4145-9558-CD1B5EDFEB5D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -171,6 +175,30 @@ Global {B171BFFE-FE6D-4EA1-AB48-334072F89B40}.Release|x64.Build.0 = Release|Any CPU {B171BFFE-FE6D-4EA1-AB48-334072F89B40}.Release|x86.ActiveCfg = Release|Any CPU {B171BFFE-FE6D-4EA1-AB48-334072F89B40}.Release|x86.Build.0 = Release|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Debug|x64.ActiveCfg = Debug|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Debug|x64.Build.0 = Debug|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Debug|x86.ActiveCfg = Debug|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Debug|x86.Build.0 = Debug|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Release|Any CPU.Build.0 = Release|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Release|x64.ActiveCfg = Release|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Release|x64.Build.0 = Release|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Release|x86.ActiveCfg = Release|Any CPU + {6002A3B9-6FB4-4E0F-89FB-044AB2655434}.Release|x86.Build.0 = Release|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Debug|x64.ActiveCfg = Debug|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Debug|x64.Build.0 = Debug|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Debug|x86.ActiveCfg = Debug|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Debug|x86.Build.0 = Debug|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Release|Any CPU.Build.0 = Release|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Release|x64.ActiveCfg = Release|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Release|x64.Build.0 = Release|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Release|x86.ActiveCfg = Release|Any CPU + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -186,6 +214,8 @@ Global {7A8F52CA-113E-4461-8D91-80A1683DDC26} = {B31B4797-1D9F-4288-808C-BE9A31A98C7D} {558A7852-42FB-4867-B9AB-0D58F69CD7F6} = {106AE906-5075-410A-B941-912F811848EE} {B171BFFE-FE6D-4EA1-AB48-334072F89B40} = {106AE906-5075-410A-B941-912F811848EE} + {6002A3B9-6FB4-4E0F-89FB-044AB2655434} = {106AE906-5075-410A-B941-912F811848EE} + {6DC1366E-F471-4145-9558-CD1B5EDFEB5D} = {106AE906-5075-410A-B941-912F811848EE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0F19343-8185-4A9F-9165-0EA8544BC925} diff --git a/docker-compose.development.yml b/docker-compose.development.yml deleted file mode 100644 index 055b338..0000000 --- a/docker-compose.development.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - postgres-db: - image: postgres:latest - container_name: postgres.db - ports: - - 5432:5432 - environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres - - POSTGRES_DB=NServiceBusTutorial \ No newline at end of file diff --git a/src/NServiceBusTutorial.AspireHost/NServiceBusTutorial.AspireHost.csproj b/src/NServiceBusTutorial.AspireHost/NServiceBusTutorial.AspireHost.csproj new file mode 100644 index 0000000..d4e5a3a --- /dev/null +++ b/src/NServiceBusTutorial.AspireHost/NServiceBusTutorial.AspireHost.csproj @@ -0,0 +1,32 @@ + + + + + + Exe + net9.0 + enable + enable + true + c540eeb6-e06b-4456-a539-be58dd8b88c7 + + + + + + + + + + + + + + + + + + + diff --git a/src/NServiceBusTutorial.AspireHost/Program.cs b/src/NServiceBusTutorial.AspireHost/Program.cs new file mode 100644 index 0000000..804b97f --- /dev/null +++ b/src/NServiceBusTutorial.AspireHost/Program.cs @@ -0,0 +1,21 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var username = builder.AddParameter("username", "postgres",secret: false); +var password = builder.AddParameter("password", "postgres",secret: false); + +var postgres = builder.AddPostgres("postgres", username, password, 5432); +var db = postgres.AddDatabase("NServiceBusTutorial"); + +builder.AddProject("Web") + .WithReference(postgres) + .WaitFor(db); + +builder.AddProject("Saga") + .WithReference(postgres) + .WaitFor(db); + +builder.AddProject("Worker") + .WithReference(postgres) + .WaitFor(db); + +builder.Build().Run(); diff --git a/src/NServiceBusTutorial.AspireHost/Properties/launchSettings.json b/src/NServiceBusTutorial.AspireHost/Properties/launchSettings.json new file mode 100644 index 0000000..f3d5d86 --- /dev/null +++ b/src/NServiceBusTutorial.AspireHost/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:17143;http://localhost:15258", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21007", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22245" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15258", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19187", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20134" + } + } + } +} diff --git a/src/NServiceBusTutorial.AspireHost/appsettings.json b/src/NServiceBusTutorial.AspireHost/appsettings.json new file mode 100644 index 0000000..31c092a --- /dev/null +++ b/src/NServiceBusTutorial.AspireHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/src/NServiceBusTutorial.Infrastructure/Data/AppDbContext.cs b/src/NServiceBusTutorial.Infrastructure/Data/AppDbContext.cs index 1c485d4..789c0fd 100644 --- a/src/NServiceBusTutorial.Infrastructure/Data/AppDbContext.cs +++ b/src/NServiceBusTutorial.Infrastructure/Data/AppDbContext.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using Ardalis.SharedKernel; +using Ardalis.SharedKernel; using NServiceBusTutorial.Core.ContributorAggregate; using Microsoft.EntityFrameworkCore; @@ -21,7 +20,7 @@ public AppDbContext(DbContextOptions options, protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); } public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) diff --git a/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs b/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs index a41fc25..04d4a84 100644 --- a/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs +++ b/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs @@ -13,7 +13,7 @@ public async Task> ListAsync() { // NOTE: This will fail if testing with EF InMemory provider! var result = await _db.Database.SqlQuery( - $"SELECT Id, Name, PhoneNumber_Number AS PhoneNumber, Verification FROM Contributors") // don't fetch other big columns + $"SELECT Id, Name, PhoneNumber_Number AS PhoneNumber, Verification FROM public.Contributors") // don't fetch other big columns .ToListAsync(); return result; diff --git a/src/NServiceBusTutorial.Infrastructure/Data/SeedData.cs b/src/NServiceBusTutorial.Infrastructure/Data/SeedData.cs index 0bcc574..df7301a 100644 --- a/src/NServiceBusTutorial.Infrastructure/Data/SeedData.cs +++ b/src/NServiceBusTutorial.Infrastructure/Data/SeedData.cs @@ -1,6 +1,4 @@ using NServiceBusTutorial.Core.ContributorAggregate; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace NServiceBusTutorial.Infrastructure.Data; @@ -9,15 +7,11 @@ public static class SeedData public static readonly Contributor Contributor1 = new("Ardalis", new(string.Empty, "123-456-7890", string.Empty), ContributorStatus.CoreTeam, VerificationStatus.Pending); public static readonly Contributor Contributor2 = new("Snowfrog", new(string.Empty, "098-765-4321", string.Empty), ContributorStatus.Community, VerificationStatus.Pending); - public static void Initialize(IServiceProvider serviceProvider) + public static void Initialize(IServiceProvider serviceProvider, AppDbContext dbContext) { - using (var dbContext = new AppDbContext( - serviceProvider.GetRequiredService>(), null)) - { - if (dbContext.Contributors.Any()) return; // DB has been seeded + if (dbContext.Contributors.Any()) return; // DB has been seeded - PopulateTestData(dbContext); - } + PopulateTestData(dbContext); } public static void PopulateTestData(AppDbContext dbContext) { diff --git a/src/NServiceBusTutorial.ServiceDefaults/Extensions.cs b/src/NServiceBusTutorial.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..87808a3 --- /dev/null +++ b/src/NServiceBusTutorial.ServiceDefaults/Extensions.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + 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(); + }) + .WithTracing(tracing => + { + tracing.AddAspNetCoreInstrumentation() + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + 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(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks("/health"); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks("/alive", new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/src/NServiceBusTutorial.ServiceDefaults/NServiceBusTutorial.ServiceDefaults.csproj b/src/NServiceBusTutorial.ServiceDefaults/NServiceBusTutorial.ServiceDefaults.csproj new file mode 100644 index 0000000..09110f1 --- /dev/null +++ b/src/NServiceBusTutorial.ServiceDefaults/NServiceBusTutorial.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/src/NServiceBusTutorial.Web/Program.cs b/src/NServiceBusTutorial.Web/Program.cs index 82c5ae6..1abd993 100644 --- a/src/NServiceBusTutorial.Web/Program.cs +++ b/src/NServiceBusTutorial.Web/Program.cs @@ -100,8 +100,9 @@ static void SeedDatabase(WebApplication app) { var context = services.GetRequiredService(); // context.Database.Migrate(); + context.Database.EnsureDeleted(); context.Database.EnsureCreated(); - SeedData.Initialize(services); + SeedData.Initialize(services, context); } catch (Exception ex) { From c21e734c902c91233c91009d08f14f3e9382df63 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Tue, 20 May 2025 13:54:17 -0400 Subject: [PATCH 2/6] update list service to use AppDbContext --- .../Data/Queries/ListContributorsQueryService.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs b/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs index 04d4a84..58b1698 100644 --- a/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs +++ b/src/NServiceBusTutorial.Infrastructure/Data/Queries/ListContributorsQueryService.cs @@ -12,8 +12,12 @@ public class ListContributorsQueryService(AppDbContext _db) : IListContributorsQ public async Task> ListAsync() { // NOTE: This will fail if testing with EF InMemory provider! - var result = await _db.Database.SqlQuery( - $"SELECT Id, Name, PhoneNumber_Number AS PhoneNumber, Verification FROM public.Contributors") // don't fetch other big columns + // var result = await _db.Database.SqlQuery( + // $"SELECT Id, Name, PhoneNumber_Number AS PhoneNumber, Verification FROM public.Contributors") // don't fetch other big columns + // .ToListAsync(); + var result = await _db.Contributors + .AsNoTracking() + .Select(c => new ContributorDTO(c.Id, c.Name, c.PhoneNumber!.Number, c.Verification.Name)) .ToListAsync(); return result; From f51d4f98be9034ffb7b3f848eeec314d08563872 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Tue, 20 May 2025 15:12:23 -0400 Subject: [PATCH 3/6] Enable structured logging with Serilog --- Directory.Packages.props | 4 ++++ .../NServiceBusTutorial.Infrastructure.csproj | 4 ++++ src/NServiceBusTutorial.Web/appsettings.json | 4 ++++ src/NServiceBusTutorial.Worker/Program.cs | 6 ++++++ .../appsettings.json | 21 +++++++++++++++++++ 5 files changed, 39 insertions(+) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3144b2f..335bd58 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -39,6 +39,10 @@ + + + + diff --git a/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj b/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj index ae5150a..c193f1c 100644 --- a/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj +++ b/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj @@ -11,6 +11,10 @@ + + + + diff --git a/src/NServiceBusTutorial.Web/appsettings.json b/src/NServiceBusTutorial.Web/appsettings.json index e30447e..8c2e31d 100644 --- a/src/NServiceBusTutorial.Web/appsettings.json +++ b/src/NServiceBusTutorial.Web/appsettings.json @@ -4,6 +4,7 @@ , "SqliteConnection": "Data Source=database.sqlite" }, "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.OpenTelemetry" ], "MinimumLevel": { "Default": "Information" }, @@ -17,6 +18,9 @@ "path": "log.txt", "rollingInterval": "Day" } + }, + { + "Name": "OpenTelemetry" } //Uncomment this section if you'd like to push your logs to Azure Application Insights //Full list of Serilog Sinks can be found here: https://github.com/serilog/serilog/wiki/Provided-Sinks diff --git a/src/NServiceBusTutorial.Worker/Program.cs b/src/NServiceBusTutorial.Worker/Program.cs index 6e2de1e..6fb2a4c 100644 --- a/src/NServiceBusTutorial.Worker/Program.cs +++ b/src/NServiceBusTutorial.Worker/Program.cs @@ -6,6 +6,7 @@ using NServiceBusTutorial.Infrastructure.Data; using NServiceBusTutorial.Infrastructure.Notifications; using NServiceBusTutorial.Worker.Contributors; +using Serilog; var builder = Host.CreateDefaultBuilder(); @@ -25,6 +26,11 @@ services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies([typeof(ContributorCreateCommandHandler).Assembly])); services.AddScoped(); services.AddScoped(); + + services.AddSerilog((services, loggerConfiguration) => loggerConfiguration + .ReadFrom.Configuration(hostContext.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext()); }); builder.UseNServiceBus(context => diff --git a/src/NServiceBusTutorial.Worker/appsettings.json b/src/NServiceBusTutorial.Worker/appsettings.json index ab06c07..a0bbeb4 100644 --- a/src/NServiceBusTutorial.Worker/appsettings.json +++ b/src/NServiceBusTutorial.Worker/appsettings.json @@ -8,5 +8,26 @@ "Default": "Information", "Microsoft.Hosting.Lifetime": "Information" } + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.OpenTelemetry" ], + "MinimumLevel": { + "Default": "Information" + }, + "WriteTo": [ + { + "Name": "Console" + }, + { + "Name": "File", + "Args": { + "path": "log.txt", + "rollingInterval": "Day" + } + }, + { + "Name": "OpenTelemetry" + } + ] } } From 51c8110242d23ccc332e3ec75f3b3e8cfeef3ab3 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Tue, 20 May 2025 15:18:28 -0400 Subject: [PATCH 4/6] update aspire packages --- Directory.Packages.props | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 335bd58..0b43be5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -50,15 +50,15 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file From 1bd65095bcb639eb52b8d4d06e93e4eb1e04568a Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Tue, 20 May 2025 15:28:00 -0400 Subject: [PATCH 5/6] Add serilog to Saga project --- src/NServiceBusTutorial.Saga/Program.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/NServiceBusTutorial.Saga/Program.cs b/src/NServiceBusTutorial.Saga/Program.cs index 5e9bec9..c60627b 100644 --- a/src/NServiceBusTutorial.Saga/Program.cs +++ b/src/NServiceBusTutorial.Saga/Program.cs @@ -1,9 +1,18 @@ using Npgsql; using NpgsqlTypes; using NServiceBusTutorial.Core.ContributorAggregate.Commands; +using Serilog; var builder = Host.CreateDefaultBuilder(args); +builder.ConfigureServices((hostContext, services) => +{ + services.AddSerilog((services, loggerConfiguration) => loggerConfiguration + .ReadFrom.Configuration(hostContext.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext()); +}); + builder.UseNServiceBus(context => { var endpointConfiguration = new EndpointConfiguration("contributors-saga"); From 0a172c5285f29ec68b30f59af1af65f5a5f63a03 Mon Sep 17 00:00:00 2001 From: Kyle McMaster Date: Tue, 20 May 2025 16:35:57 -0400 Subject: [PATCH 6/6] Add OpenTelemetry and update Aspire dependency graph for Saga database --- Directory.Packages.props | 1 + src/NServiceBusTutorial.AspireHost/Program.cs | 10 ++++---- .../NServiceBusTutorial.Infrastructure.csproj | 2 ++ .../ContributorVerificationSaga.cs | 2 ++ src/NServiceBusTutorial.Saga/Program.cs | 15 +++++++++++- src/NServiceBusTutorial.Saga/appsettings.json | 23 ++++++++++++++++++- src/NServiceBusTutorial.Web/Program.cs | 15 +++++++++++- src/NServiceBusTutorial.Worker/Program.cs | 16 ++++++++++++- .../appsettings.json | 2 +- 9 files changed, 77 insertions(+), 9 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0b43be5..b07e731 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -37,6 +37,7 @@ + diff --git a/src/NServiceBusTutorial.AspireHost/Program.cs b/src/NServiceBusTutorial.AspireHost/Program.cs index 804b97f..aaa3acd 100644 --- a/src/NServiceBusTutorial.AspireHost/Program.cs +++ b/src/NServiceBusTutorial.AspireHost/Program.cs @@ -4,18 +4,20 @@ var password = builder.AddParameter("password", "postgres",secret: false); var postgres = builder.AddPostgres("postgres", username, password, 5432); -var db = postgres.AddDatabase("NServiceBusTutorial"); +var domainDb = postgres.AddDatabase("NServiceBusTutorial"); +var sagaDb = postgres.AddDatabase("SagaDb"); builder.AddProject("Web") .WithReference(postgres) - .WaitFor(db); + .WaitFor(domainDb); builder.AddProject("Saga") .WithReference(postgres) - .WaitFor(db); + .WaitFor(domainDb) + .WaitFor(sagaDb); builder.AddProject("Worker") .WithReference(postgres) - .WaitFor(db); + .WaitFor(domainDb); builder.Build().Run(); diff --git a/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj b/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj index c193f1c..ef56d58 100644 --- a/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj +++ b/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj @@ -11,6 +11,8 @@ + + diff --git a/src/NServiceBusTutorial.Saga/ContributorVerificationSaga.cs b/src/NServiceBusTutorial.Saga/ContributorVerificationSaga.cs index ebd8f58..001c92f 100644 --- a/src/NServiceBusTutorial.Saga/ContributorVerificationSaga.cs +++ b/src/NServiceBusTutorial.Saga/ContributorVerificationSaga.cs @@ -1,5 +1,6 @@ using NServiceBusTutorial.Core.ContributorAggregate.Commands; using NServiceBusTutorial.Core.ContributorAggregate.Events; +using Serilog; namespace NServiceBusTutorial.Saga; @@ -17,6 +18,7 @@ protected override void ConfigureHowToFindSaga(SagaPropertyMapper @@ -11,11 +15,19 @@ .ReadFrom.Configuration(hostContext.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext()); + + services.AddOpenTelemetry() + .ConfigureResource(resourceBuilder => resourceBuilder.AddService(endpointName)) + .WithTracing(builder => + { + builder.AddSource("NServiceBus.*"); + builder.AddConsoleExporter(); + }); }); builder.UseNServiceBus(context => { - var endpointConfiguration = new EndpointConfiguration("contributors-saga"); + var endpointConfiguration = new EndpointConfiguration(endpointName); endpointConfiguration.UseSerialization(); endpointConfiguration.EnableInstallers(); @@ -46,6 +58,7 @@ return new NpgsqlConnection(connectionString); }); endpointConfiguration.EnableInstallers(); + endpointConfiguration.EnableOpenTelemetry(); var recoverability = endpointConfiguration.Recoverability(); recoverability.Immediate(c => c.NumberOfRetries(0)); diff --git a/src/NServiceBusTutorial.Saga/appsettings.json b/src/NServiceBusTutorial.Saga/appsettings.json index 79365c2..32bf7ae 100644 --- a/src/NServiceBusTutorial.Saga/appsettings.json +++ b/src/NServiceBusTutorial.Saga/appsettings.json @@ -1,11 +1,32 @@ { "ConnectionStrings": { - "DefaultConnection": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=NServiceBusTutorial;Pooling=true;MinPoolSize=1;MaxPoolSize=100;Timeout=15" + "DefaultConnection": "User ID=postgres;Password=postgres;Host=localhost;Port=5432;Database=SagaDb;Pooling=true;MinPoolSize=1;MaxPoolSize=100;Timeout=15" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.Hosting.Lifetime": "Information" } + }, + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.OpenTelemetry" ], + "MinimumLevel": { + "Default": "Information" + }, + "WriteTo": [ + { + "Name": "Console" + }, + { + "Name": "File", + "Args": { + "path": "logs/log.txt", + "rollingInterval": "Day" + } + }, + { + "Name": "OpenTelemetry" + } + ] } } diff --git a/src/NServiceBusTutorial.Web/Program.cs b/src/NServiceBusTutorial.Web/Program.cs index 1abd993..932096e 100644 --- a/src/NServiceBusTutorial.Web/Program.cs +++ b/src/NServiceBusTutorial.Web/Program.cs @@ -12,6 +12,10 @@ using Serilog.Extensions.Logging; using NServiceBusTutorial.Core.ContributorAggregate.Commands; using NServiceBusTutorial.Core.ContributorAggregate.Events; +using OpenTelemetry.Trace; +using OpenTelemetry.Resources; + +string endpointName = "contributors-api"; var logger = Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() @@ -26,6 +30,14 @@ var microsoftLogger = new SerilogLoggerFactory(logger) .CreateLogger(); +builder.Services.AddOpenTelemetry() + .ConfigureResource(resourceBuilder => resourceBuilder.AddService(endpointName)) + .WithTracing(builder => + { + builder.AddSource("NServiceBus.*"); + builder.AddConsoleExporter(); + }); + // Configure Web Behavior builder.Services.Configure(options => { @@ -47,7 +59,7 @@ builder.Host.UseNServiceBus(context => { - var endpointConfiguration = new EndpointConfiguration("contributors-api"); + var endpointConfiguration = new EndpointConfiguration(endpointName); endpointConfiguration.UseSerialization(); endpointConfiguration.EnableInstallers(); @@ -65,6 +77,7 @@ endpointConfiguration.SendOnly(); endpointConfiguration.EnableInstallers(); + endpointConfiguration.EnableOpenTelemetry(); return endpointConfiguration; }); diff --git a/src/NServiceBusTutorial.Worker/Program.cs b/src/NServiceBusTutorial.Worker/Program.cs index 6fb2a4c..5d43968 100644 --- a/src/NServiceBusTutorial.Worker/Program.cs +++ b/src/NServiceBusTutorial.Worker/Program.cs @@ -6,8 +6,12 @@ using NServiceBusTutorial.Infrastructure.Data; using NServiceBusTutorial.Infrastructure.Notifications; using NServiceBusTutorial.Worker.Contributors; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; using Serilog; +string endpointName = "contributors-worker"; + var builder = Host.CreateDefaultBuilder(); builder.UseConsoleLifetime(); @@ -31,11 +35,19 @@ .ReadFrom.Configuration(hostContext.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext()); + + services.AddOpenTelemetry() + .ConfigureResource(resourceBuilder => resourceBuilder.AddService(endpointName)) + .WithTracing(builder => + { + builder.AddSource("NServiceBus.*"); + builder.AddConsoleExporter(); + }); }); builder.UseNServiceBus(context => { - var endpointConfiguration = new EndpointConfiguration("contributors-worker"); + var endpointConfiguration = new EndpointConfiguration(endpointName); endpointConfiguration.UseSerialization(); endpointConfiguration.EnableInstallers(); endpointConfiguration.SendFailedMessagesTo("error"); @@ -46,6 +58,8 @@ typeof(StartContributorVerificationCommand), "contributors-saga"); + endpointConfiguration.EnableOpenTelemetry(); + var recoverability = endpointConfiguration.Recoverability(); recoverability.Immediate(c => c.NumberOfRetries(0)); recoverability.Delayed(c => c.NumberOfRetries(0)); diff --git a/src/NServiceBusTutorial.Worker/appsettings.json b/src/NServiceBusTutorial.Worker/appsettings.json index a0bbeb4..a79eace 100644 --- a/src/NServiceBusTutorial.Worker/appsettings.json +++ b/src/NServiceBusTutorial.Worker/appsettings.json @@ -9,7 +9,7 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "Serilog": { + "Serilog": { "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.OpenTelemetry" ], "MinimumLevel": { "Default": "Information"