diff --git a/Directory.Packages.props b/Directory.Packages.props index 5e02d448..401887bb 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -24,22 +24,21 @@ - - + + + + + - - - - - - - - - + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 83a864c0..ba5dcc4d 100644 --- a/README.md +++ b/README.md @@ -33,64 +33,8 @@ It showcases: 1. Learn more about [dotnet-ef](https://learn.microsoft.com/en-us/ef/core/cli/dotnet) ### Running the application -To run the application, run both the [Todo.Web/Server](Todo.Web/Server) and [TodoApi](TodoApi). Below are different ways to run both applications: - - **Visual Studio** - Setup multiple startup projects by right clicking on the solution and selecting Properties. Select `TodoApi` and `Todo.Web.Server` as startup projects. - - image - - - **Visual Studio Code** - A `compound` launch definition is provided in the `launch.json` file, use the 'Run and Debug' view and select 'Web and API' and start that profile. - - ![image](https://github.com/timheuer/TodoApi/assets/4821/dfa83682-86c4-45ae-965b-fa6f6a6f07a9) - - - - **Terminal/CLI** - Open up 2 terminal windows, one in [Todo.Web.Server](Todo.Web/Server/) and the other in [TodoApi](TodoApi/) run: - - ``` - dotnet watch run -lp https - ``` - - This will run both applications with the `https` profile. - - - **Tye** - Install the global tool using the following command: - - ``` - dotnet tool install --global Microsoft.Tye --version 0.11.0-alpha.22111.1 - ``` - - Run `tye run` in the repository root and navigate to the tye dashboard (usually http://localhost:8000) to see both applications running. - - - **Docker Compose** - Open your terminal, navigate to the root folder of this project and run the following commands: - 1. Build a docker image for the `TodoApi` directly from dotnet publish. - ``` - dotnet publish ./TodoApi/TodoApi.csproj --os linux --arch x64 /t:PublishContainer -c Release - ``` - - 1. Build a docker image for the `Todo.Web.Server` directly from dotnet publish. - ``` - dotnet publish ./Todo.Web/Server/Todo.Web.Server.csproj --os linux --arch x64 /t:PublishContainer -c Release --self-contained true - ``` - - 1. Generate certificate and configure local machine so we can start our apps with https support using docker compose. - - Windows using Linux containers - ``` - set PASSWORD YourPasswordHere - dotnet dev-certs https -ep ${HOME}/.aspnet/https/todoapps.pfx -p $PASSWORD --trust - ``` - macOS or Linux - ``` - export PASSWORD=YourPasswordHere - dotnet dev-certs https -ep ~/.aspnet/https/todoapps.pfx -p $PASSWORD --trust - ``` - - 1. Change these variables below in the `docker-compose.yml` file to match your https certificate and password. - - `ASPNETCORE_Kestrel__Certificates__Default__Password` - - `ASPNETCORE_Kestrel__Certificates__Default__Path` - - 1. Run `docker-compose up -d` to spin up both apps todo-api and todo-web-server plus jaeger and prometheus. - - 1. Navigate to the Todo Web app https://localhost:5003. +To run the application, run the [TodoApi.AppHost](TodoApi.AppHost) project. This uses .NET Aspire to run both the [Todo.Web/Server](Todo.Web/Server) and [TodoApi](TodoApi). ## Optional @@ -188,26 +132,6 @@ This sample has **Auth0** configured as an OIDC server. It can be configured wit Learn more about the Auth0 .NET SDK [here](https://github.com/auth0/auth0-aspnetcore-authentication). ### OpenTelemetry -TodoApi uses OpenTelemetry to collect logs, metrics and spans. - -If you wish to view the collected telemetry, follow the steps below. - -#### Metrics -1. Run Prometheus with Docker: -``` -docker run -d -p 9090:9090 --name prometheus -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus -``` -1. Open [Prometheus in your browser](http://localhost:9090/) -1. Query the collected metrics - -#### Spans - -1. Configure environment variable `OTEL_EXPORTER_OTLP_ENDPOINT` with the right endpoint URL to enable `.AddOtlpExporter` below `builder.Services.AddOpenTelemetryTracing`, in the `TodoApi/OpenTelemetryExtensions.cs` file -1. Run Jaeger with Docker: - -``` -docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 -e COLLECTOR_OTLP_ENABLED=true -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest -``` -1. Open [Jaeger in your browser](http://localhost:16686/) -1. View the collected spans +TodoApi uses OpenTelemetry to collect logs, metrics and spans. You can see this +using the [Aspire Dashboard](https://aspiredashboard.com/). diff --git a/Todo.Web/Server/AuthApi.cs b/Todo.Web/Server/AuthApi.cs index 2f6480c2..05b0939c 100644 --- a/Todo.Web/Server/AuthApi.cs +++ b/Todo.Web/Server/AuthApi.cs @@ -57,7 +57,7 @@ public static RouteGroupBuilder MapAuth(this IEndpointRouteBuilder routes) // This name maps to the registered authentication scheme names in AuthenticationExtensions.cs return Results.Challenge( properties: new() { RedirectUri = $"/auth/signin/{provider}" }, - authenticationSchemes: new[] { provider }); + authenticationSchemes: [provider]); }); group.MapGet("signin/{provider}", async (string provider, AuthClient client, HttpContext context) => diff --git a/Todo.Web/Server/Program.cs b/Todo.Web/Server/Program.cs index 776e903a..2825e92a 100644 --- a/Todo.Web/Server/Program.cs +++ b/Todo.Web/Server/Program.cs @@ -3,6 +3,8 @@ var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Configure auth with the front end builder.AddAuthentication(); builder.Services.AddAuthorizationBuilder(); @@ -14,20 +16,18 @@ .AddInteractiveWebAssemblyComponents(); // Add the forwarder to make sending requests to the backend easier -builder.Services.AddHttpForwarder(); - -var todoUrl = builder.Configuration.GetServiceUri("todoapi")?.ToString() ?? - builder.Configuration["TodoApiUrl"] ?? - throw new InvalidOperationException("Todo API URL is not configured"); +builder.Services.AddHttpForwarderWithServiceDiscovery(); // Configure the HttpClient for the backend API builder.Services.AddHttpClient(client => { - client.BaseAddress = new(todoUrl); + client.BaseAddress = new("http://todoapi"); }); var app = builder.Build(); +app.MapDefaultEndpoints(); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { @@ -48,7 +48,7 @@ // Configure the APIs app.MapAuth(); -app.MapTodos(todoUrl); +app.MapTodos(); app.Run(); diff --git a/Todo.Web/Server/Todo.Web.Server.csproj b/Todo.Web/Server/Todo.Web.Server.csproj index cbe9f771..7ba10bdd 100644 --- a/Todo.Web/Server/Todo.Web.Server.csproj +++ b/Todo.Web/Server/Todo.Web.Server.csproj @@ -14,10 +14,11 @@ - + + diff --git a/Todo.Web/Server/TodoApi.cs b/Todo.Web/Server/TodoApi.cs index 13304884..5fa74ca4 100644 --- a/Todo.Web/Server/TodoApi.cs +++ b/Todo.Web/Server/TodoApi.cs @@ -1,13 +1,12 @@ using Microsoft.AspNetCore.Authentication; using Yarp.ReverseProxy.Forwarder; using Yarp.ReverseProxy.Transforms; -using Yarp.ReverseProxy.Transforms.Builder; namespace Todo.Web.Server; public static class TodoApi { - public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes, string todoUrl) + public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes) { // The todo API translates the authentication cookie between the browser the BFF into an // access token that is sent to the todo API. We're using YARP to forward the request. @@ -16,7 +15,7 @@ public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes, stri group.RequireAuthorization(); - group.MapForwarder("{*path}", todoUrl, new ForwarderRequestConfig(), b => + group.MapForwarder("{*path}", "http://todoapi", new ForwarderRequestConfig(), b => { b.AddRequestTransform(async c => { diff --git a/TodoApi.AppHost/Program.cs b/TodoApi.AppHost/Program.cs new file mode 100644 index 00000000..f8fc8c81 --- /dev/null +++ b/TodoApi.AppHost/Program.cs @@ -0,0 +1,8 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var todoapi = builder.AddProject("todoapi"); + +builder.AddProject("todo-web-server") + .WithReference(todoapi); + +builder.Build().Run(); diff --git a/TodoApi.AppHost/Properties/launchSettings.json b/TodoApi.AppHost/Properties/launchSettings.json new file mode 100644 index 00000000..45004599 --- /dev/null +++ b/TodoApi.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:17249;http://localhost:15169", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21226", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22195" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15169", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19224", + "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20024" + } + } + } +} diff --git a/TodoApi.AppHost/TodoApi.AppHost.csproj b/TodoApi.AppHost/TodoApi.AppHost.csproj new file mode 100644 index 00000000..ced1d6ec --- /dev/null +++ b/TodoApi.AppHost/TodoApi.AppHost.csproj @@ -0,0 +1,23 @@ + + + + + + Exe + net8.0 + enable + enable + true + 22f8485f-c345-41fa-9f21-ba96b1e511d1 + + + + + + + + + + + + diff --git a/TodoApi.AppHost/appsettings.Development.json b/TodoApi.AppHost/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/TodoApi.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/TodoApi.AppHost/appsettings.json b/TodoApi.AppHost/appsettings.json new file mode 100644 index 00000000..31c092aa --- /dev/null +++ b/TodoApi.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/TodoApi.ServiceDefaults/Extensions.cs b/TodoApi.ServiceDefaults/Extensions.cs new file mode 100644 index 00000000..13151bf4 --- /dev/null +++ b/TodoApi.ServiceDefaults/Extensions.cs @@ -0,0 +1,119 @@ +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.AddSource(builder.Environment.ApplicationName) + .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/TodoApi.ServiceDefaults/TodoApi.ServiceDefaults.csproj b/TodoApi.ServiceDefaults/TodoApi.ServiceDefaults.csproj new file mode 100644 index 00000000..4f74b4fb --- /dev/null +++ b/TodoApi.ServiceDefaults/TodoApi.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/TodoApi.sln b/TodoApi.sln index 9fbd068f..fc505a41 100644 --- a/TodoApi.sln +++ b/TodoApi.sln @@ -11,9 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props Directory.Packages.props = Directory.Packages.props - docker-compose.yml = docker-compose.yml README.md = README.md - tye.yaml = tye.yaml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo.Web.Server", "Todo.Web\Server\Todo.Web.Server.csproj", "{9459B25A-3A37-43C4-A7DE-6113C1E2CDD7}" @@ -22,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo.Web.Client", "Todo.Web EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Todo.Web.Shared", "Todo.Web\Shared\Todo.Web.Shared.csproj", "{272942F6-94E8-4D6B-8AD8-C4CCA305836D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApi.AppHost", "TodoApi.AppHost\TodoApi.AppHost.csproj", "{4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TodoApi.ServiceDefaults", "TodoApi.ServiceDefaults\TodoApi.ServiceDefaults.csproj", "{67B571B3-F4FB-46AA-888E-17A5A773610C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,6 +50,14 @@ Global {272942F6-94E8-4D6B-8AD8-C4CCA305836D}.Debug|Any CPU.Build.0 = Debug|Any CPU {272942F6-94E8-4D6B-8AD8-C4CCA305836D}.Release|Any CPU.ActiveCfg = Release|Any CPU {272942F6-94E8-4D6B-8AD8-C4CCA305836D}.Release|Any CPU.Build.0 = Release|Any CPU + {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4ECB456A-BC4A-4F99-A1D5-FE2DF1C4EA1D}.Release|Any CPU.Build.0 = Release|Any CPU + {67B571B3-F4FB-46AA-888E-17A5A773610C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67B571B3-F4FB-46AA-888E-17A5A773610C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67B571B3-F4FB-46AA-888E-17A5A773610C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67B571B3-F4FB-46AA-888E-17A5A773610C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TodoApi/Extensions/OpenApiExtensions.cs b/TodoApi/Extensions/OpenApiExtensions.cs index c997e08b..2d544bd9 100644 --- a/TodoApi/Extensions/OpenApiExtensions.cs +++ b/TodoApi/Extensions/OpenApiExtensions.cs @@ -32,7 +32,7 @@ public static IEndpointConventionBuilder AddOpenApiSecurity(this IEndpointConven { new() { - [BearerScheme] = Array.Empty() + [BearerScheme] = [] } } }); diff --git a/TodoApi/Extensions/OpenTelemetryExtensions.cs b/TodoApi/Extensions/OpenTelemetryExtensions.cs deleted file mode 100644 index c78a4265..00000000 --- a/TodoApi/Extensions/OpenTelemetryExtensions.cs +++ /dev/null @@ -1,75 +0,0 @@ -using OpenTelemetry; -using OpenTelemetry.Logs; -using OpenTelemetry.Metrics; -using OpenTelemetry.Resources; -using OpenTelemetry.Trace; - -namespace TodoApi; - -public static class OpenTelemetryExtensions -{ - - /// - /// Configures logging, distributed tracing, and metrics - /// - /// Distributed tracing uses the OTLP Exporter, which can be viewed with Jaeger - /// Metrics uses the Prometheus Exporter - /// Logging can use the OTLP Exporter, but due to limited vendor support it is not enabled by default - /// - /// - /// - /// - public static WebApplicationBuilder AddOpenTelemetry(this WebApplicationBuilder builder) - { - var resourceBuilder = ResourceBuilder.CreateDefault().AddService(builder.Environment.ApplicationName); - var otlpEndpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]; - - if (!string.IsNullOrWhiteSpace(otlpEndpoint)) - { - builder.Logging.AddOpenTelemetry(logging => - { - logging.SetResourceBuilder(resourceBuilder) - .AddOtlpExporter(); - }); - } - - builder.Services.AddOpenTelemetry() - .WithMetrics(metrics => - { - metrics.SetResourceBuilder(resourceBuilder) - .AddPrometheusExporter() - .AddAspNetCoreInstrumentation() - .AddRuntimeInstrumentation() - .AddHttpClientInstrumentation() - .AddEventCountersInstrumentation(c => - { - // https://learn.microsoft.com/en-us/dotnet/core/diagnostics/available-counters - c.AddEventSources( - "Microsoft.AspNetCore.Hosting", - "Microsoft-AspNetCore-Server-Kestrel", - "System.Net.Http", - "System.Net.Sockets", - "System.Net.NameResolution", - "System.Net.Security"); - }); - }) - .WithTracing(tracing => - { - // We need to use AlwaysSampler to record spans - // from Todo.Web.Server, because there it no OpenTelemetry - // instrumentation - tracing.SetResourceBuilder(resourceBuilder) - .SetSampler(new AlwaysOnSampler()) - .AddAspNetCoreInstrumentation() - .AddHttpClientInstrumentation() - .AddEntityFrameworkCoreInstrumentation(); - - if (!string.IsNullOrWhiteSpace(otlpEndpoint)) - { - tracing.AddOtlpExporter(); - } - }); - - return builder; - } -} diff --git a/TodoApi/Filters/ValidationFilter.cs b/TodoApi/Filters/ValidationFilter.cs index a12ba476..92db9db1 100644 --- a/TodoApi/Filters/ValidationFilter.cs +++ b/TodoApi/Filters/ValidationFilter.cs @@ -23,7 +23,7 @@ public static TBuilder WithParameterValidation(this TBuilder builder, { if (typesToValidate.Contains(p.ParameterType)) { - parameterIndexesToValidate ??= new(); + parameterIndexesToValidate ??= []; parameterIndexesToValidate.Add(p.Position); } } @@ -58,17 +58,11 @@ public static TBuilder WithParameterValidation(this TBuilder builder, } // Equivalent to the .Produces call to add metadata to endpoints - private sealed class ProducesResponseTypeMetadata : IProducesResponseTypeMetadata + private sealed class ProducesResponseTypeMetadata(Type type, int statusCode, string contentType) : + IProducesResponseTypeMetadata { - public ProducesResponseTypeMetadata(Type type, int statusCode, string contentType) - { - Type = type; - StatusCode = statusCode; - ContentTypes = new[] { contentType }; - } - - public Type Type { get; } - public int StatusCode { get; } - public IEnumerable ContentTypes { get; } + public Type Type { get; } = type; + public int StatusCode { get; } = statusCode; + public IEnumerable ContentTypes { get; } = [contentType]; } } diff --git a/TodoApi/Program.cs b/TodoApi/Program.cs index 8b7b150a..a8aed1c8 100644 --- a/TodoApi/Program.cs +++ b/TodoApi/Program.cs @@ -1,7 +1,10 @@ +using Microsoft.AspNetCore.HttpLogging; using Microsoft.AspNetCore.Identity; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Configure auth builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme); builder.Services.AddAuthorizationBuilder().AddCurrentUserHandler(); @@ -25,18 +28,27 @@ // Configure rate limiting builder.Services.AddRateLimiting(); -// Configure OpenTelemetry -builder.AddOpenTelemetry(); +builder.Services.AddHttpLogging(o => +{ + if (builder.Environment.IsDevelopment()) + { + o.CombineLogs = true; + o.LoggingFields = HttpLoggingFields.ResponseBody | HttpLoggingFields.ResponseHeaders; + } +}); var app = builder.Build(); +app.UseHttpLogging(); +app.UseRateLimiter(); + if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } -app.UseRateLimiter(); +app.MapDefaultEndpoints(); app.Map("/", () => Results.Redirect("/swagger")); @@ -44,9 +56,4 @@ app.MapTodos(); app.MapUsers(); -// Configure the prometheus endpoint for scraping metrics -app.MapPrometheusScrapingEndpoint(); -// NOTE: This should only be exposed on an internal port! -// .RequireHost("*:9100"); - app.Run(); diff --git a/TodoApi/TodoApi.csproj b/TodoApi/TodoApi.csproj index 828b0048..47c3c8eb 100644 --- a/TodoApi/TodoApi.csproj +++ b/TodoApi/TodoApi.csproj @@ -15,22 +15,14 @@ - - - - - - - - - - - - + + + + diff --git a/TodoApi/appsettings.Development.json b/TodoApi/appsettings.Development.json index 4b5aad0f..4b94cfa3 100644 --- a/TodoApi/appsettings.Development.json +++ b/TodoApi/appsettings.Development.json @@ -1,11 +1,8 @@ { - "Serilog": { - "MinimumLevel": { + "Logging": { + "LogLevel": { "Default": "Information", - "Override": { - "Microsoft": "Information", - "Microsoft.Hosting.Lifetime": "Information" - } + "Microsoft.AspNetCore": "Information" } }, "Authentication": { diff --git a/TodoApi/prometheus.yml b/TodoApi/prometheus.yml deleted file mode 100644 index 3ff39af5..00000000 --- a/TodoApi/prometheus.yml +++ /dev/null @@ -1,12 +0,0 @@ -global: - scrape_interval: 15s - evaluation_interval: 15s - -scrape_configs: - - job_name: "prometheus" - static_configs: - - targets: ["localhost:9090"] - - - job_name: "todo-api" - static_configs: - - targets: ["host.docker.internal:5000"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 45b52658..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,113 +0,0 @@ -version: '3.4' - -configs: - prometheus_config: - file: ./TodoApi/prometheus.yml - -x-common-variables: &common-variables - ASPNETCORE_ENVIRONMENT: Development - ASPNETCORE_URLS: https://+:443;http://+:80 - ASPNETCORE_Kestrel__Certificates__Default__Password: YourPasswordHere - ASPNETCORE_Kestrel__Certificates__Default__Path: /https/todoapps.pfx - -services: - todo-api: - image: todo-api - pull_policy: if_not_present - environment: - <<: *common-variables - OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 - ports: - - "5000:80" - - "5001:443" - volumes: - - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro - - ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro - - ~/.aspnet/https:/https:ro - - ./TodoApi/.db:/app/.db - container_name: todo-api - - todo-web-server: - image: todo-web-server - pull_policy: if_not_present - environment: - <<: *common-variables - TodoApiUrl: http://todo-api - ports: - - "5002:80" - - "5003:443" - volumes: - - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro - - ~/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro - - ~/.aspnet/https:/https:ro - container_name: todo-web-server - - prometheus: - image: prom/prometheus - ports: - - "9090:9090" - configs: - - source: prometheus_config - target: /etc/prometheus/prometheus.yml - container_name: prometheus - - jaeger: - image: jaegertracing/all-in-one:latest - environment: - - COLLECTOR_ZIPKIN_HOST_PORT=:9411 - - COLLECTOR_OTLP_ENABLED=true - - METRICS_STORAGE_TYPE=prometheus - - PROMETHEUS_SERVER_URL=http://prometheus:9090 - ports: - - "6831:6831/udp" - - "6832:6832/udp" - - "5778:5778" - - "16686:16686" - - "4317:4317" - - "4318:4318" - - "14250:14250" - - "14268:14268" - - "14269:14269" - - "9411:9411" - container_name: jaeger - - seq: - image: datalust/seq:latest - container_name: seq - restart: on-failure - ports: - - 8081:80 - - 5341:5341 - environment: - ACCEPT_EULA: Y - - elasticsearch: - container_name: elastic_search - restart: on-failure - image: docker.elastic.co/elasticsearch/elasticsearch:latest - environment: - - discovery.type=single-node - - bootstrap.memory_lock=true - - xpack.monitoring.enabled=true - - xpack.watcher.enabled=false - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - ulimits: - memlock: - soft: -1 - hard: -1 - volumes: - - elastic-data:/usr/share/elasticsearch/data - ports: - - "9200:9200" - - "9300:9300" - - kibana: - image: docker.elastic.co/kibana/kibana:latest - container_name: kibana - restart: on-failure - environment: - - ELASTICSEARCH_HOSTS=http://elastic_search:9200 - ports: - - "5601:5601" - depends_on: - - elasticsearch diff --git a/tye.yaml b/tye.yaml deleted file mode 100644 index 27d7653f..00000000 --- a/tye.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# tye application configuration file -# read all about it at https://github.com/dotnet/tye -# -# when you've given us a try, we'd love to know what you think: -# https://aka.ms/AA7q20u -# -name: todoapi -services: -- name: todoapi - project: TodoApi/TodoApi.csproj -- name: todo-web-server - project: Todo.Web/Server/Todo.Web.Server.csproj