diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index d47fe75664..745371796b 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -95,8 +95,9 @@ private static void InitSentryAndroidSdk(SentryOptions options) o.CacheDirPath = Path.Combine(cacheDirectoryPath, "android"); } - // NOTE: Tags in options.DefaultTags should not be passed down, because we already call SetTag on each - // one when sending events, which is relayed through the scope observer. + // NOTE: options.DefaultTags are forwarded to the scope observer in SentrySdk.InitHub so the + // Android SDK attaches them to native crashes. The Enricher continues to apply them to + // managed events at send time. if (options.HttpProxy is System.Net.WebProxy proxy) { diff --git a/src/Sentry/Platforms/Cocoa/SentrySdk.cs b/src/Sentry/Platforms/Cocoa/SentrySdk.cs index cefea9c50c..9fd3f62ef5 100644 --- a/src/Sentry/Platforms/Cocoa/SentrySdk.cs +++ b/src/Sentry/Platforms/Cocoa/SentrySdk.cs @@ -50,8 +50,9 @@ private static void InitSentryCocoaSdk(SentryOptions options) // NOTE: options.CacheDirectoryPath - No option for this in Sentry Cocoa, but caching is still enabled // https://github.com/getsentry/sentry-cocoa/issues/1051 - // NOTE: Tags in options.DefaultTags should not be passed down, because we already call SetTag on each - // one when sending events, which is relayed through the scope observer. + // NOTE: options.DefaultTags are forwarded to the scope observer in SentrySdk.InitHub so the + // Cocoa SDK attaches them to native crashes. The Enricher continues to apply them to + // managed events at send time. if (options.BeforeBreadcrumbInternal is { } beforeBreadcrumb) { diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index fd7a6bd90f..a6e31c57af 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -91,6 +91,18 @@ internal static IHub InitHub(SentryOptions options) } options.PostInitCallbacks.Clear(); + // Default tags are applied per-event by the Enricher to events going through the .NET pipeline, + // but native crashes are captured and uploaded by the native SDK without going through that pipeline. + // Forward them to the scope observer so the native layer attaches them to crash reports. + // Bypassing the .NET scope keeps scope.Tags identical between native and non-native apps. + if (options is { EnableScopeSync: true, ScopeObserver: { } observer } && options.DefaultTags.Count > 0) + { + foreach (var tag in options.DefaultTags) + { + observer.SetTag(tag.Key, tag.Value); + } + } + // Platform specific check for profiler misconfiguration. #if __IOS__ // No user-facing warning necessary - the integration is part of InitSentryCocoaSdk(). diff --git a/test/Sentry.Tests/SentrySdkTests.cs b/test/Sentry.Tests/SentrySdkTests.cs index 9c5fe1eca0..c13703d400 100644 --- a/test/Sentry.Tests/SentrySdkTests.cs +++ b/test/Sentry.Tests/SentrySdkTests.cs @@ -967,6 +967,52 @@ public void InitHub_GlobalModeOff_NoWarningOrErrorLogged() } #endif + [Fact] + public void InitHub_DefaultTagsWithScopeSync_RelayedToScopeObserver() + { + // Arrange + var observer = Substitute.For(); + var options = new SentryOptions + { + Dsn = ValidDsn, + ScopeObserver = observer, + EnableScopeSync = true, + BackgroundWorker = Substitute.For(), + InitNativeSdks = false, + }; + options.DefaultTags["env"] = "production"; + options.DefaultTags["region"] = "us-east-1"; + + // Act + SentrySdk.InitHub(options); + + // Assert + observer.Received(1).SetTag("env", "production"); + observer.Received(1).SetTag("region", "us-east-1"); + } + + [Fact] + public void InitHub_DefaultTagsWithoutScopeSync_NotRelayedToScopeObserver() + { + // Arrange + var observer = Substitute.For(); + var options = new SentryOptions + { + Dsn = ValidDsn, + ScopeObserver = observer, + EnableScopeSync = false, + BackgroundWorker = Substitute.For(), + InitNativeSdks = false, + }; + options.DefaultTags["env"] = "production"; + + // Act + SentrySdk.InitHub(options); + + // Assert + observer.DidNotReceive().SetTag(Arg.Any(), Arg.Any()); + } + [Fact] public void InitHub_DebugEnabled_DebugLogsLogged() {