From cc9de07cc1f9169f48cc2e507c0ec3468cb4d8bb Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Thu, 9 Jul 2026 15:53:45 +0200 Subject: [PATCH 01/11] sync the environment --- src/Sentry/IScopeObserver.cs | 5 ++ src/Sentry/Internal/ScopeObserver.cs | 9 +++ .../Platforms/Android/AndroidScopeObserver.cs | 12 ++++ .../Platforms/Cocoa/CocoaScopeObserver.cs | 12 ++++ src/Sentry/Platforms/Native/CFunctions.cs | 3 + .../Platforms/Native/NativeScopeObserver.cs | 3 + src/Sentry/Scope.cs | 19 +++++- test/Sentry.Tests/ScopeTests.cs | 63 +++++++++++++++++++ 8 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/Sentry/IScopeObserver.cs b/src/Sentry/IScopeObserver.cs index 870a160a30..3d9dcdce90 100644 --- a/src/Sentry/IScopeObserver.cs +++ b/src/Sentry/IScopeObserver.cs @@ -35,6 +35,11 @@ public interface IScopeObserver /// public void SetTrace(SentryId traceId, SpanId parentSpanId); + /// + /// Sets the environment. + /// + public void SetEnvironment(string? environment); + /// /// Adds an attachment. /// diff --git a/src/Sentry/Internal/ScopeObserver.cs b/src/Sentry/Internal/ScopeObserver.cs index 28f4676f84..f316beabf4 100644 --- a/src/Sentry/Internal/ScopeObserver.cs +++ b/src/Sentry/Internal/ScopeObserver.cs @@ -95,6 +95,15 @@ public void SetTrace(SentryId traceId, SpanId parentSpanId) public abstract void SetTraceImpl(SentryId traceId, SpanId parentSpanId); + public void SetEnvironment(string? environment) + { + _options.DiagnosticLogger?.Log(SentryLevel.Debug, + "{0} Scope Sync - Setting Environment e:\"{1}\"", null, _name, environment); + SetEnvironmentImpl(environment); + } + + public abstract void SetEnvironmentImpl(string? environment); + public void AddAttachment(SentryAttachment attachment) { _options.DiagnosticLogger?.Log(SentryLevel.Debug, diff --git a/src/Sentry/Platforms/Android/AndroidScopeObserver.cs b/src/Sentry/Platforms/Android/AndroidScopeObserver.cs index e18b215562..c4d3e91640 100644 --- a/src/Sentry/Platforms/Android/AndroidScopeObserver.cs +++ b/src/Sentry/Platforms/Android/AndroidScopeObserver.cs @@ -112,6 +112,18 @@ public void SetTrace(SentryId traceId, SpanId parentSpanId) } } + public void SetEnvironment(string? environment) + { + try + { + // TODO: Missing corresponding scope-level functionality on the Android SDK + } + finally + { + _innerObserver?.SetEnvironment(environment); + } + } + public void AddAttachment(SentryAttachment attachment) { try diff --git a/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs b/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs index f92156a4be..46f8aebf5e 100644 --- a/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs +++ b/src/Sentry/Platforms/Cocoa/CocoaScopeObserver.cs @@ -120,6 +120,18 @@ public void SetTrace(SentryId traceId, SpanId parentSpanId) } } + public void SetEnvironment(string? environment) + { + try + { + SentryCocoaSdk.ConfigureScope(scope => scope.SetEnvironment(environment)); + } + finally + { + _innerObserver?.SetEnvironment(environment); + } + } + public void AddAttachment(SentryAttachment attachment) { try diff --git a/src/Sentry/Platforms/Native/CFunctions.cs b/src/Sentry/Platforms/Native/CFunctions.cs index 5faa149720..72105155e8 100644 --- a/src/Sentry/Platforms/Native/CFunctions.cs +++ b/src/Sentry/Platforms/Native/CFunctions.cs @@ -251,6 +251,9 @@ internal static string GetCacheDirectory(SentryOptions options) [DllImport("sentry-native")] internal static extern void sentry_set_trace(string traceId, string parentSpanId); + [DllImport("sentry-native")] + internal static extern void sentry_set_environment(string? environment); + internal static Dictionary LoadDebugImages(IDiagnosticLogger? logger) { // It only makes sense to load them once because they're cached on the native side anyway. We could force diff --git a/src/Sentry/Platforms/Native/NativeScopeObserver.cs b/src/Sentry/Platforms/Native/NativeScopeObserver.cs index f9034df998..1d6956a9f4 100644 --- a/src/Sentry/Platforms/Native/NativeScopeObserver.cs +++ b/src/Sentry/Platforms/Native/NativeScopeObserver.cs @@ -43,6 +43,9 @@ public override void SetUserImpl(SentryUser user) public override void SetTraceImpl(SentryId traceId, SpanId parentSpanId) => C.sentry_set_trace(traceId.ToString(), parentSpanId.ToString()); + public override void SetEnvironmentImpl(string? environment) => + C.sentry_set_environment(environment); + public override void AddAttachmentImpl(SentryAttachment attachment) { // TODO: Missing corresponding functionality on the Native SDK diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 62b926cb7f..601cee2c1a 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -144,8 +144,25 @@ public SentryUser User /// public string? Distribution { get; set; } + private string? _environment; + /// - public string? Environment { get; set; } + public string? Environment + { + get => _environment; + set + { + if (_environment != value) + { + _environment = value; + if (Options.EnableScopeSync && + Options.ScopeObserver is { } observer) + { + observer.SetEnvironment(value); + } + } + } + } // TransactionName is kept for legacy purposes because // SentryEvent still makes use of it. diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index ccb1b277bf..9a02dd1a20 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -696,6 +696,69 @@ public void AddBreadcrumb_ObserverExist_ObserverAddsBreadcrumbIfEnabled(bool obs observer.Received(expectedCount).AddBreadcrumb(Arg.Is(breadcrumb)); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void SetEnvironment_ObserverExist_ObserverSetsEnvironmentIfEnabled(bool observerEnable) + { + // Arrange + var observer = Substitute.For(); + var scope = new Scope(new SentryOptions + { + ScopeObserver = observer, + EnableScopeSync = observerEnable + }); + var expectedEnvironment = "staging"; + var expectedCount = observerEnable ? 1 : 0; + + // Act + scope.Environment = expectedEnvironment; + + // Assert + observer.Received(expectedCount).SetEnvironment(Arg.Is(expectedEnvironment)); + } + + [Fact] + public void SetEnvironment_Null_ObserverClearsEnvironment() + { + // Arrange + var observer = Substitute.For(); + var scope = new Scope(new SentryOptions + { + ScopeObserver = observer, + EnableScopeSync = true + }) + { + Environment = "staging" + }; + observer.ClearReceivedCalls(); + + // Act + scope.Environment = null; + + // Assert + observer.Received(1).SetEnvironment(Arg.Is((string?)null)); + } + + [Fact] + public void SetEnvironment_SameValue_ObserverNotifiedOnce() + { + // Arrange + var observer = Substitute.For(); + var scope = new Scope(new SentryOptions + { + ScopeObserver = observer, + EnableScopeSync = true + }); + + // Act + scope.Environment = "staging"; + scope.Environment = "staging"; + + // Assert + observer.Received(1).SetEnvironment(Arg.Is("staging")); + } + [Fact] public void Filtered_tags_are_not_set() { From 925a20a9617ade7a8a02af6589aec5b2ecb6364b Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 9 Jul 2026 14:12:47 +0000 Subject: [PATCH 02/11] Format code --- src/Sentry/Internal/ScopeObserver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sentry/Internal/ScopeObserver.cs b/src/Sentry/Internal/ScopeObserver.cs index f316beabf4..0ba44c98a5 100644 --- a/src/Sentry/Internal/ScopeObserver.cs +++ b/src/Sentry/Internal/ScopeObserver.cs @@ -97,7 +97,7 @@ public void SetTrace(SentryId traceId, SpanId parentSpanId) public void SetEnvironment(string? environment) { - _options.DiagnosticLogger?.Log(SentryLevel.Debug, + _options.DiagnosticLogger?.Log(SentryLevel.Debug, "{0} Scope Sync - Setting Environment e:\"{1}\"", null, _name, environment); SetEnvironmentImpl(environment); } From f8f932ad61313616d1f5c7ccc23126dd3aa4f9c0 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 10 Jul 2026 13:04:17 +0200 Subject: [PATCH 03/11] fetch/set environment early --- src/Sentry/Internal/ScopeObserver.cs | 2 +- src/Sentry/Scope.cs | 26 ++++++++++++++++++------- src/Sentry/SentrySdk.cs | 3 +++ test/Sentry.Tests/ScopeTests.cs | 29 ++++++++++++++++++++-------- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/Sentry/Internal/ScopeObserver.cs b/src/Sentry/Internal/ScopeObserver.cs index f316beabf4..0ba44c98a5 100644 --- a/src/Sentry/Internal/ScopeObserver.cs +++ b/src/Sentry/Internal/ScopeObserver.cs @@ -97,7 +97,7 @@ public void SetTrace(SentryId traceId, SpanId parentSpanId) public void SetEnvironment(string? environment) { - _options.DiagnosticLogger?.Log(SentryLevel.Debug, + _options.DiagnosticLogger?.Log(SentryLevel.Debug, "{0} Scope Sync - Setting Environment e:\"{1}\"", null, _name, environment); SetEnvironmentImpl(environment); } diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 601cee2c1a..26a84f43de 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -152,14 +152,24 @@ public string? Environment get => _environment; set { - if (_environment != value) + if (_environment == value) + { + return; + } + + if (value is null) + { + Options.LogWarning("Environment cannot be null. Reverting to default value from the options."); + _environment = Options.Environment; + } + else { _environment = value; - if (Options.EnableScopeSync && - Options.ScopeObserver is { } observer) - { - observer.SetEnvironment(value); - } + } + + if (Options is { EnableScopeSync: true, ScopeObserver: { } observer }) + { + observer.SetEnvironment(_environment); } } } @@ -305,6 +315,8 @@ internal Scope(SentryOptions? options, SentryPropagationContext? propagationCont { Options = options ?? new SentryOptions(); PropagationContext = new SentryPropagationContext(propagationContext); + + _environment = Options.Environment; } // For testing. Should explicitly require SentryOptions. @@ -423,7 +435,7 @@ public void Clear() User = new(); Release = default; Distribution = default; - Environment = default; + Environment = Options.Environment; // Restore to keep in sync with native TransactionName = default; Transaction = default; Fingerprint = Array.Empty(); diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index a6e31c57af..e0067fafb8 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -63,6 +63,9 @@ internal static IHub InitHub(SentryOptions options) #pragma warning restore 0162 #pragma warning restore CS0162 // Unreachable code detected + // This happens before the native SDKs get initialized + options.Environment = options.SettingLocator.GetEnvironment(); + // Initialize native platform SDKs here if (options.InitNativeSdks) { diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index 9a02dd1a20..9c2cf3fffc 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -708,7 +708,7 @@ public void SetEnvironment_ObserverExist_ObserverSetsEnvironmentIfEnabled(bool o ScopeObserver = observer, EnableScopeSync = observerEnable }); - var expectedEnvironment = "staging"; + const string expectedEnvironment = "staging"; var expectedCount = observerEnable ? 1 : 0; // Act @@ -719,25 +719,38 @@ public void SetEnvironment_ObserverExist_ObserverSetsEnvironmentIfEnabled(bool o } [Fact] - public void SetEnvironment_Null_ObserverClearsEnvironment() + public void SetEnvironment_Null_EnvironmentSetToOptionEnvironment() { // Arrange + const string expectedEnvironment = "staging"; + var scope = new Scope(new SentryOptions { Environment = expectedEnvironment }); + + // Act + scope.Environment = null; + + // Assert + scope.Environment.Should().Be(expectedEnvironment); + } + + [Fact] + public void SetEnvironment_Null_ObserverReceivesOptionEnvironment() + { + // Arrange + const string expectedEnvironment = "staging"; var observer = Substitute.For(); var scope = new Scope(new SentryOptions { ScopeObserver = observer, - EnableScopeSync = true - }) - { - Environment = "staging" - }; + EnableScopeSync = true, + Environment = expectedEnvironment + }); observer.ClearReceivedCalls(); // Act scope.Environment = null; // Assert - observer.Received(1).SetEnvironment(Arg.Is((string?)null)); + observer.Received(1).SetEnvironment(Arg.Is(expectedEnvironment)); } [Fact] From d2b5eb59f5621f1a7cb5bfeb8001eb36392304f7 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Fri, 10 Jul 2026 11:43:17 +0000 Subject: [PATCH 04/11] Accept API verifier changes --- test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 4b2ecc6f40..024203f121 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -220,6 +220,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 4b2ecc6f40..024203f121 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -220,6 +220,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 4b2ecc6f40..024203f121 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -220,6 +220,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 211c1e6ce2..2d91b6cea8 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -208,6 +208,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); From c6268d02335b54a768e8403b9399383316aea068 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 10 Jul 2026 15:10:20 +0200 Subject: [PATCH 05/11] verify --- test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt | 1 + test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt | 3 ++- 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt index 4b2ecc6f40..024203f121 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet10_0.verified.txt @@ -220,6 +220,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 4b2ecc6f40..024203f121 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -220,6 +220,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 4b2ecc6f40..024203f121 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -220,6 +220,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 211c1e6ce2..8394533269 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -208,6 +208,7 @@ namespace Sentry void AddAttachment(Sentry.SentryAttachment attachment); void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); void ClearAttachments(); + void SetEnvironment(string? environment); void SetExtra(string key, object? value); void SetTag(string key, string value); void SetTrace(Sentry.SentryId traceId, Sentry.SpanId parentSpanId); @@ -2029,4 +2030,4 @@ public static class SentryExceptionExtensions public static void AddSentryContext(this System.Exception ex, string name, System.Collections.Generic.IReadOnlyDictionary data) { } public static void AddSentryTag(this System.Exception ex, string name, string value) { } public static void SetSentryMechanism(this System.Exception ex, string type, string? description = null, bool? handled = default, bool? terminal = default) { } -} \ No newline at end of file +} From 52db5d34e5af4c87ef6d1920ba412c027e0ce549 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 13 Jul 2026 10:34:58 +0200 Subject: [PATCH 06/11] comment and changelog --- CHANGELOG.md | 1 + src/Sentry/Scope.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08dd24178d..7809e148a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features ✨ - feat: Add exponential backoff and log deduplication to Spotlight transport by @mattico in [#5025](https://github.com/getsentry/sentry-dotnet/pull/5025) +- feat: The `Environment` set on the `Scope` now gets synchronized to the native layers (`sentry-cocoa` and `sentry-native`) by @bitsandfoxes [#5365](https://github.com/getsentry/sentry-dotnet/pull/5365) ## 6.6.0 diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 26a84f43de..363fac1367 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -316,6 +316,7 @@ internal Scope(SentryOptions? options, SentryPropagationContext? propagationCont Options = options ?? new SentryOptions(); PropagationContext = new SentryPropagationContext(propagationContext); + // Skip scope sync with backing field. The native SDKs already received the environment set on the options. _environment = Options.Environment; } From 5a038663e88d8f90be1eb3512e5c51d9d76a2775 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 13 Jul 2026 15:37:41 +0200 Subject: [PATCH 07/11] final version v2 --- src/Sentry/Scope.cs | 15 ++++------ .../ApiApprovalTests.Run.Net4_8.verified.txt | 2 +- test/Sentry.Tests/ScopeTests.cs | 29 +++++++++++++++---- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 363fac1367..337875e415 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -144,15 +144,13 @@ public SentryUser User /// public string? Distribution { get; set; } - private string? _environment; - /// public string? Environment { - get => _environment; + get; set { - if (_environment == value) + if (field == value) { return; } @@ -160,16 +158,16 @@ public string? Environment if (value is null) { Options.LogWarning("Environment cannot be null. Reverting to default value from the options."); - _environment = Options.Environment; + field = Options.Environment; } else { - _environment = value; + field = value; } if (Options is { EnableScopeSync: true, ScopeObserver: { } observer }) { - observer.SetEnvironment(_environment); + observer.SetEnvironment(field); } } } @@ -315,9 +313,6 @@ internal Scope(SentryOptions? options, SentryPropagationContext? propagationCont { Options = options ?? new SentryOptions(); PropagationContext = new SentryPropagationContext(propagationContext); - - // Skip scope sync with backing field. The native SDKs already received the environment set on the options. - _environment = Options.Environment; } // For testing. Should explicitly require SentryOptions. diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 8394533269..2d91b6cea8 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -2030,4 +2030,4 @@ public static class SentryExceptionExtensions public static void AddSentryContext(this System.Exception ex, string name, System.Collections.Generic.IReadOnlyDictionary data) { } public static void AddSentryTag(this System.Exception ex, string name, string value) { } public static void SetSentryMechanism(this System.Exception ex, string type, string? description = null, bool? handled = default, bool? terminal = default) { } -} +} \ No newline at end of file diff --git a/test/Sentry.Tests/ScopeTests.cs b/test/Sentry.Tests/ScopeTests.cs index 9c2cf3fffc..0e04ec4b34 100644 --- a/test/Sentry.Tests/ScopeTests.cs +++ b/test/Sentry.Tests/ScopeTests.cs @@ -113,6 +113,21 @@ public void Clone_CopiesFields() Assert.Equal(_sut.Environment, clone.Environment); } + [Fact] + public void Clone_EnvironmentOverridesOptions_OverridePreserved() + { + // Arrange + var options = new SentryOptions { Environment = "production" }; + var scope = new Scope(options); + scope.Environment = "staging"; + + // Act + var clone = scope.Clone(); + + // Assert + clone.Environment.Should().Be("staging"); + } + [Fact] public void TransactionName_TransactionNotStarted_NameIsSet() { @@ -722,35 +737,37 @@ public void SetEnvironment_ObserverExist_ObserverSetsEnvironmentIfEnabled(bool o public void SetEnvironment_Null_EnvironmentSetToOptionEnvironment() { // Arrange - const string expectedEnvironment = "staging"; - var scope = new Scope(new SentryOptions { Environment = expectedEnvironment }); + const string optionsEnvironment = "production"; + var scope = new Scope(new SentryOptions { Environment = optionsEnvironment }); + scope.Environment = "staging"; // Override before resetting // Act scope.Environment = null; // Assert - scope.Environment.Should().Be(expectedEnvironment); + scope.Environment.Should().Be(optionsEnvironment); } [Fact] public void SetEnvironment_Null_ObserverReceivesOptionEnvironment() { // Arrange - const string expectedEnvironment = "staging"; + const string optionsEnvironment = "production"; var observer = Substitute.For(); var scope = new Scope(new SentryOptions { ScopeObserver = observer, EnableScopeSync = true, - Environment = expectedEnvironment + Environment = optionsEnvironment }); + scope.Environment = "staging"; // Override before resetting observer.ClearReceivedCalls(); // Act scope.Environment = null; // Assert - observer.Received(1).SetEnvironment(Arg.Is(expectedEnvironment)); + observer.Received(1).SetEnvironment(Arg.Is(optionsEnvironment)); } [Fact] From 0578be2ce4f8173ff02590683ca872743a0c2153 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 13 Jul 2026 15:47:08 +0200 Subject: [PATCH 08/11] default --- src/Sentry/Scope.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Scope.cs b/src/Sentry/Scope.cs index 337875e415..d062290133 100644 --- a/src/Sentry/Scope.cs +++ b/src/Sentry/Scope.cs @@ -157,7 +157,7 @@ public string? Environment if (value is null) { - Options.LogWarning("Environment cannot be null. Reverting to default value from the options."); + Options.LogDebug("Environment cannot be null. Reverting to default value from the options."); field = Options.Environment; } else @@ -431,7 +431,7 @@ public void Clear() User = new(); Release = default; Distribution = default; - Environment = Options.Environment; // Restore to keep in sync with native + Environment = default; TransactionName = default; Transaction = default; Fingerprint = Array.Empty(); From d83b61b7c3561e16169c07fae09640d87a0258c1 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 13 Jul 2026 16:45:29 +0200 Subject: [PATCH 09/11] reverted changes to the changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7809e148a2..08dd24178d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,6 @@ ### Features ✨ - feat: Add exponential backoff and log deduplication to Spotlight transport by @mattico in [#5025](https://github.com/getsentry/sentry-dotnet/pull/5025) -- feat: The `Environment` set on the `Scope` now gets synchronized to the native layers (`sentry-cocoa` and `sentry-native`) by @bitsandfoxes [#5365](https://github.com/getsentry/sentry-dotnet/pull/5365) ## 6.6.0 From 316a530016cf75591530d79813d344280172c6d5 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 13 Jul 2026 16:47:18 +0200 Subject: [PATCH 10/11] what the.. --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08dd24178d..cc60f228a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,5 @@ # Changelog -## Unreleased - -### Features ✨ - -- feat: Add exponential backoff and log deduplication to Spotlight transport by @mattico in [#5025](https://github.com/getsentry/sentry-dotnet/pull/5025) ## 6.6.0 From b7dd7fbabf138ed86e3d96e7a1744fe9e9d98832 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 13 Jul 2026 16:47:45 +0200 Subject: [PATCH 11/11] ... --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc60f228a0..2816f09939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,5 @@ # Changelog - ## 6.6.0 ### Features ✨