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..b07e731 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -37,11 +37,29 @@ + + + + + + + + + + + + + + + + + + \ 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..aaa3acd --- /dev/null +++ b/src/NServiceBusTutorial.AspireHost/Program.cs @@ -0,0 +1,23 @@ +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 domainDb = postgres.AddDatabase("NServiceBusTutorial"); +var sagaDb = postgres.AddDatabase("SagaDb"); + +builder.AddProject("Web") + .WithReference(postgres) + .WaitFor(domainDb); + +builder.AddProject("Saga") + .WithReference(postgres) + .WaitFor(domainDb) + .WaitFor(sagaDb); + +builder.AddProject("Worker") + .WithReference(postgres) + .WaitFor(domainDb); + +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..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 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; 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.Infrastructure/NServiceBusTutorial.Infrastructure.csproj b/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj index ae5150a..ef56d58 100644 --- a/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj +++ b/src/NServiceBusTutorial.Infrastructure/NServiceBusTutorial.Infrastructure.csproj @@ -11,6 +11,12 @@ + + + + + + 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 +{ + services.AddSerilog((services, loggerConfiguration) => loggerConfiguration + .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(); @@ -37,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.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..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; }); @@ -100,8 +113,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) { 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..5d43968 100644 --- a/src/NServiceBusTutorial.Worker/Program.cs +++ b/src/NServiceBusTutorial.Worker/Program.cs @@ -6,6 +6,11 @@ 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(); @@ -25,11 +30,24 @@ 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()); + + 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"); @@ -40,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 ab06c07..a79eace 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" + } + ] } }