$npx -y skills add Aaronontheweb/dotnet-skills --skill aspire-service-defaultsCreate a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.
| 1 | # Aspire Service Defaults |
| 2 | |
| 3 | ## When to Use This Skill |
| 4 | |
| 5 | Use this skill when: |
| 6 | - Building Aspire-based distributed applications |
| 7 | - Need consistent observability (logging, tracing, metrics) across services |
| 8 | - Want shared health check configuration |
| 9 | - Configuring HttpClient resilience and service discovery |
| 10 | |
| 11 | --- |
| 12 | |
| 13 | ## What is ServiceDefaults? |
| 14 | |
| 15 | ServiceDefaults is a shared project that provides common configuration for all services in an Aspire application: |
| 16 | |
| 17 | - **OpenTelemetry** - Logging, tracing, and metrics |
| 18 | - **Health Checks** - Readiness and liveness endpoints |
| 19 | - **Service Discovery** - Automatic service resolution |
| 20 | - **HTTP Resilience** - Retry and circuit breaker policies |
| 21 | |
| 22 | Every service references this project and calls `AddServiceDefaults()`. |
| 23 | |
| 24 | --- |
| 25 | |
| 26 | ## Project Structure |
| 27 | |
| 28 | ``` |
| 29 | src/ |
| 30 | MyApp.ServiceDefaults/ |
| 31 | Extensions.cs |
| 32 | MyApp.ServiceDefaults.csproj |
| 33 | MyApp.Api/ |
| 34 | Program.cs # Calls AddServiceDefaults() |
| 35 | MyApp.Worker/ |
| 36 | Program.cs # Calls AddServiceDefaults() |
| 37 | MyApp.AppHost/ |
| 38 | Program.cs |
| 39 | ``` |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## ServiceDefaults Project |
| 44 | |
| 45 | ### Project File |
| 46 | |
| 47 | ```xml |
| 48 | <Project Sdk="Microsoft.NET.Sdk"> |
| 49 | <PropertyGroup> |
| 50 | <TargetFramework>net9.0</TargetFramework> |
| 51 | <IsAspireSharedProject>true</IsAspireSharedProject> |
| 52 | </PropertyGroup> |
| 53 | |
| 54 | <ItemGroup> |
| 55 | <FrameworkReference Include="Microsoft.AspNetCore.App" /> |
| 56 | <PackageReference Include="Microsoft.Extensions.Http.Resilience" /> |
| 57 | <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" /> |
| 58 | <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" /> |
| 59 | <PackageReference Include="OpenTelemetry.Extensions.Hosting" /> |
| 60 | <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" /> |
| 61 | <PackageReference Include="OpenTelemetry.Instrumentation.Http" /> |
| 62 | <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" /> |
| 63 | </ItemGroup> |
| 64 | </Project> |
| 65 | ``` |
| 66 | |
| 67 | ### Extensions.cs |
| 68 | |
| 69 | ```csharp |
| 70 | using Microsoft.AspNetCore.Builder; |
| 71 | using Microsoft.AspNetCore.Diagnostics.HealthChecks; |
| 72 | using Microsoft.Extensions.DependencyInjection; |
| 73 | using Microsoft.Extensions.Diagnostics.HealthChecks; |
| 74 | using Microsoft.Extensions.Logging; |
| 75 | using OpenTelemetry; |
| 76 | using OpenTelemetry.Metrics; |
| 77 | using OpenTelemetry.Trace; |
| 78 | |
| 79 | namespace Microsoft.Extensions.Hosting; |
| 80 | |
| 81 | public static class Extensions |
| 82 | { |
| 83 | private const string HealthEndpointPath = "/health"; |
| 84 | private const string AlivenessEndpointPath = "/alive"; |
| 85 | |
| 86 | /// <summary> |
| 87 | /// Adds common Aspire services: OpenTelemetry, health checks, |
| 88 | /// service discovery, and HTTP resilience. |
| 89 | /// </summary> |
| 90 | public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) |
| 91 | where TBuilder : IHostApplicationBuilder |
| 92 | { |
| 93 | builder.ConfigureOpenTelemetry(); |
| 94 | builder.AddDefaultHealthChecks(); |
| 95 | |
| 96 | builder.Services.AddServiceDiscovery(); |
| 97 | |
| 98 | builder.Services.ConfigureHttpClientDefaults(http => |
| 99 | { |
| 100 | // Resilience: retries, circuit breaker, timeouts |
| 101 | http.AddStandardResilienceHandler(); |
| 102 | |
| 103 | // Service discovery: resolve service names to addresses |
| 104 | http.AddServiceDiscovery(); |
| 105 | }); |
| 106 | |
| 107 | return builder; |
| 108 | } |
| 109 | |
| 110 | public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) |
| 111 | where TBuilder : IHostApplicationBuilder |
| 112 | { |
| 113 | // Logging |
| 114 | builder.Logging.AddOpenTelemetry(logging => |
| 115 | { |
| 116 | logging.IncludeFormattedMessage = true; |
| 117 | logging.IncludeScopes = true; |
| 118 | }); |
| 119 | |
| 120 | builder.Services.AddOpenTelemetry() |
| 121 | // Metrics |
| 122 | .WithMetrics(metrics => |
| 123 | { |
| 124 | metrics |
| 125 | .AddAspNetCoreInstrumentation() |
| 126 | .AddHttpClientInstrumentation() |
| 127 | .AddRuntimeInstrumentation(); |
| 128 | }) |
| 129 | // Tracing |
| 130 | .WithTracing(tracing => |
| 131 | { |
| 132 | tracing |
| 133 | .AddSource(builder.Environment.ApplicationName) |
| 134 | .AddAspNetCoreInstrumentation(options => |
| 135 | // Exclude health checks from traces |
| 136 | options.Filter = context => |
| 137 | !context.Request.Path.StartsWithSegments(HealthEndpointPath) && |
| 138 | !context.Request.Path.StartsWithSegments(AlivenessEndpointPath)) |
| 139 | .AddHttpClientInstrumentation(); |
| 140 | }); |
| 141 | |
| 142 | builder.AddOpenTelemetryExporters(); |
| 143 | |
| 144 | return builder; |
| 145 | } |
| 146 | |
| 147 | private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) |
| 148 | where TBuilder : IHostApplicationBuilder |
| 149 | { |
| 150 | // Use OTLP exporter if endpoint is configured (Aspire Dashboard, Jaeger, etc.) |
| 151 | var useOtlp = !string.IsNullOrWhiteSpace( |
| 152 | builder.Confi |