feat: Standalone app start tracing#7660
Conversation
Semver Impact of This PR🟡 Minor (new features) 📋 Changelog PreviewThis is how your changes will appear in the changelog. New Features ✨
Internal Changes 🔧Samples
Other
🤖 This preview updates automatically when you update the PR. |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7660 +/- ##
=============================================
+ Coverage 85.546% 85.563% +0.017%
=============================================
Files 487 489 +2
Lines 29660 29835 +175
Branches 12889 12949 +60
=============================================
+ Hits 25373 25528 +155
- Misses 4237 4257 +20
Partials 50 50
... and 7 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 786f763 | 1217.17 ms | 1238.37 ms | 21.20 ms |
| 909f4ae | 1222.85 ms | 1253.26 ms | 30.41 ms |
| 151b3ee | 1238.69 ms | 1270.73 ms | 32.05 ms |
| 85ee155 | 1227.40 ms | 1251.52 ms | 24.12 ms |
| 66415de | 1221.21 ms | 1253.28 ms | 32.07 ms |
| 69745fc | 1226.70 ms | 1259.72 ms | 33.02 ms |
| dff20e3 | 1225.19 ms | 1258.66 ms | 33.47 ms |
| 670aba7 | 1219.98 ms | 1257.20 ms | 37.23 ms |
| da35aa2 | 1224.21 ms | 1249.00 ms | 24.79 ms |
| ec88236 | 1219.04 ms | 1253.69 ms | 34.65 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 786f763 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 909f4ae | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| 151b3ee | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 85ee155 | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| 66415de | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| 69745fc | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| dff20e3 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| 670aba7 | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| da35aa2 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
| ec88236 | 24.14 KiB | 1.15 MiB | 1.13 MiB |
Previous results on branch: feat/standalone-app-start-tracing
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9175efb | 1219.83 ms | 1239.50 ms | 19.67 ms |
| 7287df6 | 1224.02 ms | 1254.77 ms | 30.75 ms |
| 42f95dd | 1222.20 ms | 1257.02 ms | 34.82 ms |
| 0d2ca60 | 1229.17 ms | 1261.09 ms | 31.91 ms |
| 50bee10 | 1228.36 ms | 1267.13 ms | 38.77 ms |
| edbdb50 | 1217.06 ms | 1249.92 ms | 32.86 ms |
| 89b00b8 | 1218.33 ms | 1255.14 ms | 36.81 ms |
| 40d30ad | 1234.65 ms | 1269.73 ms | 35.09 ms |
| 8b737d8 | 1222.43 ms | 1252.96 ms | 30.53 ms |
| 98def9b | 1216.00 ms | 1249.29 ms | 33.29 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 9175efb | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 7287df6 | 24.14 KiB | 1.16 MiB | 1.14 MiB |
| 42f95dd | 24.14 KiB | 1.16 MiB | 1.14 MiB |
| 0d2ca60 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 50bee10 | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| edbdb50 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 89b00b8 | 24.14 KiB | 1.16 MiB | 1.13 MiB |
| 40d30ad | 24.14 KiB | 1.16 MiB | 1.14 MiB |
| 8b737d8 | 24.14 KiB | 1.12 MiB | 1.10 MiB |
| 98def9b | 24.14 KiB | 1.16 MiB | 1.14 MiB |
philipphofmann
left a comment
There was a problem hiding this comment.
Review for Claude Code.
Sentry Build Distribution
|
Sentry Build Distribution
|
📲 Install BuildsiOS
|
Sentry Build Distribution
|
…vider Move the app start measurement retrieval logic from SentryTracer into a standalone SentryAppStartMeasurementProvider class to decouple it from the tracer and prepare for a future Swift rewrite. Agent transcript: https://claudescope.sentry.dev/share/-ySeYAQr0t_-TzVJYh7grS4nvSpo6f1NKAIEUc4__60
Call SentryAppStartMeasurementProvider.reset() directly from ClearTestState and the provider tests instead of routing through SentryTracer's forwarding method. Agent transcript: https://claudescope.sentry.dev/share/L1InLC1w4Xm6HgEp2bHk-sJ2wtr9fRfQ08BpwGE4JSs
Remove unused profilerReferenceID parameter, add header docs, and fix stale TSan suppression. Agent transcript: https://claudescope.sentry.dev/share/YCbJaiw4eo47tyRGdPdNfyWa3VZueptJtPoevH1S97A
Fix trailing closure ambiguity with DispatchWorkItem and remove non-existent profilerReferenceID parameter. Agent transcript: https://claudescope.sentry.dev/share/boilLz21_S-f8uu_dcXvext-dAi2MAY8xkbK2zEafW4
Add experimental option to send standalone app start transactions instead of attaching app start data to the first UIViewController transaction. Uses a strategy pattern via AppStartMeasurementHandler protocol. The standalone handler is currently a no-op placeholder; actual transaction logic will be added in a follow-up. Refs: #6883 Agent transcript: https://claudescope.sentry.dev/share/KZR39vRrsVDqgpm56ONILZFlu1u_gpBhAnK7QrpyOH0
Create a real tracer with app.start.cold/warm operation that reuses the existing tracer pipeline for span building, measurements, context, debug images, and profiling. - Add SentrySpanOperationAppStartCold/Warm constants - Relax getAppStartMeasurement to accept app start ops - Skip intermediate root span for standalone transactions - Enable option in iOS sample app for validation Refs: #6883 Agent transcript: https://claudescope.sentry.dev/share/UaCuhStnxjQCwKH46hOsPqgeHsckPh15A0Flqw8JOPc
Move the standalone app start detection from SentryBuildAppStartSpans to SentryTracer and pass it as a BOOL parameter. Use span operation constants instead of string literals. Agent transcript: https://claudescope.sentry.dev/share/QyuEyImC81GP83tCrqUbp939ejQ3-3y_0TcwnvcejsA
Deduplicate the app start operation check in SentryTracer into a single method reused in toTransaction and getAppStartMeasurement. Agent transcript: https://claudescope.sentry.dev/share/bffTUQ5TAcp6oTyLqR8SSSjLLG4pSUsZ8l7TB7qaq1s
Also verify the trace origin is auto.app_start to more precisely identify standalone app start transactions. Agent transcript: https://claudescope.sentry.dev/share/HhoCIcB8Nh0dEd16RtFdllS_ZIxK393KCEPaOUiPp5s
Add StandaloneAppStartTransactionHelper to centralize the logic for identifying standalone app start transactions next to the code that creates them. Agent transcript: https://claudescope.sentry.dev/share/th338Y1YxxM5471m3BNGV5Az6QKNTNQzAyhCyZ9MmuM
Avoids race conditions with global static by passing the measurement directly to the tracer. Agent transcript: https://claudescope.sentry.dev/share/5dFraoe7qfSofcTRtjfi59JzjPmHtWPyBK9YLzp61_g
The assertion checked "app.vitals.start.warm.value" which is a span data key only set in standalone mode. The ui.load transaction uses "app_start_warm" as its measurement key.
SentryAppStartMeasurementProvider is only available on UIKit platforms. Without the guard, macOS builds fail.
Use a named ObjC category (SwiftPrivate) with rawNameSource: to give a distinct selector, replacing the standalone C functions and eliminating SentryTransactionContextFactory.h.
…andalone-app-start-tracing
|
Example App Start
|
philprime
left a comment
There was a problem hiding this comment.
Approved so you can move on, but I did leave a couple of feedback points to consider as you believe fit
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0e5330b. Configure here.
0e5330b to
c3d31c1
Compare
The standalone strategy now owns marking the measurement as read, co-locating the responsibility with the code that consumes it.
## DESCRIBE YOUR PR Document new experimental option for the Apple/Cocoa SDK and how to sample standalone app starts - **`experimental.enableStandaloneAppStartTracing`** (shipped in [9.14.0](getsentry/sentry-cocoa#7660)): When enabled, the SDK sends app start data as a dedicated `app.start` transaction instead of attaching it as spans to the first UIViewController transaction. ## IS YOUR CHANGE URGENT? Help us prioritize incoming PRs by letting us know when the change needs to go live. - [ ] Urgent deadline (GA date, etc.): - [ ] Other deadline: - [x] None: Not urgent, can wait up to 1 week+ ## SLA - Teamwork makes the dream work, so please add a reviewer to your PRs. - Please give the docs team up to 1 week to review your PR unless you've added an urgent due date to it. Thanks in advance for your help! ## PRE-MERGE CHECKLIST *Make sure you've checked the following before merging your changes:* - [ ] Checked Vercel preview for correctness, including links - [ ] PR was reviewed and approved by any necessary SMEs (subject matter experts) - [ ] PR was reviewed and approved by a member of the [Sentry docs team](https://github.com/orgs/getsentry/teams/docs) --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>


