Skip to content

Commit 48c0327

Browse files
authored
Add new .NET 10 Blazor telemetry features example (#4769)
1 parent 43d4fb1 commit 48c0327

File tree

5 files changed

+460
-0
lines changed

5 files changed

+460
-0
lines changed

samples/Sentry.Samples.AspNetCore.Blazor.Server/Program.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,50 @@
11
// Capture blazor bootstrapping errors
22

3+
using Microsoft.AspNetCore.Components.Server.Circuits;
4+
using OpenTelemetry.Trace;
5+
using Sentry.OpenTelemetry;
6+
using Sentry.Samples.AspNetCore.Blazor.Server.Services;
7+
38
var builder = WebApplication.CreateBuilder(args);
49

510
builder.Services.AddRazorPages();
611
builder.Services.AddServerSideBlazor();
12+
13+
#if NET10_0_OR_GREATER
14+
// OpenTelemetry is required for the new .NET 10 Blazor telemetry features
15+
builder.Services.AddOpenTelemetry()
16+
.WithTracing(tracing =>
17+
{
18+
tracing.AddSource("Microsoft.AspNetCore.Components");
19+
tracing.AddSource("Microsoft.AspNetCore.Components.Server.Circuits");
20+
tracing.AddAspNetCoreInstrumentation();
21+
// Add Sentry as an exporter
22+
tracing.AddSentry();
23+
});
24+
#endif
25+
726
builder.WebHost.UseSentry(options =>
827
{
928
#if !SENTRY_DSN_DEFINED_IN_ENV
1029
// A DSN is required. You can set here in code, in the SENTRY_DSN environment variable or in your appsettings.json
1130
// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
1231
options.Dsn = SamplesShared.Dsn;
32+
#endif
33+
#if NET10_0_OR_GREATER
34+
options.UseOpenTelemetry();
35+
options.AddEventProcessor(new BlazorEventProcessor());
1336
#endif
1437
options.TracesSampleRate = 1.0;
1538
options.Debug = true;
1639
});
1740

41+
#if NET10_0_OR_GREATER
42+
// Services to integrate with Blazor lifecycle events
43+
builder.Services.AddSingleton<BlazorSentryIntegration>();
44+
builder.Services.AddHostedService(sp => sp.GetRequiredService<BlazorSentryIntegration>());
45+
builder.Services.AddScoped<CircuitHandler, SentryCircuitHandler>();
46+
#endif
47+
1848
var app = builder.Build();
1949

2050
app.UseHttpsRedirection();

samples/Sentry.Samples.AspNetCore.Blazor.Server/Sentry.Samples.AspNetCore.Blazor.Server.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
<ItemGroup>
1919
<ProjectReference Include="..\..\src\Sentry.AspNetCore.Blazor.WebAssembly\Sentry.AspNetCore.Blazor.WebAssembly.csproj" />
2020
<ProjectReference Include="..\..\src\Sentry.AspNetCore\Sentry.AspNetCore.csproj" />
21+
<ProjectReference Include="..\..\src\Sentry.OpenTelemetry\Sentry.OpenTelemetry.csproj" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<PackageReference Include="OpenTelemetry" Version="1.14.0" />
26+
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
27+
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
2128
</ItemGroup>
2229

2330
</Project>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Sentry.Extensibility;
2+
3+
namespace Sentry.Samples.AspNetCore.Blazor.Server.Services;
4+
5+
/// <summary>
6+
/// This event processor adds Blazor-specific context to Sentry events
7+
/// and improves transaction naming for Blazor SignalR requests.
8+
/// </summary>
9+
public class BlazorEventProcessor : ISentryEventProcessor
10+
{
11+
public SentryEvent Process(SentryEvent @event)
12+
{
13+
// Add Blazor-specific context to the event
14+
if (@event.Tags.ContainsKey("blazor.circuit_id"))
15+
{
16+
@event.SetExtra("blazor", new
17+
{
18+
Route = @event.Tags.GetValueOrDefault("blazor.route"),
19+
Component = @event.Tags.GetValueOrDefault("blazor.component"),
20+
CircuitId = @event.Tags.GetValueOrDefault("blazor.circuit_id")
21+
});
22+
}
23+
24+
// Improve transaction naming for Blazor SignalR requests.
25+
// BlazorSentryIntegration.cs should have already changed the transaction name to something meaningful,
26+
// but in case it didn't, we try to do it here based on available tags.
27+
var transactionName = @event.TransactionName;
28+
if (!IsBlazorSignalRTransaction(transactionName))
29+
{
30+
return @event;
31+
}
32+
33+
var betterName = GetBetterTransactionName(@event);
34+
@event.SetTag("transaction.original", transactionName ?? "unknown");
35+
@event.SetTag("transaction.name", betterName);
36+
37+
return @event;
38+
}
39+
40+
private static bool IsBlazorSignalRTransaction(string? transaction)
41+
{
42+
if (string.IsNullOrEmpty(transaction))
43+
{
44+
return false;
45+
}
46+
47+
return transaction.Contains("_blazor", StringComparison.OrdinalIgnoreCase) ||
48+
transaction.Contains("negotiate", StringComparison.OrdinalIgnoreCase);
49+
}
50+
51+
private static string GetBetterTransactionName(SentryEvent @event)
52+
{
53+
if (@event.Tags.TryGetValue("blazor.route", out var route) &&
54+
!string.IsNullOrEmpty(route))
55+
{
56+
return route;
57+
}
58+
59+
if (@event.Tags.TryGetValue("blazor.component", out var component) &&
60+
!string.IsNullOrEmpty(component))
61+
{
62+
var lastDot = component.LastIndexOf('.');
63+
var shortName = lastDot >= 0 ? component[(lastDot + 1)..] : component;
64+
return $"Blazor/{shortName}";
65+
}
66+
67+
return "Blazor/Unknown";
68+
}
69+
}

0 commit comments

Comments
 (0)