BlazorAmpowering.Observability
1.0.0
dotnet add package BlazorAmpowering.Observability --version 1.0.0
NuGet\Install-Package BlazorAmpowering.Observability -Version 1.0.0
<PackageReference Include="BlazorAmpowering.Observability" Version="1.0.0" />
<PackageVersion Include="BlazorAmpowering.Observability" Version="1.0.0" />
<PackageReference Include="BlazorAmpowering.Observability" />
paket add BlazorAmpowering.Observability --version 1.0.0
#r "nuget: BlazorAmpowering.Observability, 1.0.0"
#:package BlazorAmpowering.Observability@1.0.0
#addin nuget:?package=BlazorAmpowering.Observability&version=1.0.0
#tool nuget:?package=BlazorAmpowering.Observability&version=1.0.0
BlazorAmpowering.Observability
Codeless OpenTelemetry instrumentation for Blazor Server applications.
Add full observability (traces, metrics, profiling) to your Blazor Server app with minimal code changes — no manual span creation required in your business logic.
What is instrumented automatically
| Signal | What is traced |
|---|---|
| HTTP requests | Incoming ASP.NET Core requests, outgoing HttpClient calls |
| SignalR circuits | Connection, disconnection, circuit lifetime |
| Component lifecycle | OnInitialized, OnParametersSet, OnAfterRender, user events (clicks, inputs) |
| SPA navigation | Every route change |
| Business services | Every method call via AOP (Castle DynamicProxy) |
| Runtime metrics | GC, thread pool, JIT (.NET runtime metrics) |
Installation
dotnet add package BlazorAmpowering.Observability
EF Core tracing is available as a separate package —
BlazorAmpowering.Observability.EntityFrameworkCore(pre-release):dotnet add package BlazorAmpowering.Observability.EntityFrameworkCore --prerelease
Configuration
1. appsettings.json
{
"OpenTelemetry": {
"Enabled": true,
"ApplicationName": "my-app",
"OtlpEndpoint": "http://localhost:4318/v1/traces",
"OtlpMetricsEndpoint": "http://localhost:4318/v1/metrics",
"OtlpHeaders": "",
"OtlpMetricsHeaders": "",
"SamplingProbability": 1.0
}
}
| Key | Default | Description |
|---|---|---|
Enabled |
false |
Enable or disable the entire OTel pipeline |
ApplicationName |
"BlazorApp" |
Service name displayed in Grafana / Tempo |
OtlpEndpoint |
http://localhost:4318/v1/traces |
OTLP endpoint for traces |
OtlpHeaders |
(empty) | Auth header for traces: Authorization=Basic xxx |
OtlpMetricsEndpoint |
derived from OtlpEndpoint |
OTLP endpoint for metrics |
OtlpMetricsHeaders |
derived from OtlpHeaders |
Auth header for metrics (required on Grafana Cloud) |
SamplingProbability |
0.1 |
Sampling rate — 1.0 in dev, 0.1 (10%) in prod |
Grafana Cloud
{
"OpenTelemetry": {
"Enabled": true,
"ApplicationName": "my-app",
"OtlpEndpoint": "https://otlp-gateway-prod-eu-west-2.grafana.net/otlp/v1/traces",
"OtlpHeaders": "Authorization=Basic <base64(gatewayInstanceId:apiToken)>",
"OtlpMetricsEndpoint": "https://prometheus-prod-xx-prod-eu-west-2.grafana.net/otlp/v1/metrics",
"OtlpMetricsHeaders": "Authorization=Basic <base64(prometheusInstanceId:apiToken)>",
"SamplingProbability": 0.1
}
}
Traces endpoint: use the OTLP Gateway (
otlp-gateway-prod-<zone>.grafana.net), not the Tempo direct URL.
The instance ID is shown in Grafana Cloud → Stack → Tempo → "OTLP Endpoint" section.
Local Docker stack
{
"OpenTelemetry": {
"Enabled": true,
"ApplicationName": "my-app",
"OtlpEndpoint": "http://localhost:4318/v1/traces",
"SamplingProbability": 1.0
}
}
Pyroscope (Linux in-process profiler)
Add a Pyroscope section to enable CPU and memory profiling. The native profiler must be loaded via CORECLR_* environment variables — see the Pyroscope profiling section below.
{
"Pyroscope": {
"Enable": true,
"EnableAllocationTracking": false,
"EnableContentionTracking": false,
"EnableExceptionTracking": false
}
}
| Key | Default | Description |
|---|---|---|
Enable |
false |
Enable CPU profiling |
EnableAllocationTracking |
false |
Track memory allocations (adds overhead) |
EnableContentionTracking |
false |
Track lock contention |
EnableExceptionTracking |
false |
Track thrown exceptions |
2. Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents(); // if hosting a Blazor WASM client
// Web API controllers — one span per action, reads X-Blazor-* headers from WASM automatically
builder.Services
.AddControllers()
.AddApiObservability();
// OpenTelemetry pipeline (traces + metrics)
builder.AddBlazorTelemetry();
// Pyroscope CPU profiler — no-op if native profiler is not loaded
builder.AddBlazorProfiler();
// Circuit handler + navigation instrumentation
builder.Services.AddBlazorObservability();
var app = builder.Build();
// ...
app.Run();
3. Pyroscope profiling
AddBlazorProfiler() is a no-op when the native profiler is not loaded. To activate it, the CLR must be started with the Pyroscope native profiler injected via environment variables.
Platform support
Linux — native in-process profiler (Pyroscope.Profiler.Native.so). Profiles are sent directly from the app process.
Windows / macOS — native profiler not supported. Use the provided scripts which capture profiles withdotnet-traceand push them to Pyroscope in pprof format.Three ready-to-use scripts are bundled in the NuGet package under
scripts/:
run-with-profiling-windows.ps1run-with-profiling-macos.shrun-with-profiling-linux.sh
Required environment variables (Linux)
Download the native profiler from grafana/pyroscope-dotnet releases and extract it to observability/profiler/.
# Load the native profiler into the CLR
export CORECLR_ENABLE_PROFILING=1
export CORECLR_PROFILER={BD1A650D-AC5D-4896-B64F-D6FA25D6B26A}
export CORECLR_PROFILER_PATH=./observability/profiler/Pyroscope.Profiler.Native.so
# Pyroscope server (read by the native profiler — cannot come from appsettings.json)
export PYROSCOPE_APPLICATION_NAME=my-app
export PYROSCOPE_SERVER_ADDRESS=http://localhost:4040
# Grafana Cloud Pyroscope (instead of local server)
export PYROSCOPE_SERVER_ADDRESS=https://profiles-prod-XXX.grafana.net
export PYROSCOPE_BASIC_AUTH_USER=<pyroscopeInstanceId>
export PYROSCOPE_BASIC_AUTH_PASSWORD=<apiToken>
dotnet run --project MyApp.csproj
PYROSCOPE_APPLICATION_NAMEandPYROSCOPE_SERVER_ADDRESSmust be set as environment variables — the native profiler reads them at CLR startup before any managed code runs.
Dynamic tags added automatically
AddBlazorProfiler() attaches the following tags to every profile sent to Pyroscope:
| Tag | Value |
|---|---|
environment |
IHostEnvironment.EnvironmentName (e.g. Production) |
host |
Environment.MachineName |
version |
Assembly version |
4. Component instrumentation
Add to Components/_Imports.razor to instrument all components in the folder:
@using BlazorAmpowering.Observability.Components
@inherits InstrumentedComponentBase
InstrumentedComponentBaseuses the template method pattern —OnInitializedAsyncandOnParametersSetAsyncaresealed.
Override theCorevariants instead:
| Override this | Not this |
|---|---|
OnInitializedCoreAsync() |
OnInitializedAsync() |
OnParametersSetCoreAsync() |
OnParametersSetAsync() |
OnAfterRenderCoreAsync(bool) |
OnAfterRenderAsync(bool) |
@code {
protected override async Task OnInitializedCoreAsync()
{
data = await MyService.GetDataAsync();
}
}
For a custom span inside a component:
@code {
private async Task LoadData()
{
var result = await TraceOperationAsync(
"LoadData.ApiCall",
() => MyService.GetDataAsync(),
tags: new() { ["api.endpoint"] = "/data" });
}
}
5. Navigation tracing
Inject NavigationInstrumentation in MainLayout.razor — injection alone activates route change tracing:
@using BlazorAmpowering.Observability.Services
@inject NavigationInstrumentation NavInstrumentation
6. Automatic service tracing (AOP)
Replace AddScoped/Singleton/Transient with the traced variants to automatically create a span for every method call.
Requires registration via an interface (Castle DynamicProxy limitation):
// Every method call generates a span automatically
builder.Services.AddTracedScoped<IMyService, MyService>();
builder.Services.AddTracedSingleton<IMyService, MyService>();
builder.Services.AddTracedTransient<IMyService, MyService>();
7. Web API controller tracing
AddApiObservability() registers an action filter that automatically creates a child span for every controller action:
builder.Services
.AddControllers()
.AddApiObservability();
// Disable parameter capture in production (sensitive data)
builder.Services
.AddControllers()
.AddApiObservability(options =>
{
options.CaptureActionParameters = false;
});
Each span includes:
| Tag | Example |
|---|---|
controller |
Products |
action |
AddProduct |
http.method |
POST |
http.route |
api/products |
http.status_code |
201 |
action.param.* |
primitive action parameters (if CaptureActionParameters = true) |
When called from a Blazor WebAssembly client (via BlazorAmpowering.Observability.WebAssembly), the span is also enriched automatically with blazor.component, blazor.method, blazor.event, and client.latency_ms from the request headers.
8. Manual business context enrichment
using BlazorAmpowering.Observability.Services;
using var activity = BusinessContextEnricher.StartBusinessOperation("Order.Place");
BusinessContextEnricher.EnrichWithUserContext(activity, userId, role: "customer");
BusinessContextEnricher.EnrichWithFeatureContext(activity, "Cart", "Checkout");
activity?.SetTag("order.id", orderId);
ActivitySource constants
Use ActivitySources.* when creating custom spans to stay within the correct trace context:
using BlazorAmpowering.Observability;
private static readonly ActivitySource Source = new(ActivitySources.Services);
| Constant | Value |
|---|---|
ActivitySources.Circuits |
BlazorApp.Circuits |
ActivitySources.Components |
BlazorApp.Components |
ActivitySources.Business |
BlazorApp.Business |
ActivitySources.Services |
BlazorApp.Services |
ActivitySources.Navigation |
BlazorApp.Navigation |
License
Apache 2.0 — Copyright 2026 Gaetan Delpierre
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Castle.Core (>= 5.2.1)
- OpenTelemetry (>= 1.15.3)
- OpenTelemetry.Api (>= 1.15.3)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.15.3)
- OpenTelemetry.Extensions.Hosting (>= 1.15.3)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.15.2)
- OpenTelemetry.Instrumentation.Http (>= 1.15.1)
- OpenTelemetry.Instrumentation.Runtime (>= 1.15.1)
- Pyroscope (>= 0.15.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 29 | 5/15/2026 |