Skip to content

feat(attributes): Add React Native native module call attributes - #564

Merged
alwx merged 5 commits into
mainfrom
alwx/turbo-module-attributes
Sep 22, 2026
Merged

alwx merged 5 commits into
mainfrom
alwx/turbo-module-attributes

Conversation

@alwx

@alwx alwx commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Description

Registers the attributes sentry-react-native emits for its native module (TurboModule) call instrumentation under a react_native.* namespace. 24 attribute files — 11 canonical, 13 deprecated — plus turbo_module as a mobile span operation.

These attributes shipped before being registered here, which is backwards: they have been live since sentry-react-native 8.14.0 (turbo_module.name / .method), 8.19.0 (call aggregate) and 8.21.0 (span attribution). This PR is retroactive registration plus a cleanup of the naming that slipped through as a result. That is why 13 of the 24 files arrive already deprecated — they are the shipped names, registered so they can be aliased and backfilled rather than silently broken.

Canonical

Key Type Purpose
react_native.architecture string new or legacy
react_native.module.name string Native module name (TurboModule name, or NativeModules key on Old Arch)
react_native.module.method string Method name
react_native.module.kind string sync or async
react_native.module.call.count integer Calls observed during the span
react_native.module.call.distinct_count integer Distinct (module, method) pairs
react_native.module.error.count integer Calls that failed
react_native.module.duration.total double Summed call duration, ms
react_native.module.duration.max double Slowest single call, ms
react_native.module.top.name string <module>.<method> with the highest total duration
react_native.module.top.duration double That total, ms

Why react_native.* and not turbo_module.*

The first version of this PR put everything under turbo_module.*, matching the shipped names. That namespace is wrong on two counts.

It names a React Native implementation detail rather than the thing being measured. And it is inaccurate even for the attributes in this PR: react_native.architecture has the value legacy, describing calls reached over the Old Architecture bridge, which are not TurboModules at all. The instrumentation covers native module calls on both architectures; only one of them involves TurboModules.

react_native is also a namespace this repo will want regardless — Hermes, Fabric, bundle and architecture attributes all belong there, and turbo_module would otherwise sit at the top level as a single-purpose orphan next to react.

The architecture sits at react_native.architecture rather than under react_native.module.* because it describes the runtime the app is on, not a property of an individual call, and it is worth setting on spans that involve no native module call.

Doing this now rather than in the next major is the cheap option. Nothing here is merged, and the SDK-side rename is already gated on this PR, so the SDK pays for one rename instead of two. Renaming later would mean deprecating all 11 canonical keys — a second backfill wave on keys added weeks earlier.

Is there a more general namespace for this?

The closest existing fit is rpc.*: module → rpc.service, method → rpc.method, plus an rpc.system.name value (the OTel enum is open). It was considered and rejected.

On the New Architecture a TurboModule call is a direct synchronous JSI invocation in the same process — no serialization, no network, no remote peer. OTel's rpc.* describes remoting systems, with client/server spans and rpc.response.status_code. The Old Architecture bridge (async, serialized) is arguably RPC-like, but the attributes are shared across both, so the group as a whole does not fit. Adopting rpc.* would also mean product surfaces aggregating by rpc.system.name start mixing gRPC services with local native module calls.

code.* does not fit either: it describes the currently executing frame, not the callee.

Deprecated

All with _status: "backfill" and symmetric aliases.

Deprecated Replacement
turbo_module.name react_native.module.name
turbo_module.method react_native.module.method
turbo_module.arch react_native.architecture
turbo_module.total_call_count, turbo_modules.total_call_count react_native.module.call.count
turbo_module.total_error_count, turbo_modules.total_error_count react_native.module.error.count
turbo_module.total_duration_ms, turbo_modules.total_duration_ms react_native.module.duration.total
turbo_module.unique_methods, turbo_modules.unique_methods react_native.module.call.distinct_count
turbo_module.top_module react_native.module.top.name
turbo_module.top_module_duration_ms react_native.module.top.duration

