Problem Statement
Sentry.AspNetCore bridges the .NET hosting environment to the Sentry environment automatically, via SentryAspNetCoreOptions.SetEnvironment(IWebHostEnvironment). 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 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:
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<ILogger<Program>>().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:
-
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.
-
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.
-
Docs — the .NET options docs 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 (ASP.NET Core API + generic-host batch worker + Svelte frontend). The JobRunner worker needed hand-written mapping to reproduce what the API gets for free.
Problem Statement
Sentry.AspNetCorebridges the .NET hosting environment to the Sentry environment automatically, viaSentryAspNetCoreOptions.SetEnvironment(IWebHostEnvironment). Nothing else does.Grepping the repo, no source file outside
Sentry.AspNetCorereferencesIHostEnvironmentat all.AddSentryexists onILoggingBuilder,ILoggerFactory,IServiceCollection,TracerProviderBuilderandIChatClient; 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 isSENTRY_ENVIRONMENT, because that's allSettingLocator.GetEnvironmentreads.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 inproductionwhile the host saysStaging. Nothing warns, and the value looks plausible, so it tends to be discovered long after the fact.Reproduction
Generic host,
logging.AddSentry, no explicitoptions.Environment:DOTNET_ENVIRONMENTSENTRY_ENVIRONMENTHostingEnvironmentevent.EnvironmentstagingProductionstagingStagingStagingproduction←StagingpreviewStagingpreviewRow 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
stagingwhile the worker tags themproduction— from what looks like identical setup. Cross-surface filtering then quietly excludes half the system.It's also not obvious that
Sentry.Extensions.Loggingwould 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:
A generic-host integration —
Sentry.Extensions.Hosting, or anIHostBuilder.UseSentry()overload, applying the sameSetEnvironmentlogic (includingAdjustStandardEnvironmentNameCasing) thatSentry.AspNetCoredoes. Most complete, and it gives generic-host apps a natural home for other host-aware defaults later.Diagnostics only — if
IHostEnvironmentis 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.Docs — the .NET options docs 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 (ASP.NET Core API + generic-host batch worker + Svelte frontend). The
JobRunnerworker needed hand-written mapping to reproduce what the API gets for free.