Skip to content

fix: sync default tags to native layer#5214

Open
bitsandfoxes wants to merge 2 commits intomainfrom
fix/default-tags-native-sync
Open

fix: sync default tags to native layer#5214
bitsandfoxes wants to merge 2 commits intomainfrom
fix/default-tags-native-sync

Conversation

@bitsandfoxes
Copy link
Copy Markdown
Contributor

@bitsandfoxes bitsandfoxes commented May 8, 2026

Came up through and resolves getsentry/sentry-unity#2668

The Problem

When a user sets DefaultTags on the options, these tags do not make it to native events from Android or iOS. This is not only an issue for Unity but affects C# Android and iOS app as well.
The comment seems to be out of date and prior to the addition of the extensive native support the SDK has now.

// 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.

Context

When a user sets the DefaultTags on the options, in the regular processing pipeline, these get applied by the Enricher on each individual event.

_options.ApplyDefaultTags(eventLike);

I don't know what the grand design here was, and why tags are not getting applied to the scope instead. Maybe this comes from one of the other integrations, or behaviour outside of having a GlobalScope? If not, we could move it out of the Enricher in general. Happy to follow up on this if that's the case.

Proposal

During initialization, and if scope sync is enabled, and if there is a ScopeObserver, we set those tags, making them available on the native layer. The regular flow inside the C# processing stays preserved.

Sample events taken from the Unity SDK with options.DefaultTags.Add("my", "tag");:

@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 74.14%. Comparing base (af4ebc3) to head (04bf992).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5214   +/-   ##
=======================================
  Coverage   74.14%   74.14%           
=======================================
  Files         506      506           
  Lines       18292    18295    +3     
  Branches     3576     3578    +2     
=======================================
+ Hits        13562    13565    +3     
  Misses       3859     3859           
  Partials      871      871           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/Sentry/SentrySdk.cs
// 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)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It looks like this would work in Global mode (where there is only one scope).

I'm wondering what would happen for an Asp.Net Core application that's compiled AOT. I believe we enable Sentry Native for AOT compiled applications - but I imagine Sentry Native doesn't have any notion of an 'AsyncLocal' scope stack so we effectively end up with Global scopes (and no scope observer in any case) for native events in Asp.Net Core apps compiled AOT. So that would (probably) work as expected as well (i.e. anything you put in the Default tags would end up on all native events in Asp.Net Core AOT apps).

It might be worth testing that scenario just to check (or getting an LLM to build a little test app to check that - can't really add it to our test suite as we have no easy way to test AOT yet - so it would just be a sanity check).

Comment thread src/Sentry/SentrySdk.cs
// 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)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can DefaultTags.Count > 0 be merged into the pattern match to simplify this?

Copy link
Copy Markdown
Collaborator

@jamescrosswell jamescrosswell left a comment

Choose a reason for hiding this comment

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

Generally LGTM... two comments, would be good to check the ASP.NET Core behaviour before merging, just to be sure we're not breaking anything there (seems unlikely but that could be famous last words).

Honestly I'm not sure why Default Tags vs putting some tags on the root scope - seems like either would have worked 🤷🏻

Comment thread src/Sentry/SentrySdk.cs
{
foreach (var tag in options.DefaultTags)
{
observer.SetTag(tag.Key, tag.Value);
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.

question: re

I don't know what the grand design here was, and why tags are not getting applied to the scope instead. Maybe this comes from one of the other integrations, or behaviour outside of having a GlobalScope? If not, we could move it out of the Enricher in general. Happy to follow up on this if that's the case.

Should we follow-up on that refactoring now,
or keep it in mind when doing "Global Attributes".
I can imagine, though, that a good opportunity to do the refactoring is when we're looking into removing the Enricher altogether.

See also #4882.

@jamescrosswell what do you think?

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.

follow-up question:

Do we now apply "Default Tags" to Events twice, in scenarios where we have a Scope-Observer/Scope-Sync (Unity, Android, iOS, Native)?

So should we actually move ApplyDefaultTags out of the Enricher in an immediate follow-up?

I guess it's not really a problem, double-setting/overwriting Tags in the underlying Dictionary<,> via the Scope-Observer and the Enricher ... but it's not all too great either.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we now apply "Default Tags" to Events twice, in scenarios where we have a Scope-Observer/Scope-Sync (Unity, Android, iOS, Native)?

I think they only get applied by the .NET SDK for managed events (by the enricher)... and they would only get applied by the native SDKs for native events right?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't know what the grand design here was, and why tags are not getting applied to the scope instead.

It may be because we don't currently provide any simple mechanism to initialise the 'Root' scope (when not in Global mode).

Should we follow-up on that refactoring now,

In terms of setting DefaultTags via some other mechanism than the enricher, I can't see any urgent need/benefit to doing that.

If we could replace DefaultTags with an easy way to initialise the RootScope, that would probably be more powerful/flexible since it would allow SDK users to set default all kinds of things (not just tags). Removing DefaultTags sounds like a breaking change... so I don't think we want to do that immediately. Maybe something that we could do for the next major (if .net 11 isn't too painful to upgrade to).

BackgroundWorker = Substitute.For<IBackgroundWorker>(),
InitNativeSdks = false,
};
options.DefaultTags["env"] = "production";
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.

suggestion: keep tests in sync

This is totally a nit-pick, though ... my care factor is quite minimal 😉

Suggested change
options.DefaultTags["env"] = "production";
options.DefaultTags["env"] = "production";
options.DefaultTags["region"] = "us-east-1";

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DefaultTags from SentryUnityOptions are not propagated to native crash events

3 participants