getsentry / getsentry/sentry-dotnet
Generic host apps silently report the wrong environment (no IHostEnvironment bridge outside Sentry.AspNetCore)
- Dominant language
- C#
- Stars
- 770
- Forks
- 248
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 51
Description
## Problem Statement
`Sentry.AspNetCore` bridges the .NET hosting environment to the Sentry environment automatically, via [`SentryAspNetCoreOptions.SetEnvironment(IWebHostEnvironment)`](https://github.com/getsentry/sentry-dotnet/blob/main/src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs#L113). Nothing else does.
Grepping the repo, no source file outside `Sentry.AspNetCore` references `IHostEnvironment` at all. `AddSentry` exists on `ILoggingBuilder`, `ILoggerFactory`, `IServiceCollection`, `TracerProviderBuilder` and `IChatClient`; none of them look at the host environment. So for a Worker Service, a console app, or anything else on the generic host, the only thing the SDK can resolve is `SENTRY_ENVIRONMENT`, because that's all [`SettingLocator.GetEnvironment`](https://github.com/getsentry/sentry-dotnet/blob/main/src/Sentry/Internal/SettingLocator.cs#L80) reads.
The result is a silent misreport. Setting `DOTNET_ENVIRONMENT` — the documented way to tell a generic host where it's running — has no effect on Sentry, and events land in `production` while the host says `Staging`. Nothing warns, and the value looks plausible, so it tends to be discovered long after the fact.
### Reproduction
Generic host, `logging.AddSentry`, no explicit `options.Environment`:
```csharp
var host = Host.CreateDefaultBuilder(args)
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
logging.AddSentry(options =>
{
options.Dsn = "...";
options.SetBeforeSend((e, _) =>
{
Console.WriteLine($"HostingEnvironment = {context.HostingEnvironment.EnvironmentName}");
Console.WriteLine($"event.Environment = {e.Environment}");
return null;
});
});
})
.Build();
host.Services.GetRequiredService>().LogError("probe");
```
| `DOTNET_ENVIRONMENT` | `SENTRY_ENVIRONMENT` | `HostingEnvironment` | `event.Environment` |
|---|---|---|---|
| *(unset)* | `staging` | `Production` | `staging` |
| `Staging` | *(unset)* | `Staging` | **`production`** ← |
| `Staging` | `preview` | `Staging` | `preview` |
Row 2 is the problem. Verified against 6.4.1 on .NET 10.
### Why it's easy to get wrong
The asymmetry isn't visible from the calling code. Both surfaces are configured the same way, so an app with an ASP.NET Core API and a generic-host worker reporting to the same project will have the API tag events `staging` while the worker tags them `production` — from what looks like identical setup. Cross-surface filtering then quietly excludes half the system.
It's also not obvious that `Sentry.Extensions.Logging` would be the wrong place to look for hosting behaviour. It's the package a worker app references, but it's a *logging* integration, so it has no hosting concepts — the bridge lives in a package such an app has no reason to reference.
## Solution Brainstorm
A few options:
1. **A generic-host integration** — `Sentry.Extensions.Hosting`, or an `IHostBuilder.UseSentry()` overload, applying the same `SetEnvironment` logic (including `AdjustStandardEnvironmentNameCasing`) that `Sentry.AspNetCore` does. Most complete, and it gives generic-host apps a natural home for other host-aware defaults later.
2. **Diagnostics only** — if `IHostEnvironment` is resolvable and disagrees with the resolved Sentry environment, log a warning at init. Cheap, doesn't change behaviour, and would have turned a silent misreport into something discoverable.
3. **Docs** — the [.NET options docs](https://docs.sentry.io/platforms/dotnet/configuration/options/) note that the ASP.NET Core integration surfaces `staging`/`development`, but don't say that non-ASP.NET Core apps get no host inference at all. Worth stating explicitly whatever else is done.
### Context
Found while instrumenting [mentaldesk/truman](https://github.com/mentaldesk/truman) (ASP.NET Core API + generic-host batch worker + Svelte frontend). The `JobRunner` worker needed [hand-written mapping](https://github.com/mentaldesk/truman/pull/87) to reproduce what the API gets for free.
Contributor guide
Research direction
Compare the existing bridge in src/Sentry.AspNetCore/SentryAspNetCoreOptions.cs with environment resolution in src/Sentry/Internal/SettingLocator.cs, and inspect the AddSentry entry points listed in the issue. Decide which generic-host integration or diagnostic approach is intended, then verify the chosen behavior against the DOTNET_ENVIRONMENT and SENTRY_ENVIRONMENT combinations described in the reproduction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100