Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions samples/Sentry.Samples.AspNetCore.Blazor.Server/Program.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,58 @@
// Capture blazor bootstrapping errors

using Microsoft.AspNetCore.Components.Server.Circuits;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Sentry.OpenTelemetry;
using Sentry.Samples.AspNetCore.Blazor.Server.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

#if NET10_0_OR_GREATER
// OpenTelemetry is required for the new .NET 10 Blazor telemetry features
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing.AddSource("Microsoft.AspNetCore.Components");
tracing.AddSource("Microsoft.AspNetCore.Components.Server.Circuits");
tracing.AddAspNetCoreInstrumentation();
// Add Sentry as an exporter
tracing.AddSentry();
})
.WithMetrics(metrics =>
{
metrics.AddMeter("Microsoft.AspNetCore.Components");
metrics.AddMeter("Microsoft.AspNetCore.Components.Lifecycle");
metrics.AddMeter("Microsoft.AspNetCore.Components.Server.Circuits");
metrics.AddAspNetCoreInstrumentation();
});
#endif

builder.WebHost.UseSentry(options =>
{
#if !SENTRY_DSN_DEFINED_IN_ENV
// A DSN is required. You can set here in code, in the SENTRY_DSN environment variable or in your appsettings.json
// See https://docs.sentry.io/product/sentry-basics/dsn-explainer/
options.Dsn = SamplesShared.Dsn;
#endif
#if NET10_0_OR_GREATER
options.UseOpenTelemetry();
options.AddEventProcessor(new BlazorEventProcessor());
#endif
options.TracesSampleRate = 1.0;
options.Debug = true;
});

#if NET10_0_OR_GREATER
// Services to integrate with Blazor lifecycle events
builder.Services.AddSingleton<BlazorSentryIntegration>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<BlazorSentryIntegration>());
builder.Services.AddScoped<CircuitHandler, SentryCircuitHandler>();
#endif

var app = builder.Build();

app.UseHttpsRedirection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@
<ProjectReference Include="..\..\src\Sentry.AspNetCore\Sentry.AspNetCore.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.14.0" />
<PackageReference Include="Sentry.OpenTelemetry" Version="5.16.2" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Sentry.Extensibility;

namespace Sentry.Samples.AspNetCore.Blazor.Server.Services;

/// <summary>
/// This event processor adds Blazor-specific context to Sentry events
/// and improves transaction naming for Blazor SignalR requests.
/// </summary>
public class BlazorEventProcessor : ISentryEventProcessor
{
public SentryEvent Process(SentryEvent @event)
{
// Add Blazor-specific context to the event
if (@event.Tags.ContainsKey("blazor.circuit_id"))
{
@event.SetExtra("blazor", new
{
Route = @event.Tags.GetValueOrDefault("blazor.route"),
Component = @event.Tags.GetValueOrDefault("blazor.component"),
CircuitId = @event.Tags.GetValueOrDefault("blazor.circuit_id")
});
}

// For transaction name, we need to set it through Contexts
var transactionName = @event.Contexts.Trace?.Operation;
if (!IsBlazorSignalRTransaction(transactionName))
{
return @event;
}

var betterName = GetBetterTransactionName(@event);
@event.SetTag("transaction.original", transactionName ?? "unknown");
@event.SetTag("transaction.name", betterName);

return @event;
}

private static bool IsBlazorSignalRTransaction(string? transaction)
{
if (string.IsNullOrEmpty(transaction))
{
return false;
}

return transaction.Contains("_blazor", StringComparison.OrdinalIgnoreCase) ||
transaction.Contains("negotiate", StringComparison.OrdinalIgnoreCase);
}

private static string GetBetterTransactionName(SentryEvent @event)
{
if (@event.Tags.TryGetValue("blazor.route", out var route) &&
!string.IsNullOrEmpty(route))
{
return route;
}

if (@event.Tags.TryGetValue("blazor.component", out var component) &&
!string.IsNullOrEmpty(component))
{
var lastDot = component.LastIndexOf('.');
var shortName = lastDot >= 0 ? component[(lastDot + 1)..] : component;
return $"Blazor/{shortName}";
}

return "Blazor/Unknown";
}
}
Loading