Introduction
Modern applications are increasingly distributed, combining APIs, front-end SPAs, databases, and cloud storage. While observability is critical in production, developers often overlook its importance during local development. Without proper visibility, debugging performance bottlenecks, tracing requests across services, or identifying storage issues becomes guesswork.
There are many ways to observe local systems — logs, metrics dashboards, or tracing tools — but this time I focus on using the standalone Aspire Dashboard with OpenTelemetry (OTLP).
Local observability helps developers catch issues earlier, experiment with instrumentation safely, and build confidence in their system’s resilience.
Prerequisites
- Podman (or Docker) for containerized Aspire Dashboard
Here’s how I set things up in my own project, based on my experience.:
1 - Run Aspire Dashboard
Pull and run the Aspire Dashboard container using Podman.
podman run --rm -it -p 18888:18888 -p 4317:18889 --name aspire-dashboard mcr.microsoft.com/dotnet/aspire-dashboard:latest
Expose the necessary ports for local access.
2 - Configure OpenTelemetry in .NET API
Add OpenTelemetry.Extensions.Hosting and OpenTelemetry.Exporter.OTLP packages.
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.EntityFrameworkCore" Version="1.14.0-beta.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.14.0" />
Configure logs, tracing and metrics in Program.cs.
builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});
// Configure OpenTelemetry
var otel = builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation();
//metrics.AddMeter("Microsoft.AspNetCore.Hosting");
//metrics.AddMeter("Microsoft.AspNetCore.Server.Kestrel");
})
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddEntityFrameworkCoreInstrumentation(options =>
{
options.EnrichWithIDbCommand = (activity, command) =>
{
var stateDisplayName = $"{command.CommandType} operation";
activity.DisplayName = stateDisplayName;
activity.SetTag("db.name", stateDisplayName);
};
}));
var OtlpEndpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
if (OtlpEndpoint != null)
{
otel.UseOtlpExporter();
}
Appsetting.json file
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
"OTEL_SERVICE_NAME": "StorageManagementAPI",
"OTEL_TRACES_SAMPLER": "always_on"
3 - Run the system locally.
3.1 - Logs
3.2 - Traces
3.2 - Metrics
Conclusion
This approach is lightweight, easy to set up, and highly effective for debugging complex interactions across APIs, SPAs, databases, ...
Whether you’ve already tested it or you’re just thinking about it, local observability is worth exploring.






Top comments (0)