--- name: sentry-dotnet-sdk description: Full Sentry SDK setup for .NET. Use when asked to "add Sentry to .NET", "install Sentry for C#", or configure error monitoring, tracing, profiling, logging, or crons for ASP.NET Core, MAUI, WPF, WinForms, Blazor, Azure Functions, or any other .NET application. license: Apache-2.0 --- # Sentry .NET SDK Opinionated wizard that scans your .NET project and guides you through complete Sentry setup: error monitoring, distributed tracing, profiling, structured logging, and cron monitoring across all major .NET frameworks. ## Invoke This Skill When - User asks to "add Sentry to .NET", "set up Sentry in C#", or "install Sentry for ASP.NET Core" - User wants error monitoring, tracing, profiling, logging, or crons for a .NET app - User mentions `SentrySdk.Init`, `UseSentry`, `Sentry.AspNetCore`, or `Sentry.Maui` - User wants to capture unhandled exceptions in WPF, WinForms, MAUI, or Azure Functions - User asks about `SentryOptions`, `BeforeSend`, `TracesSampleRate`, or symbol upload > **Note:** SDK version and APIs below reflect `Sentry` NuGet packages ≥6.1.0. > Always verify against [docs.sentry.io/platforms/dotnet/](https://docs.sentry.io/platforms/dotnet/) before implementing. --- ## Phase 1: Detect Run these commands to understand the project before making any recommendations: ```bash # Detect framework type — find all .csproj files find . -name "*.csproj" | head -20 # Detect framework targets grep -r "TargetFramework\|Project Sdk" --include="*.csproj" . # Check for existing Sentry packages grep -r "Sentry" --include="*.csproj" . | grep "PackageReference" # Check startup files ls Program.cs src/Program.cs App.xaml.cs MauiProgram.cs 2>/dev/null # Check for appsettings ls appsettings.json src/appsettings.json 2>/dev/null # Check for logging libraries grep -r "Serilog\|NLog\|log4net" --include="*.csproj" . # Check for companion frontend ls ../frontend ../client ../web 2>/dev/null cat ../package.json 2>/dev/null | grep -E '"next"|"react"|"vue"' | head -3 ``` **What to determine:** | Question | Impact | |----------|--------| | Framework type? | Determines correct package and init pattern | | .NET version? | .NET 8+ recommended; .NET Framework 4.6.2+ supported | | Sentry already installed? | Skip install, go to feature config | | Logging library (Serilog, NLog)? | Recommend matching Sentry sink/target | | Async/hosted app (ASP.NET Core)? | `UseSentry()` on `WebHost`; no `IsGlobalModeEnabled` needed | | Desktop app (WPF, WinForms, WinUI)? | Must set `IsGlobalModeEnabled = true` | | Serverless (Azure Functions, Lambda)? | Must set `FlushOnCompletedRequest = true` | | Frontend directory found? | Trigger Phase 4 cross-link | **Framework → Package mapping:** | Detected | Package to install | |----------|--------------------| | `Sdk="Microsoft.NET.Sdk.Web"` (ASP.NET Core) | `Sentry.AspNetCore` | | `App.xaml.cs` with `Application` base | `Sentry` (WPF) | | `[STAThread]` in `Program.cs` | `Sentry` (WinForms) | | `MauiProgram.cs` | `Sentry.Maui` | | `WebAssemblyHostBuilder` | `Sentry.AspNetCore.Blazor.WebAssembly` | | `FunctionsStartup` | `Sentry.Extensions.Logging` + `Sentry.OpenTelemetry` | | `HttpApplication` / `Global.asax` | `Sentry.AspNet` | | Generic host / Worker Service | `Sentry.Extensions.Logging` | --- ## Phase 2: Recommend Present a concrete recommendation based on what you found. Lead with a proposal — don't ask open-ended questions. **Recommended (core coverage):** - ✅ **Error Monitoring** — always; captures unhandled exceptions, structured captures, scope enrichment - ✅ **Tracing** — always for ASP.NET Core and hosted apps; auto-instruments HTTP requests and EF Core queries - ✅ **Logging** — recommended for all apps; routes ILogger / Serilog / NLog entries to Sentry as breadcrumbs and events **Optional (enhanced observability):** - ⚡ **Profiling** — CPU profiling; recommend for performance-critical services running on .NET 6+ - ⚡ **Crons** — detect missed/failed scheduled jobs; recommend when Hangfire, Quartz.NET, or scheduled endpoints detected **Recommendation logic:** | Feature | Recommend when... | |---------|------------------| | Error Monitoring | **Always** — non-negotiable baseline | | Tracing | **Always for ASP.NET Core** — request traces, EF Core spans, HttpClient spans are high-value | | Logging | App uses `ILogger`, Serilog, NLog, or log4net | | Profiling | Performance-critical service on .NET 6+ | | Crons | App uses Hangfire, Quartz.NET, or scheduled Azure Functions | Propose: *"I recommend setting up Error Monitoring + Tracing + Logging. Want me to also add Profiling or Crons?"* --- ## Phase 3: Guide ### Option 1: Wizard (Recommended) ```bash npx @sentry/wizard@latest -i dotnet ``` The wizard logs you into Sentry, selects your org and project, configures your DSN, and sets up MSBuild symbol upload for readable stack traces in production. **Skip to [Verification](#verification) after running the wizard.** --- ### Option 2: Manual Setup #### Install the right package ```bash # ASP.NET Core dotnet add package Sentry.AspNetCore -v 6.1.0 # WPF or WinForms or Console dotnet add package Sentry -v 6.1.0 # .NET MAUI dotnet add package Sentry.Maui -v 6.1.0 # Blazor WebAssembly dotnet add package Sentry.AspNetCore.Blazor.WebAssembly -v 6.1.0 # Azure Functions (Isolated Worker) dotnet add package Sentry.Extensions.Logging -v 6.1.0 dotnet add package Sentry.OpenTelemetry -v 6.1.0 # Classic ASP.NET (System.Web / .NET Framework) dotnet add package Sentry.AspNet -v 6.1.0 ``` --- #### ASP.NET Core — `Program.cs` ```csharp var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseSentry(options => { options.Dsn = Environment.GetEnvironmentVariable("SENTRY_DSN") ?? "___YOUR_DSN___"; options.Debug = true; // disable in production options.SendDefaultPii = true; // captures user IP, name, email options.MaxRequestBodySize = RequestSize.Always; options.MinimumBreadcrumbLevel = LogLevel.Debug; options.MinimumEventLevel = LogLevel.Warning; options.TracesSampleRate = 1.0; // tune to 0.1–0.2 in production options.SetBeforeSend((@event, hint) => { @event.ServerName = null; // scrub hostname from events return @event; }); }); var app = builder.Build(); app.Run(); ``` **`appsettings.json` (alternative configuration):** ```json { "Sentry": { "Dsn": "___YOUR_DSN___", "SendDefaultPii": true, "MaxRequestBodySize": "Always", "MinimumBreadcrumbLevel": "Debug", "MinimumEventLevel": "Warning", "AttachStacktrace": true, "Debug": true, "TracesSampleRate": 1.0, "Environment": "production", "Release": "my-app@1.0.0" } } ``` **Environment variables (double underscore as separator):** ```bash export Sentry__Dsn="https://examplePublicKey@o0.ingest.sentry.io/0" export Sentry__TracesSampleRate="0.1" export Sentry__Environment="staging" ``` --- #### WPF — `App.xaml.cs` > ⚠️ **Critical:** Initialize in the **constructor**, NOT in `OnStartup()`. The constructor fires earlier, catching more failure modes. ```csharp using System.Windows; using Sentry; public partial class App : Application { public App() { SentrySdk.Init(options => { options.Dsn = "___YOUR_DSN___"; options.Debug = true; options.SendDefaultPii = true; options.TracesSampleRate = 1.0; options.IsGlobalModeEnabled = true; // required for all desktop apps }); // Capture WPF UI-thread exceptions before WPF's crash dialog appears DispatcherUnhandledException += App_DispatcherUnhandledException; } private void App_DispatcherUnhandledException( object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { SentrySdk.CaptureException(e.Exception); // Set e.Handled = true to prevent crash dialog and keep app running } } ``` --- #### WinForms — `Program.cs` ```csharp using System; using System.Windows.Forms; using Sentry; static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); // Required: allows Sentry to see unhandled WinForms exceptions Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); using (SentrySdk.Init(new SentryOptions { Dsn = "___YOUR_DSN___", Debug = true, TracesSampleRate = 1.0, IsGlobalModeEnabled = true, // required for desktop apps })) { Application.Run(new MainForm()); } // Disposing flushes all pending events } } ``` --- #### .NET MAUI — `MauiProgram.cs` ```csharp public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() .UseSentry(options => { options.Dsn = "___YOUR_DSN___"; options.Debug = true; options.SendDefaultPii = true; options.TracesSampleRate = 1.0; // MAUI-specific: opt-in breadcrumbs (off by default — PII risk) options.IncludeTextInBreadcrumbs = false; options.IncludeTitleInBreadcrumbs = false; options.IncludeBackgroundingStateInBreadcrumbs = false; }); return builder.Build(); } } ``` --- #### Blazor WebAssembly — `Program.cs` ```csharp var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.UseSentry(options => { options.Dsn = "___YOUR_DSN___"; options.Debug = true; options.SendDefaultPii = true; options.TracesSampleRate = 0.1; }); // Hook logging pipeline without re-initializing the SDK builder.Logging.AddSentry(o => o.InitializeSdk = false); await builder.Build().RunAsync(); ``` --- #### Azure Functions (Isolated Worker) — `Program.cs` ```csharp using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using OpenTelemetry.Trace; using Sentry.OpenTelemetry; var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults() .ConfigureServices(services => { services.AddOpenTelemetry().WithTracing(builder => { builder .AddSentry() // route OTel spans to Sentry .AddHttpClientInstrumentation(); // capture outgoing HTTP }); }) .ConfigureLogging(logging => { logging.AddSentry(options => { options.Dsn = "___YOUR_DSN___"; options.Debug = true; options.TracesSampleRate = 1.0; options.UseOpenTelemetry(); // let OTel drive tracing options.DisableSentryHttpMessageHandler = true; // prevent duplicate HTTP spans }); }) .Build(); await host.RunAsync(); ``` --- #### AWS Lambda — `LambdaEntryPoint.cs` ```csharp public class LambdaEntryPoint : APIGatewayProxyFunction { protected override void Init(IWebHostBuilder builder) { builder .UseSentry(options => { options.Dsn = "___YOUR_DSN___"; options.TracesSampleRate = 1.0; options.FlushOnCompletedRequest = true; // REQUIRED for Lambda }) .UseStartup(); } } ``` --- #### Classic ASP.NET — `Global.asax.cs` ```csharp public class MvcApplication : HttpApplication { private IDisposable _sentry; protected void Application_Start() { _sentry = SentrySdk.Init(options => { options.Dsn = "___YOUR_DSN___"; options.TracesSampleRate = 1.0; options.AddEntityFramework(); // EF6 query breadcrumbs options.AddAspNet(); // Classic ASP.NET integration }); } protected void Application_Error() => Server.CaptureLastError(); protected void Application_BeginRequest() => Context.StartSentryTransaction(); protected void Application_EndRequest() => Context.FinishSentryTransaction(); protected void Application_End() => _sentry?.Dispose(); } ``` --- ### Symbol Upload (Readable Stack Traces) Without debug symbols, stack traces show only method names — no file names or line numbers. Upload PDB files to unlock full source context. **Step 1: Create a Sentry auth token** Go to [sentry.io/settings/auth-tokens/](https://sentry.io/settings/auth-tokens/) and create a token with `project:releases` and `org:read` scopes. **Step 2: Add MSBuild properties to `.csproj` or `Directory.Build.props`:** ```xml ___ORG_SLUG___ ___PROJECT_SLUG___ true true true true ``` **Step 3: Set `SENTRY_AUTH_TOKEN` in CI:** ```yaml # GitHub Actions - name: Build & upload symbols run: dotnet build -c Release env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} ``` --- ### For Each Agreed Feature Load the corresponding reference file and follow its steps: | Feature | Reference file | Load when... | |---------|---------------|-------------| | Error Monitoring | `references/error-monitoring.md` | Always — `CaptureException`, scopes, enrichment, filtering | | Tracing | `references/tracing.md` | Server apps, distributed tracing, EF Core spans, custom instrumentation | | Profiling | `references/profiling.md` | Performance-critical apps on .NET 6+ | | Logging | `references/logging.md` | `ILogger`, Serilog, NLog, log4net integration | | Crons | `references/crons.md` | Hangfire, Quartz.NET, or scheduled function monitoring | For each feature: read the reference file, follow its steps exactly, and verify before moving on. --- ## Verification After wizard or manual setup, add a test throw and remove it after verifying: ```csharp // ASP.NET Core: add a temporary endpoint app.MapGet("/sentry-test", () => { throw new Exception("Sentry test error — delete me"); }); // Or capture explicitly anywhere SentrySdk.CaptureException(new Exception("Sentry test error — delete me")); ``` Then check your [Sentry Issues dashboard](https://sentry.io/issues/) — the error should appear within ~30 seconds. **Verification checklist:** | Check | How | |-------|-----| | Exceptions captured | Throw a test exception, verify in Sentry Issues | | Stack traces readable | Check that file names and line numbers appear | | Tracing active | Check Performance tab for transactions | | Logging wired | Log an error via `ILogger`, check it appears as Sentry breadcrumb | | Symbol upload working | Stack trace shows `Controllers/HomeController.cs:42` not `` | --- ## Config Reference ### Core `SentryOptions` | Option | Type | Default | Env Var | Notes | |--------|------|---------|---------|-------| | `Dsn` | `string` | — | `SENTRY_DSN` | Required. SDK disabled if unset. | | `Debug` | `bool` | `false` | — | SDK diagnostic output. Disable in production. | | `DiagnosticLevel` | `SentryLevel` | `Debug` | — | `Debug`, `Info`, `Warning`, `Error`, `Fatal` | | `Release` | `string` | auto | `SENTRY_RELEASE` | Auto-detected from assembly version + git SHA | | `Environment` | `string` | `"production"` | `SENTRY_ENVIRONMENT` | `"debug"` when debugger attached | | `Dist` | `string` | — | — | Build variant. Max 64 chars. | | `SampleRate` | `float` | `1.0` | — | Error event sampling rate 0.0–1.0 | | `TracesSampleRate` | `double` | `0.0` | — | Transaction sampling. Must be `> 0` to enable. | | `TracesSampler` | `Func` | — | — | Per-transaction dynamic sampler; overrides `TracesSampleRate` | | `ProfilesSampleRate` | `double` | `0.0` | — | Fraction of traced transactions to profile. Requires `Sentry.Profiling`. | | `SendDefaultPii` | `bool` | `false` | — | Include user IP, name, email | | `AttachStacktrace` | `bool` | `true` | — | Attach stack trace to all messages | | `MaxBreadcrumbs` | `int` | `100` | — | Max breadcrumbs stored per event | | `IsGlobalModeEnabled` | `bool` | `false`* | — | *Auto-`true` for MAUI, Blazor WASM. **Must** be `true` for WPF, WinForms, Console. | | `AutoSessionTracking` | `bool` | `false`* | — | *Auto-`true` for MAUI. Enable for Release Health. | | `CaptureFailedRequests` | `bool` | `true` | — | Auto-capture HTTP client errors | | `CacheDirectoryPath` | `string` | — | — | Offline event caching directory | | `ShutdownTimeout` | `TimeSpan` | — | — | Max wait for event flush on shutdown | | `HttpProxy` | `string` | — | — | Proxy URL for Sentry requests | | `EnableBackpressureHandling` | `bool` | `true` | — | Auto-reduce sample rates on delivery failures | ### ASP.NET Core Extended Options (`SentryAspNetCoreOptions`) | Option | Type | Default | Notes | |--------|------|---------|-------| | `MaxRequestBodySize` | `RequestSize` | `None` | `None`, `Small` (~4 KB), `Medium` (~10 KB), `Always` | | `MinimumBreadcrumbLevel` | `LogLevel` | `Information` | Min log level for breadcrumbs | | `MinimumEventLevel` | `LogLevel` | `Error` | Min log level to send as Sentry event | | `CaptureBlockingCalls` | `bool` | `false` | Detect `.Wait()` / `.Result` threadpool starvation | | `FlushOnCompletedRequest` | `bool` | `false` | **Required for Lambda / serverless** | | `IncludeActivityData` | `bool` | `false` | Capture `System.Diagnostics.Activity` values | ### MAUI Extended Options (`SentryMauiOptions`) | Option | Type | Default | Notes | |--------|------|---------|-------| | `IncludeTextInBreadcrumbs` | `bool` | `false` | Text from `Button`, `Label`, `Entry` elements. ⚠️ PII risk. | | `IncludeTitleInBreadcrumbs` | `bool` | `false` | Titles from `Window`, `Page` elements. ⚠️ PII risk. | | `IncludeBackgroundingStateInBreadcrumbs` | `bool` | `false` | `Window.Backgrounding` event state. ⚠️ PII risk. | ### Environment Variables | Variable | Purpose | |----------|---------| | `SENTRY_DSN` | Project DSN | | `SENTRY_RELEASE` | App version (e.g. `my-app@1.2.3`) | | `SENTRY_ENVIRONMENT` | Deployment environment name | | `SENTRY_AUTH_TOKEN` | MSBuild / `sentry-cli` symbol upload auth token | **ASP.NET Core:** use double underscore `__` as hierarchy separator: ```bash export Sentry__Dsn="https://..." export Sentry__TracesSampleRate="0.1" ``` ### MSBuild Symbol Upload Properties | Property | Type | Default | Description | |----------|------|---------|-------------| | `SentryOrg` | `string` | — | Sentry organization slug | | `SentryProject` | `string` | — | Sentry project slug | | `SentryUploadSymbols` | `bool` | `false` | Upload PDB files for line numbers in stack traces | | `SentryUploadSources` | `bool` | `false` | Upload source files for source context | | `SentryCreateRelease` | `bool` | `false` | Auto-create a Sentry release during build | | `SentrySetCommits` | `bool` | `false` | Associate git commits with the release | | `SentryUrl` | `string` | — | Self-hosted Sentry URL | --- ## Phase 4: Cross-Link After completing .NET setup, check for companion frontend projects: ```bash # Check for frontend in adjacent directories ls ../frontend ../client ../web ../app 2>/dev/null # Check for JavaScript framework indicators cat ../package.json 2>/dev/null | grep -E '"next"|"react"|"vue"|"nuxt"' | head -3 ``` If a frontend is found, suggest the matching SDK skill: | Frontend detected | Suggest skill | |-------------------|--------------| | Next.js (`"next"` in `package.json`) | `sentry-nextjs-sdk` | | React SPA (`"react"` without `"next"`) | `@sentry/react` — see [docs.sentry.io/platforms/javascript/guides/react/](https://docs.sentry.io/platforms/javascript/guides/react/) | | Vue.js | `@sentry/vue` — see [docs.sentry.io/platforms/javascript/guides/vue/](https://docs.sentry.io/platforms/javascript/guides/vue/) | | Nuxt | `@sentry/nuxt` — see [docs.sentry.io/platforms/javascript/guides/nuxt/](https://docs.sentry.io/platforms/javascript/guides/nuxt/) | Connecting frontend and backend with the same Sentry project enables **distributed tracing** — a single trace view spanning browser, .NET server, and any downstream APIs. --- ## Troubleshooting | Issue | Cause | Solution | |-------|-------|----------| | Events not appearing | DSN misconfigured | Set `Debug = true` and check console output for SDK diagnostic messages | | Stack traces show no file/line | PDB files not uploaded | Add `SentryUploadSymbols=true` to `.csproj`; set `SENTRY_AUTH_TOKEN` in CI | | WPF/WinForms exceptions missing | `IsGlobalModeEnabled` not set | Set `options.IsGlobalModeEnabled = true` in `SentrySdk.Init()` | | Lambda/serverless events lost | Container freezes before flush | Set `options.FlushOnCompletedRequest = true` | | WPF UI-thread exceptions missing | `DispatcherUnhandledException` not wired | Register `App.DispatcherUnhandledException` in constructor (not `OnStartup`) | | Duplicate HTTP spans in Azure Functions | Both Sentry and OTel instrument HTTP | Set `options.DisableSentryHttpMessageHandler = true` | | `TracesSampleRate` has no effect | Rate is `0.0` (default) | Set `TracesSampleRate > 0` to enable tracing | | `appsettings.json` values ignored | Config key format wrong | Use flat key `"Sentry:Dsn"` or env var `Sentry__Dsn` (double underscore) | | `BeforeSend` drops all events | Hook returns `null` unconditionally | Verify your filter logic; return `null` only for events you want to drop | | MAUI native crashes not captured | Wrong package | Confirm `Sentry.Maui` is installed (not just `Sentry`) |