Three problems fixed. The namespace, above. Then: the SDK emitted the same four totals as turbo_module.* on root spans and turbo_modules.* on the aggregate span — same semantics, two prefixes, which is a third of the deprecations. The rest drop total_ prefixes and _ms suffixes in favour of dot-separated grouping, with the unit in the brief as app.vitals.* does.

react_native.module.kind and react_native.module.duration.max are genuinely new and have no deprecated predecessor.

Keys removed rather than deprecated

The SDK also emits per-method breakdowns with the dynamic segment in the middle of the key, e.g. turbo_module.<module>.<method>.call_count and turbo_modules.<module>.<method>.<kind>.count. has_dynamic_suffix only models a dynamic trailing segment, so these are not expressible in the schema and no file is included for them; they cannot be formally deprecated.

The SDK will stop emitting them and carry the per-method breakdown as one child span per (module, method) using the static attributes above. That is why the identity attributes (name, method, kind, architecture) are registered alongside the counters: on a per-method child span they are dimensions, not a key prefix.

Not included

  • The four turbo_modules.* transaction measurements. Every existing entry in model/measurements/ is a flat unnamespaced key, and they duplicate numbers now available as span attributes, so the SDK will drop them instead.
  • native.turbo_module, the slow-call breadcrumb category — breadcrumb categories are not modelled in this repo.
  • The event.kind: turbo_modules.aggregate tag, an unnamespaced generic key being removed SDK-side.

Span operation

The turbo_module span operation keeps its name. Span operations in this repo have no alias or deprecation fields, so renaming one is an unmitigated break for anything querying span.op — worth discussing separately from the attributes.

The SDK-side rename is gated on this PR and tracked in getsentry/sentry-react-native#6168.

PR Checklist

  • I have run yarn test and verified that the tests pass.
  • I have run yarn generate to generate and format code and docs.

If an attribute was added:

  • The attribute is in a namespace (e.g. nextjs.function_id, not function_id)
  • I have used the correct value for apply_scrubbing (i.e. manual or auto. Use never only for values that should never be scrubbed such as IDs)

If an attribute was deprecated:

Registers the attributes sentry-react-native emits for native module call
instrumentation, consolidated under a single `turbo_module.*` namespace.

These shipped before being registered here, which is backwards — they have been
live since 8.14.0 (`turbo_module.name`/`.method`), 8.19.0 (call aggregate) and
8.21.0 (span attribution). Hence 10 of the 21 files arrive already deprecated:
they are the shipped names, registered so they can be aliased and backfilled
rather than silently broken.

Two problems are being corrected. The SDK emitted the same four totals under both
`turbo_module.*` (root spans) and `turbo_modules.*` (aggregate span), so half the
deprecations exist only to collapse a singular/plural split. The rest drop
`total_` prefixes and `_ms` suffixes in favour of dot-separated grouping, putting
the unit in the `brief` as `app.vitals.*` does.

The per-method breakdown keys are removed rather than deprecated. They place the
dynamic segment mid-key (`turbo_module.<module>.<method>.call_count`), and
`has_dynamic_suffix` only models a trailing segment, so they cannot be expressed
in the schema at all. The SDK will carry that breakdown as one child span per
(module, method) instead, which is why the identity attributes are registered
alongside the counters: on a per-method span they are dimensions, not a prefix.

Measurements are deliberately excluded. The SDK also emits four transaction
measurements under `turbo_modules.*`, but every existing entry in
model/measurements is flat and unnamespaced, and they now duplicate span
attributes, so the SDK will drop them instead.

`changelog.prs` is unset pending this PR's number.

Refs getsentry/sentry-react-native#6168
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Attributes

  • Add React Native native module call attributes by alwx in #564
  • Use the web-vitals navigation types as-is for browser.navigation.type by logaretm in #640
  • Add legacy gen_ai cache token attributes (deprecated) by JPeer264 in #582

