Per-call spans for every TurboModule invocation would explode span counts and add noise. Instead, aggregate counters + latency histograms per (module, method, kind) and flush them.
Ideas:
- In the native code, maintain per-
(module, method, kind) counters: call_count, error_count, total_duration_ms, plus a small fixed-bucket histogram (e.g. <1ms, <5ms, <20ms, <100ms, <500ms, >=500ms).
- Flush at two points:
- On every transaction finish — emit the aggregate as span attributes / measurements on a synthetic
turbo_modules.aggregate child span (or as measurements on the root span), so a slow screen load shows which native modules consumed the time.
- On a periodic timer (e.g. every 30s, configurable) for long-running sessions without transactions — emit as a metric / event so we don't lose the signal.
- Per-module overhead must stay O(1). Keep all aggregation lock-free where possible (per-thread bucket → merge on flush) to avoid contention on hot async paths.
- Provide an opt-out per module via
options.ignoreTurboModules: string[] (e.g. users may want to exclude RNSentry itself to keep the signal clean).
Per-call spans for every TurboModule invocation would explode span counts and add noise. Instead, aggregate counters + latency histograms per
(module, method, kind)and flush them.Ideas:
(module, method, kind)counters:call_count,error_count,total_duration_ms, plus a small fixed-bucket histogram (e.g.<1ms, <5ms, <20ms, <100ms, <500ms, >=500ms).turbo_modules.aggregatechild span (or asmeasurementson the root span), so a slow screen load shows which native modules consumed the time.options.ignoreTurboModules: string[](e.g. users may want to excludeRNSentryitself to keep the signal clean).