Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
301 changes: 301 additions & 0 deletions docs/platforms/dotnet/guides/extensions-hosting/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
---
title: Generic Host
sdk: sentry.dotnet.extensions.hosting
description: "Learn about Sentry's .NET integration with the .NET Generic Host, for worker services, console apps, and Windows services."
---

Sentry provides an integration with the [.NET Generic Host](https://learn.microsoft.com/dotnet/core/extensions/generic-host) through the [Sentry.Extensions.Hosting NuGet package](https://www.nuget.org/packages/Sentry.Extensions.Hosting). Use it in worker services, Windows services, and console apps that run on the host. Azure Functions apps on the isolated worker model use it too, with or without the ASP.NET Core integration.

<Alert title="New in Version 7.0.0">

In earlier versions, generic host apps initialized Sentry with `builder.Logging.AddSentry()`. To upgrade, see the <PlatformLink to="/migration/">migration guide</PlatformLink>.

</Alert>

## Install

Add the Sentry dependency:

```shell {tabTitle:.NET Core CLI}
dotnet add package Sentry.Extensions.Hosting -v {{@inject packages.version('sentry.dotnet') }}
```

```powershell {tabTitle:Package Manager}
Install-Package Sentry.Extensions.Hosting -Version {{@inject packages.version('sentry.dotnet') }}
```

The package brings in [Sentry.Extensions.Logging](/platforms/dotnet/guides/extensions-logging/) and the core [Sentry](/platforms/dotnet/) SDK, so you don't need to add them yourself.

## Configure

**Error Monitoring** starts working as soon as you initialize the SDK. **Logs** sent through `Microsoft.Extensions.Logging` need `EnableLogs = true`, and **Application Metrics** need no extra configuration. You'll learn how to send them in [Send Logs](#send-logs) and [Send Application Metrics](#send-application-metrics-).

This guide also enables **Tracing**.

Call `UseSentry` on the host builder. It initializes Sentry, adds Sentry's `ILogger` integration, and registers `IHub`, `ISentryClient`, and `Func<IHub>` for dependency injection. It's available on:

- `IHostApplicationBuilder`, which `Host.CreateApplicationBuilder()` returns, on .NET 8 and later.
- `IHostBuilder`, which `Host.CreateDefaultBuilder()` returns, on every target, including [.NET Framework](#net-framework).

The snippet also turns on:

- Logs: via <PlatformLink to="/logs/#enablelogs">`EnableLogs`</PlatformLink>
- Tracing: via <PlatformLink to="/configuration/options/#TracesSampleRate">`TracesSampleRate`</PlatformLink>

Don't need a feature? Remove its options from the snippet.

```csharp {tabTitle:IHostApplicationBuilder} {filename:Program.cs} {mdExpandTabs}
var builder = Host.CreateApplicationBuilder(args);
builder.UseSentry(options =>
{
options.Dsn = "___PUBLIC_DSN___";
// Send ILogger entries to Sentry as logs
options.EnableLogs = true;
// Enables Tracing.
// Set to 1.0 to capture 100% of spans for tracing.
// We recommend reducing this value in production.
options.TracesSampleRate = 1.0;
});
builder.Services.AddHostedService<Worker>();

await builder.Build().RunAsync();
```

```csharp {tabTitle:IHostBuilder} {filename:Program.cs}
await Host.CreateDefaultBuilder(args)
.UseSentry(options =>
{
options.Dsn = "___PUBLIC_DSN___";
// Send ILogger entries to Sentry as logs
options.EnableLogs = true;
// Enables Tracing.
// Set to 1.0 to capture 100% of spans for tracing.
// We recommend reducing this value in production.
options.TracesSampleRate = 1.0;
})
.ConfigureServices(services => services.AddHostedService<Worker>())
.Build()
.RunAsync();
```

Don't also call `builder.Logging.AddSentry()`. `UseSentry` already adds Sentry's logger providers, so that call has no effect, and the options passed to it are ignored. The one exception is [clearing the logger providers](#clearing-logger-providers).

A worker has no incoming requests, so Sentry doesn't start transactions on its own. Start one around each unit of work, as shown in <PlatformLink to="/tracing/instrumentation/custom-instrumentation/">Custom Instrumentation</PlatformLink>. Requests your app makes through `IHttpClientFactory` inside a transaction become spans.

### Configuration

`UseSentry` reads its options from the `Sentry` section of your configuration, such as `appsettings.json`, and the log levels from `Logging:Sentry`. Options set in the callback are applied after the configuration, so they win.

```json {filename:appsettings.json}
{
"Logging": {
"Sentry": {
"MinimumBreadcrumbLevel": "Debug",
"MinimumEventLevel": "Warning"
}
},
"Sentry": {
"Dsn": "___PUBLIC_DSN___",
"SendDefaultPii": true,
"EnableLogs": true,
"TracesSampleRate": 1.0
}
}
```

`Logging:Sentry` only takes `LogLevel`, `MinimumBreadcrumbLevel`, and `MinimumEventLevel`. Any other key there, such as `Dsn`, makes `Build()` throw.

To turn Sentry off, for example during local development, set `"Dsn": ""`. The `SENTRY_DSN` environment variable still takes precedence over an empty `Dsn`. If no DSN is set anywhere, `Build()` throws.

### Environment

The SDK uses the host environment as the Sentry environment. The host environment defaults to `Production`, and you usually set it with `DOTNET_ENVIRONMENT`. The SDK lowercases `Production`, `Staging`, and `Development`, and keeps other names as they are. To keep the original casing, set `AdjustStandardEnvironmentNameCasing` to `false`.

An `Environment` set in code or in the `Sentry` section takes precedence, followed by the `SENTRY_ENVIRONMENT` environment variable.

### When Sentry Starts and Stops

`UseSentry` starts the SDK in `Build()`, before any hosted service starts, so code that runs between `Build()` and `Run()` is covered too. That's also the case when the app clears the logger providers or replaces the logger factory.

The SDK closes when the host raises `ApplicationStopped`, after the hosted services have stopped. Errors logged while the app shuts down are still sent.

### Clearing Logger Providers

`ClearProviders()` removes every logger provider registered before it, including the ones `UseSentry` adds. Call `UseSentry` after it:

```csharp {filename:Program.cs}
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.UseSentry();
```

If the app has to clear the providers after `UseSentry`, call `builder.Logging.AddSentry()` after `ClearProviders()`. It adds Sentry's providers back, with the options of `UseSentry`. Leave it out if NLog, Serilog, or log4net already sends your `ILogger` entries to Sentry, because each entry would then arrive twice.

With `Debug` on, the SDK logs a warning during `Build()` when Sentry's providers were removed and not added back.

### ASP.NET Core and MAUI Apps

Web and MAUI apps use `UseSentry` from [Sentry.AspNetCore](/platforms/dotnet/guides/aspnetcore/) or [Sentry.Maui](/platforms/dotnet/guides/maui/). `WebApplicationBuilder` and `MauiAppBuilder` implement `IHostApplicationBuilder`, so the generic `UseSentry` compiles there too. With only the generic call, those apps miss what their own integration adds:

- ASP.NET Core apps get no request data on events, and no transactions for incoming requests.
- MAUI apps get no navigation or UI breadcrumbs, and Sentry starts only when the app first uses a logger or `IHub`.

The generic `UseSentry` warns about this once. In a web app, it logs the warning through `ILogger` when the host starts. In a MAUI app, it writes the warning to standard error.

If an app calls both, the generic `UseSentry` steps aside, in either order, and the app runs with the ASP.NET Core or MAUI integration and its options.

### .NET Aspire Service Defaults

The `AddServiceDefaults` method in an Aspire ServiceDefaults project takes any `IHostApplicationBuilder`, so it runs in web projects as well as workers. Its `Extensions.cs` file is declared in the `Microsoft.Extensions.Hosting` namespace. C# looks for extension methods in the enclosing namespace before the ones from `using` directives, so in that file, `web.UseSentry()` calls the generic `UseSentry`, even on a `WebApplicationBuilder`. Call the ASP.NET Core or MAUI `UseSentry` as a static method instead:

```csharp {tabTitle:ASP.NET Core} {filename:Extensions.cs}
using Microsoft.AspNetCore.Builder;

namespace Microsoft.Extensions.Hosting;

public static class Extensions
{
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
if (builder is WebApplicationBuilder web)
{
SentryWebApplicationBuilderExtensions.UseSentry(web);
}
else
{
builder.UseSentry();
}

// ...
return builder;
}
}
```

```csharp {tabTitle:MAUI} {filename:Extensions.cs}
using Microsoft.Maui.Hosting;

namespace Microsoft.Extensions.Hosting;

public static class Extensions
{
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
if (builder is MauiAppBuilder maui)
{
SentryMauiAppBuilderExtensions.UseSentry(maui);
}
else
{
builder.UseSentry();
}

// ...
return builder;
}
}
```

The ServiceDefaults project then needs both `Sentry.Extensions.Hosting` and `Sentry.AspNetCore` or `Sentry.Maui`.

Don't call the ASP.NET Core `UseSentry` again in the web project's `Program.cs`: with two calls, each error is sent twice. The generic `UseSentry` doesn't have that problem. If a worker calls it in `AddServiceDefaults` and again in `Program.cs`, Sentry is set up once, with the options from both calls.

### .NET Framework

On .NET Framework, `UseSentry` is only available on `IHostBuilder`. The package supports Microsoft.Extensions.Hosting 3.1 and later there, and `IHostApplicationBuilder` only exists from 8.0. An app that uses `Host.CreateApplicationBuilder()` can switch to `Host.CreateDefaultBuilder()`, as in the `IHostBuilder` example [above](#configure).

The app can also keep `Host.CreateApplicationBuilder()`, initialize Sentry with `SentrySdk.Init`, and add the logging integration with `builder.Logging.AddSentry()`. It then doesn't read the `Sentry` section, and Sentry doesn't start and close with the host. `Logging.AddSentry()` doesn't register `IHub`, `ISentryClient`, or `Func<IHub>`, so register them if your services inject them:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Readers coming from Send Logs will look for EnableLogs here, and the IHub error is what people search for when they miss the registrations. What do you think about naming both?

Suggested change
The app can also keep `Host.CreateApplicationBuilder()`, initialize Sentry with `SentrySdk.Init`, and add the logging integration with `builder.Logging.AddSentry()`. It then doesn't read the `Sentry` section, and Sentry doesn't start and close with the host. `Logging.AddSentry()` doesn't register `IHub`, `ISentryClient`, or `Func<IHub>`, so register them if your services inject them:
The app can also keep `Host.CreateApplicationBuilder()`, initialize Sentry with `SentrySdk.Init`, and add the logging integration with `builder.Logging.AddSentry()`. It then doesn't read the `Sentry` section, and Sentry doesn't start and close with the host. It also sends `ILogger` entries as logs, and `EnableLogs` can't turn that off, because it only exists on the `UseSentry` options. `Logging.AddSentry()` doesn't register `IHub`, `ISentryClient`, or `Func<IHub>`. If your services inject them, register them, or the host fails to start with `Unable to resolve service for type 'Sentry.IHub'`:

@jamescrosswell jamescrosswell Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging.AddSentry() doesn't register IHub, ISentryClient, or Func<IHub>. If your services inject them, register them, or the host fails to start with Unable to resolve service for type 'Sentry.IHub':

That's a bit confusing to me. I think it should probably be in it's own section and we should just tell people what to do in order to get it right... it starts getting confusing when we describe all the things that could go wrong and what happens in those situations.

Depends if we're writing this for humans or LLMs though. LLMs are fine with a long list of seemingly unrelated statements. The docs here are generally to help people get up and running quickly though - LLMs can just read the source code to debug issues.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair! The code sample already shows what to do, so let's leave this paragraph as it is.


```csharp {filename:Program.cs}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Sentry.Extensibility;

using var sentry = SentrySdk.Init(options =>
{
options.Dsn = "___PUBLIC_DSN___";
});

var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddSentry();
builder.Services.AddSingleton<IHub>(HubAdapter.Instance);
builder.Services.AddSingleton<ISentryClient>(HubAdapter.Instance);
builder.Services.AddSingleton<Func<IHub>>(() => HubAdapter.Instance);
builder.Services.AddHostedService<Worker>();

await builder.Build().RunAsync();
```

Disposing the object that `SentrySdk.Init` returns sends any pending events before the app exits.

### Replacing SentrySdk.Init

If your app calls `SentrySdk.Init`, remove that call when you add `UseSentry`, and move its options into the `UseSentry` callback or the `Sentry` section. Whichever initialization runs last wins, and the two don't merge their options.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apps that call SentrySdk.Init with Logging.AddSentry(), like the fallback above, stop sending logs after the switch until they set EnableLogs. Maybe worth a sentence here:

Suggested change
If your app calls `SentrySdk.Init`, remove that call when you add `UseSentry`, and move its options into the `UseSentry` callback or the `Sentry` section. Whichever initialization runs last wins, and the two don't merge their options.
If your app calls `SentrySdk.Init`, remove that call when you add `UseSentry`, and move its options into the `UseSentry` callback or the `Sentry` section. Whichever initialization runs last wins, and the two don't merge their options. If it also calls `Logging.AddSentry()`, remove that call too. That setup sends `ILogger` entries as logs without any option, while `UseSentry` needs `EnableLogs` set to `true`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it also calls Logging.AddSentry(), remove that call too. That setup sends ILogger entries as logs without any option, while UseSentry needs EnableLogs set to true.

Again, this starts to confuse me... reading the whole paragraph I come away not knowing what it was trying to tell me - possibly it's trying to communicate 3 different things, none of which I feel like I can digest without source code samples.... like:

## Correct

Do this...

## False

Don't do this or this will happen

## False

Don't do this or that will happen

I think that kind of prose is appropriate in github issues where we're trying to get to the bottom issues but we should try to keep the docs here as clear and concise as possible. It it's hard to read, that maybe indicates we've stuffed up the API design.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, let's drop it. It only affects apps already on 7.x with SentrySdk.Init and Logging.AddSentry(), so it's not worth the extra words here.


## Send Logs

Use `Microsoft.Extensions.Logging` to send structured logs from your application. Set `EnableLogs = true` in the `UseSentry` options or in the `Sentry` section. It's off by default, as in the ASP.NET Core and MAUI integrations.

See <PlatformLink to="/logs/">Set Up Logs</PlatformLink> for all available logging methods and formatting options.

```csharp {filename:Worker.cs}
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Processing batch {BatchId}", Guid.NewGuid());
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
```

`EnableLogs` doesn't change how errors and breadcrumbs are captured. Entries at `Error` and above are sent as events, and entries at `Information` and above are kept as breadcrumbs. To change that, set `MinimumEventLevel` and `MinimumBreadcrumbLevel`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AddLogEntryFilter works on the UseSentry options too. If #19961 drops its Generic Host content, this page is the only place left that shows it. Maybe something like:

Suggested change
`EnableLogs` doesn't change how errors and breadcrumbs are captured. Entries at `Error` and above are sent as events, and entries at `Information` and above are kept as breadcrumbs. To change that, set `MinimumEventLevel` and `MinimumBreadcrumbLevel`.
`EnableLogs` doesn't change how errors and breadcrumbs are captured. Entries at `Error` and above are sent as events, and entries at `Information` and above are kept as breadcrumbs. To change that, set `MinimumEventLevel` and `MinimumBreadcrumbLevel`.
To drop specific entries before they become breadcrumbs or events, add a filter with `AddLogEntryFilter`. Return `true` to drop the entry:
```csharp {filename:Program.cs}
using Sentry.Extensions.Logging;
builder.UseSentry(options =>
{
options.AddLogEntryFilter((category, level, eventId, exception) =>
exception is OperationCanceledException);
});
```

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To drop specific entries before they become breadcrumbs or events, add a filter with AddLogEntryFilter

This bit's good. We could link to the docs for that option rather than trying to re-explain it here (which means two different copies of the same explanation to keep in sync)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just this?

Suggested change
`EnableLogs` doesn't change how errors and breadcrumbs are captured. Entries at `Error` and above are sent as events, and entries at `Information` and above are kept as breadcrumbs. To change that, set `MinimumEventLevel` and `MinimumBreadcrumbLevel`.
`EnableLogs` doesn't change how errors and breadcrumbs are captured. Entries at `Error` and above are sent as events, and entries at `Information` and above are kept as breadcrumbs. To change that, set `MinimumEventLevel` and `MinimumBreadcrumbLevel`.
To drop specific entries before they become breadcrumbs or events, call [`AddLogEntryFilter`](/platforms/dotnet/guides/extensions-logging/#filters) in the `UseSentry` callback.

#19961 renames that heading to Log Entry Filters, so it'd need to update this anchor when it lands.


## Send Application Metrics <FeatureBadge type="new" />

Metrics are enabled by default. Use `SentrySdk.Metrics` to send counters, gauges, and distributions from your app.

See <PlatformLink to="/metrics/">Set Up Metrics</PlatformLink> for all available metric types and configuration options.

```csharp
SentrySdk.Metrics.EmitCounter("button_click", 5,
new Dictionary<string, object> { ["browser"] = "Firefox" });
SentrySdk.Metrics.EmitGauge("page_load", 15.0, MeasurementUnit.Duration.Millisecond,
new Dictionary<string, object> { ["page"] = "/home" });
SentrySdk.Metrics.EmitDistribution("api_latency", 187, MeasurementUnit.Duration.Millisecond,
new Dictionary<string, object> { ["endpoint"] = "/api/orders" });
```

## Verify Your Setup

Let's test your setup and confirm that data reaches your Sentry project.

<PlatformContent includePath="getting-started-verify" />

### View Captured Data in Sentry

Open your Sentry project to view the captured event.

## Samples

- A [worker service](https://github.com/getsentry/sentry-dotnet/tree/main/samples/Sentry.Samples.GenericHost) that calls `UseSentry` and reads its options from `appsettings.json`. (**C#**)
- For [more samples](https://github.com/getsentry/sentry-dotnet-samples) of the .NET SDKs.

## Next Steps

At this point, you should have integrated Sentry into your generic host app. Here's what to explore next:

- Explore [practical guides](/get-started/guides/) on what to monitor, log, track, and investigate after setup
- Learn how to <PlatformLink to="/usage/">manually capture errors</PlatformLink>
- Explore <PlatformLink to="/tracing/">Tracing</PlatformLink>
- See all <PlatformLink to="/configuration/options/">configuration options</PlatformLink>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Pass the options to `UseSentry` in a callback:

```csharp {filename:Program.cs}
var builder = Host.CreateApplicationBuilder(args);
builder.UseSentry(options =>
{
options.Dsn = "___PUBLIC_DSN___";
options.MaxBreadcrumbs = 50;
options.Debug = true;
options.SendDefaultPii = true;
});
```

You can also set them in the `Sentry` section of `appsettings.json`:

```json {filename:appsettings.json}
{
"Sentry": {
"Dsn": "___PUBLIC_DSN___",
"MaxBreadcrumbs": 50,
"Debug": true,
"SendDefaultPii": true
}
}
```

`UseSentry` applies the configuration first and then runs the callback, so a value set in the callback wins.

<Alert>

After you have configured Sentry in your code, you can also configure the
[MSBuild setup](/platforms/dotnet/configuration/msbuild/) for your project. While this step is optional,
it can greatly improve your Sentry experience.

</Alert>
Loading