Other

  • (op) Add ui.resolve span op by Lms24 in #633
  • (replays) Define replayId as a deprecated attribute by mjq in #401

Internal Changes 🔧

Deps

  • Bump devalue from 5.8.1 to 5.9.2 by dependabot in #639
  • Bump smol-toml from 1.6.1 to 1.8.0 by dependabot in #635

🤖 This preview updates automatically when you update the PR.

@alwx
alwx marked this pull request as ready for review August 12, 2026 08:31
@alwx
alwx requested review from a team, Lms24, cleptric, mjq and nsdeschenes as code owners August 12, 2026 08:31

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 822ed36. Configure here.

Comment thread javascript/sentry-conventions/src/op.ts Outdated
@alwx
alwx requested a review from antonis September 7, 2026 08:26
Resolve the generated python/src/sentry_conventions/attributes.py conflict and
re-run `yarn generate`. The op constant is now emitted as `TURBO_MODULE` after
#565 dropped the category prefix and `SPAN_OP` suffix from op constants.
* Record `prs: [564]` on every changelog entry now that the PR number is known.
* Switch from the legacy `example` field to `examples`, which CONTRIBUTING now
  prefers for new attributes, and normalise field order to match
  `create:attribute` output. `turbo_module.arch` and `turbo_module.kind` list
  both of their enumerated values.
* Drop the "native module and method names" scrubbing reason from
  `turbo_module.arch` and `turbo_module.kind` — both are closed enums that carry
  no app-defined names — and add it to `turbo_module.top_module`, which holds the
  same value as `turbo_module.top.name`.

@antonis antonis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@alwx

alwx commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

@Lms24 could you please review this one? Cannot be merged without your approval.

@Lms24 Lms24 left a comment

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.

Apologies for the review delay! To be completely honest, I don’t have the bandwidth to think every attribute through here, if it makes sense, is semantically correct, etc. My high-level advice is:

  • Every attribute we currently send in SDKs should be documented as-is.
  • I’d encourage you to think about if
    • there aren’t already attributes that map this in a more general name space, we’d want to use in the future.
    • long-term, the turbo_module namespace makes sense. To me, as a non-RN expert, it seems to be something very RN-specific and I wonder if another namespace makes more sense (e.g. something like react_native), for any attributes that don’t fit into an existing namespace
    • obviously, all attribute changes, can happen over time, e.g. when working on the next major
    • Doing nothing is also an option :)

Approving to unblock you. Also, just for completeness, there’s 4 other code owners who could have reviewed this change. In the future I won’t have the bandwidth to carefully review every PR. Happy to share advice or answer any questions though.

…espace

`turbo_module` named a React Native implementation detail, and it was
already inaccurate within this PR: `turbo_module.arch: legacy` describes
calls reached over the Old Architecture bridge, which are not
TurboModules. Move the canonical attributes to `react_native.module.*`,
with the architecture as `react_native.architecture` since it describes
the runtime rather than an individual call.

`turbo_module.name`, `.method` and `.arch` are shipped, so they now
become deprecations too, with `_status: "backfill"` and symmetric
aliases. Deprecations go from 10 to 13. Doing this in the next major
instead would mean deprecating all 11 canonical keys, so this is the
cheapest point to rename: nothing is merged and the SDK-side rename is
already gated on this PR.

The `turbo_module` span operation is left as-is; span operations have no
alias or deprecation mechanism.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@alwx alwx changed the title feat(attributes): Add React Native TurboModule call attributes feat(attributes): Add React Native native module call attributes Sep 22, 2026
@alwx
alwx merged commit e4973c8 into main Sep 22, 2026
18 checks passed
@alwx
alwx deleted the alwx/turbo-module-attributes branch September 22, 2026 11:56
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.

4 participants