📜 Description
Send app start data as a standalone transaction instead of attaching it to the first UIViewController transaction.

The measurement is passed directly via
SentryTracerConfiguration, avoiding the global static and its race conditions.We intentionally don't add public user-facing docs for this because this is more of a proof of concept for #6883 that we still need to iterate on before we want a larger audience to try it. The Sentry product doesn't handle these standalone app start transactions yet.
💡 Motivation and Context
Fixes #6883
💚 How did you test it?
enableStandaloneAppStartTracing = falseon this branch and compared againstmain. Both produce identical transaction structure (same operations, same hierarchy:Cold Startgrouping span with 5 child phase spans under theui.loadtransaction). No regression.Regression test run 2 — 2026-03-12T10:42Z
Method
regression-testscope tag to identify each runsearch_events+get_trace_details)Results
regression-test-feature-branch-2regression-test-main-branch-22279393e2bdd4126a29efeda00dd946b7a1bb60029c842fea30b0e289e507b81Cold Start+ 5 phases)Cold Start+ 5 phases)ui.loadui.load¹ The extra span is an unrelated
profileLaunchfile.write, not an app start span.Full span comparison (sorted by duration desc)
ui.loadui.load.initial_displayui.load.full_displayapp.start.coldapp.start.coldhttp.clientapp.start.coldapp.start.coldapp.start.coldui.loadapp.start.coldfile.readfile.writefile.readfile.readfile.readfile.readui.loadui.loadui.loadui.loadui.loadui.loadui.loadui.loadui.loadSentry links
Regression test run 1 — 2026-03-12T09:20Z
Method
regression-testscope tag to identify each runsearch_events+get_trace_details)Results
regression-test-feature-branchregression-test-main-branch3ff1c4e3c3754ab18ece26d7e0cf722a3edc353029d34c9292f5ea85df4851acCold Start+ 5 phases)Cold Start+ 5 phases)ui.loadui.loadFull span comparison (sorted by duration desc)
ui.loadui.load.initial_displayui.load.full_displayapp.start.coldapp.start.coldhttp.clientapp.start.coldapp.start.coldapp.start.coldui.loadapp.start.coldfile.readfile.readfile.readfile.writefile.readui.loadfile.readui.loadui.loadui.loadui.loadui.loadui.loadui.loadui.loadSentry links
Reproducible prompt
📝 Checklist
You have to check all boxes before merging:
sendDefaultPIIis enabled.