feat: attach PersistentDataFileAttachmentPaths to native crash reports - #246
feat: attach PersistentDataFileAttachmentPaths to native crash reports#246bobbyg603 wants to merge 8 commits into
Conversation
PersistentDataFileAttachmentPaths reached managed exception reports, feedback and minidumps only. Native crash reports are assembled by the platform's crash reporter, which never sees BugSplat.Attachments, so files configured on the options asset were silently absent from the reports most users expect them on. This was hit during 5.0.0 smoke testing. CreateFromOptions now registers each resolved file with both mechanisms. Startup is the only correct place for the native half. On macOS and iOS a crash report is uploaded at the next launch and BugSplat asks its delegate for attachments then, in a fresh process, so a path registered mid-session is not remembered across the crash and never reaches the report. CreateFromOptions runs on every launch, which is exactly what that model requires. Native registration is compiled out in the editor, so an internal seam records what was resolved and handed to the native reporter, following the existing AutoSubmitCrashReportSetting convention in this file. Refs #242 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G7QwJaDfGEc1sE3sMiCSJp
There was a problem hiding this comment.
🟡 Changes recommended
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR extends BugSplatOptions.PersistentDataFileAttachmentPaths so that files configured on the options asset are registered for native crash reports in addition to the existing managed reporting pipeline, by wiring the resolved paths through AttachNativeLogFile during BugSplat.CreateFromOptions.
Changes:
- Register each resolved persistent-data attachment with the native attachment mechanism during
CreateFromOptions, and expose an internal list (NativePersistentDataAttachmentPaths) as a PlayMode-test seam. - Add PlayMode tests asserting that relative existing files are offered to the native layer, while absolute paths and missing files are not.
- Update API docs and changelog to reflect native crash report attachment behavior.
File summaries
| File | Description |
|---|---|
Runtime/BugSplat.cs |
Registers resolved persistent-data attachment paths with native attachment handling at startup; adds internal observable list for tests. |
Tests/Runtime/BugSplatCreateFromOptionsTests.cs |
Adds coverage for “offered to native reporter” behavior for relative/absolute/missing paths. |
Documentation~/api.md |
Updates option and native-attachment docs to describe the new attachment routing. |
CHANGELOG.md |
Notes the behavior change for PersistentDataFileAttachmentPaths in release notes. |
Review details
Suppressed comments (1)
Documentation~/api.md:62
- This paragraph says
PersistentDataFileAttachmentPathsreaches native crash reports, but the support table immediately below states Android native attachments are not supported. Please qualify the claim to supported platforms (Windows/macOS/iOS).
`Attachments` adds files to managed posts only. A native crash is captured and uploaded by the platform's crash reporter, which never sees that list. Two things do reach native reports: `PersistentDataFileAttachmentPaths` on your options asset, which is applied to both mechanisms at startup, and `AttachNativeLogFile` in code:
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| | CaptureScreenshots | Should BugSplat a screenshot and upload it when Post is called | | ||
| | PostExceptionsInEditor | Should BugSplat upload exceptions when in editor. Defaults to false so play mode exceptions stay out of your database | | ||
| | PersistentDataFileAttachmentPaths | Paths to files (relative to Application.persistentDataPath) to upload with each report | | ||
| | PersistentDataFileAttachmentPaths | Paths to files (relative to Application.persistentDataPath) to attach to both managed reports and native crash reports | |
There was a problem hiding this comment.
Fixed in 6550b59. The table entry now reads "to managed reports, and to native crash reports on platforms where native crash reporting is enabled", and the section at line 62 carries the same qualification.
One correction on the premise: Android native attachments are no longer unsupported. bugsplat-android 1.4.0 added addAttachment/removeAttachment, and the AAR vendored in this PR includes them, so all four native platforms behave the same way now. The qualification that was missing is the dependency on native crash reporting being enabled, not the platform.
|
|
||
| ### Changed | ||
|
|
||
| - `BugSplatOptions.PersistentDataFileAttachmentPaths` now attaches its files to **native crash reports** as well as managed reports. Previously the list reached managed exception reports, feedback, and minidumps only, so files configured there were silently absent from the native crash reports most users expected them on. Applying the list at startup is also what makes it work on macOS and iOS, where a report uploads at the next launch and its attachments are collected in a fresh process. |
There was a problem hiding this comment.
Fixed in 6550b59 — the entry now says "on every platform whose native crash reporting is enabled".
As above, the Android exception no longer exists: 1.4.0 supports post-init attachments and this PR vendors it.
bugsplat-android accepted attachments only while it initialized, so AttachNativeLogFile was a documented no-op there and the paths this branch registers from PersistentDataFileAttachmentPaths reached every native platform except Android. BugSplat-Git/bugsplat-android#26 adds addAttachment and removeAttachment, so the existing registration path now works on Android with no change to how callers use it. Also switches the Android branches from BugSplatBridge to the public BugSplat class and adopts its new setUser, setEmail, setNotes, and setKey rather than promoting the reserved attribute names by hand. The bridge is the JNI surface, not the public API, and hiding it is only possible once nothing outside the AAR calls it. Requires a bugsplat-android build with #26 merged; the vendored AAR must be refreshed before these calls resolve on device. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013vehBwh7WFaVQiawtJa7AN
Records what the platform actually does: options attachments reach native reports, AttachNativeLogFile now works there, and CapturePlayerLog does nothing because Unity writes no Player.log on Android. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013vehBwh7WFaVQiawtJa7AN
There was a problem hiding this comment.
🟡 Changes recommended
Documentation and Android-bridge integration details are inconsistent enough to risk misleading users or causing Android runtime failures.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
Runtime/BugSplat.cs:67
- The XML doc for CapturePlayerLog implies Player.log is uploaded on all platforms and only calls out Android as a native-attachment special case, but the managed CapturePlayerLog implementation is standalone-only (DotNetStandardExceptionReporter.cs:149-211 logs "not implemented" on other platforms). This comment should reflect that CapturePlayerLog has no effect on Android managed reports too.
Documentation~/api.md:82
- This section now says PersistentDataFileAttachmentPaths and AttachNativeLogFile reach native reports, but the platform support table still claims Android attachments are a no-op, and the next paragraph says Player.log ships with managed posts including Android. That conflicts with the new Android docs (Documentation~/android.md:17-21) and the code path for managed Player.log attachments (DotNetStandardExceptionReporter.cs only implements it on standalone platforms).
| Android | Not supported — the call is a no-op |
On macOS and iOS each attachment is truncated to its last 10 MB.
> **Register native attachments during initialization.** On macOS and iOS a crash report is uploaded at the *next* launch, and BugSplat asks for that report's attachments then, in a fresh process. A path registered part-way through a session is not remembered across the crash, so it never reaches the report. `PersistentDataFileAttachmentPaths` is applied on every launch and is unaffected by this. On Windows the handler captures attachments at crash time, so `AttachNativeLogFile` takes effect whenever you call it.
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
| using var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); | ||
| using var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); | ||
|
|
||
| using var javaClass = new AndroidJavaClass("com.bugsplat.android.BugSplatBridge"); | ||
| javaClass.CallStatic("initBugSplat", activity, database, application, version); | ||
| using var javaClass = new AndroidJavaClass("com.bugsplat.android.BugSplat"); | ||
| javaClass.CallStatic("init", activity, database, application, version); |
There was a problem hiding this comment.
Declined, with a caveat.
The runtime claim does not hold: BugSplatBridge is still public in 1.4.0 and still exposes crash() and hang(). javap on the AAR vendored in this PR:
com.bugsplat.android.BugSplatBridge: public static void crash(); public static void hang();
com.bugsplat.android.BugSplat: public static void crash(); public static void hang();
So the sample keeps working. The C# bindings moved to the BugSplat facade because it is the public API and the bridge is the JNI layer, not because the bridge went away.
The consistency point is fair, and the sample should move to BugSplat too — eventually the bridge should become package-private, and the sample would break then. Not doing it in this commit because Samples~/my-unity-crasher/Scripts/CrashScenarios.cs has unrelated uncommitted local edits in the author's working tree, and sweeping them into a review-feedback commit would be worse than the inconsistency. It is a two-line change (CrashNativeAndroid, HangNativeAndroid) to make once those land.
|
|
||
| ## Attachments | ||
|
|
||
| Paths listed in `BugSplatOptions.PersistentDataFileAttachmentPaths` are resolved against `Application.persistentDataPath` and registered with the native crash reporter at startup, so they ride along with native Android crash reports as well as managed ones. |
There was a problem hiding this comment.
Fixed in 6550b59. The Android page now opens with "With UseNativeCrashReportingForAndroid enabled..." and states that with it disabled the paths still reach managed reports while nothing is registered natively.
Adds addAttachment/removeAttachment and setUser/setEmail/setNotes/setKey, which the Android branches in this PR already call. Until now those calls resolved to methods the bundled AAR did not have and would have thrown on device. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013vehBwh7WFaVQiawtJa7AN
There was a problem hiding this comment.
🟡 Changes recommended
Android native bridge API changes and Android attachment support need compatibility/verification, and the API docs still contain a platform-support inconsistency that should be corrected.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Runtime/BugSplat.cs:450
- Android native initialization now hard-codes
com.bugsplat.android.BugSplat+init(...). The repo still referencescom.bugsplat.android.BugSplatBridgeelsewhere (e.g., sample code), and this PR doesn’t update the bundledbugsplat-android-release.aar, so this is likely to throwAndroidJavaExceptionat runtime and silently break native crash reporting on Android.
if (useNativeLibAndroid)
{
using var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
using var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
using var javaClass = new AndroidJavaClass("com.bugsplat.android.BugSplat");
javaClass.CallStatic("init", activity, database, application, version);
nativeCrashReportingEnabled = true;
Documentation~/api.md:78
- The native-attachments support table still says Android is "Not supported — the call is a no-op", but this PR and
Documentation~/android.mdnow describe Android native attachments as supported. Please update the table row to match the new behavior (or revert the behavior/docs if Android attachments aren’t actually supported).
| Android | Not supported — the call is a no-op |
- Files reviewed: 5/6 changed files
- Comments generated: 2
- Review effort level: Lite
| private void AddNativeAttachment(string path) | ||
| { | ||
| #if (UNITY_IOS || UNITY_STANDALONE_OSX) && !UNITY_EDITOR | ||
| _attachNativeLogFile(path); | ||
| #elif UNITY_STANDALONE_WIN && !UNITY_EDITOR | ||
| BugSplat_AddAttachment(path); | ||
| #elif UNITY_ANDROID && !UNITY_EDITOR | ||
| using var javaClass = new AndroidJavaClass("com.bugsplat.android.BugSplat"); | ||
| javaClass.CallStatic("addAttachment", path); | ||
| #endif |
There was a problem hiding this comment.
Fixed in 6550b59. Both AddNativeAttachment and RemoveNativeAttachment now wrap the Android call and log a warning naming the version required, rather than letting the exception escape.
The failure mode was worth taking seriously: CreateFromOptions attaches at startup, so a project that updated this package without the matching AAR would fail to launch, not merely fail to attach. A crash reporter should never be the reason a game does not start.
Review feedback: AddNativeAttachment and RemoveNativeAttachment called into the AAR with no error handling. CreateFromOptions attaches at startup, so a project that updates the package without the matching bugsplat-android would fail to launch rather than fail to attach. Both now log a warning naming the version needed. NativePersistentDataAttachmentPaths handed out the backing list as an IReadOnlyList, which an in-assembly caller could downcast and mutate. Returns AsReadOnly instead. The docs and changelog said PersistentDataFileAttachmentPaths reaches native crash reports without qualification. It only does so where native crash reporting is enabled; the Android page now names the option. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011bapaeRsoZPuK3jjtdk51V
There was a problem hiding this comment.
🟡 Changes recommended
The API reference still states Android native attachments are unsupported, contradicting the updated runtime behavior and other docs, and a couple of new comments/docs describe the test seam inaccurately.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
Runtime/BugSplat.cs:250
- The XML doc for NativePersistentDataAttachmentPaths says these paths were "handed to the native crash reporter", but native registration is compiled out in the editor and AttachNativeLogFile early-returns when nativeCrashReportingEnabled is false. The doc should describe this as the resolved paths passed to AttachNativeLogFile (or attempted registration) so it stays accurate in editor/disabled-native scenarios.
Tests/Runtime/BugSplatCreateFromOptionsTests.cs:292 - This comment says NativePersistentDataAttachmentPaths records what CreateFromOptions "handed" to the native reporter, but in the editor AttachNativeLogFile is compiled out / nativeCrashReportingEnabled is false, so nothing is actually handed off. Reword to match what the seam truly captures: the resolved paths CreateFromOptions passes to AttachNativeLogFile.
- Files reviewed: 5/6 changed files
- Comments generated: 1
- Review effort level: Lite
| | iOS | Multiple | | ||
| | Android | Not supported — the call is a no-op | |
The platform table still said Android was "Not supported - the call is a no-op", and the callout named Windows as the only platform that captures attachments at crash time. Both stopped being true when this branch vendored bugsplat-android 1.4.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011bapaeRsoZPuK3jjtdk51V
Both Apple pages covered Player.log and hang detection but said nothing about attachments, leaving the next-launch behavior discoverable only by finding it in api.md. Anyone reading the platform page they were sent to would register an attachment mid-session and never learn why it does not arrive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011bapaeRsoZPuK3jjtdk51V
There was a problem hiding this comment.
🔵 Needs a closer look
The updated docs contain an internally inconsistent statement about Player.log on Android that conflicts with the implementation and the Android documentation.
Review details
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
Documentation~/api.md:86
- The statement that
Player.log“still ships with managed posts on every platform, including Android” conflicts with the implementation:DotNetStandardExceptionReporteronly attaches Player.log for standalone Windows/macOS/Linux and logs “CapturePlayerLog is not implemented on this platform” elsewhere (e.g., Android). The Android docs also state Unity does not write Player.log there. Please update this line to avoid implying Android managed reports include Player.log.
> **Register native attachments during initialization.** On macOS and iOS a crash report is uploaded at the *next* launch, and BugSplat asks for that report's attachments then, in a fresh process. A path registered part-way through a session is not remembered across the crash, so it never reaches the report. `PersistentDataFileAttachmentPaths` is applied on every launch and is unaffected by this. On Windows and Android the handler captures attachments at crash time, so `AttachNativeLogFile` takes effect whenever you call it.
`Player.log` still ships with managed posts on every platform, including Android.
`CapturePlayerLog` uses the same mechanism with `Application.consoleLogPath`, so the two cooperate: setting it `false` detaches only `Player.log`, and attaching your own file leaves `Player.log` alone. Prefer `CapturePlayerLog` for that file rather than attaching `Application.consoleLogPath` yourself — see [Player.log and privacy](#playerlog-and-privacy).
Runtime/BugSplat.cs:250
NativePersistentDataAttachmentPathsis described as being “handed to the native crash reporter”, but in the editor the native registration call is compiled out / short-circuited (AttachNativeLogFilereturns whennativeCrashReportingEnabledis false). This list is really a test seam for the paths thatCreateFromOptionsresolved for native registration (i.e., passed intoAttachNativeLogFile), not evidence they reached the native reporter. Tightening the wording here will prevent future confusion when debugging native attachment behavior.
- Files reviewed: 7/8 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
It changes multi-platform native crash-reporting behavior (including Android JNI integration), which warrants final human verification on target platforms despite passing tests.
Review details
- Files reviewed: 7/8 changed files
- Comments generated: 1
- Review effort level: Lite
| bugSplat.nativePersistentDataAttachmentPaths.Add(fileInfo.FullName); | ||
| bugSplat.AttachNativeLogFile(fileInfo.FullName); |
On macOS and iOS a pending crash report's attachments are gathered synchronously inside -start, once, and persisted with the report. CreateFromOptions registered PersistentDataFileAttachmentPaths after the constructor returned, and the constructor is where start runs, so on Apple those files were absent from every native report. The same ordering mistake #231 fixed for Player.log on iOS. The constructor takes the paths as a parameter now. CreateFromOptions resolves them first and passes them in; the Apple branches hand each one to the bridge before start, and Windows and Android register them after init, which is early enough because both capture at crash time. NativePersistentDataAttachmentPaths still records what was handed over, so the PlayMode tests observe the same thing they did. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011bapaeRsoZPuK3jjtdk51V
There was a problem hiding this comment.
🟡 Changes recommended
Android native initialization and setter JNI calls lack the same defensive exception handling used for Android attachment calls, which can allow a mismatched/stripped AAR to crash the app during startup or at runtime.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
Runtime/BugSplat.cs:479
- Android native crash reporting initialization calls into the Java SDK without any exception handling. If the packaged AAR is missing, stripped, or an older version that lacks
com.bugsplat.android.BugSplat.init, this will throw an AndroidJavaException during startup and prevent the app from launching (the same failure mode you already guarded against for add/removeAttachment). Consider wrapping init in a try/catch and only setting nativeCrashReportingEnabled when init succeeds.
using var javaClass = new AndroidJavaClass("com.bugsplat.android.BugSplat");
javaClass.CallStatic("init", activity, database, application, version);
nativeCrashReportingEnabled = true;
- Files reviewed: 7/8 changed files
- Comments generated: 1
- Review effort level: Lite
| using var javaClass = new AndroidJavaClass("com.bugsplat.android.BugSplat"); | ||
| javaClass.CallStatic("setAttribute", key, value); | ||
| #endif |
What
PersistentDataFileAttachmentPathsnow attaches its files to native crash reports, not just managed ones.CreateFromOptionsresolves each path once at startup and registers it with the platform's crash reporter.Android support arrives with it: the Android branches move from
BugSplatBridgeto the publicBugSplatclass,AttachNativeLogFile/DetachNativeLogFilegain Android implementations, andSetNativeUser/Email/Notes/Keycall bugsplat-android's new first-class setters instead of promoting reserved attribute names by hand. Requires bugsplat-android 1.4.0, vendored here.Why
The two attachment mechanisms are disjoint.
Attachmentsreaches managed exception reports, feedback, and minidumps you post yourself; native crash reports are assembled by the platform's crash reporter, which never reads that list. Files configured on the options asset were silently missing from the native reports most people expect them on — hit for real during 5.0.0 smoke testing.Why startup — and why before start
On macOS and iOS a crash report uploads at the next launch. bugsplat-apple gathers its attachments synchronously inside
-start, once, and persists them with the report; retries re-send from disk and never ask again. So the files have to be registered before-start, not merely in the same launch.The first version of this PR registered them after the constructor returned — and the constructor is where
-startruns — so on Apple they were absent from every native report (the same ordering mistake #231 fixed forPlayer.logon iOS; Android and Windows capture at crash time and never showed it).CreateFromOptionsnow resolves the paths first and passes them into the constructor's newnativeAttachmentsparameter; the Apple branches hand each one to the bridge before_startBugSplat, and Windows and Android register them after init.Windows and Android capture attachments at crash time and accept them at any point, so
AttachNativeLogFileworks mid-session there. Android reaches that through a list file the handler wrapper reads when it execs.Tests
Non-vacuity confirmed: with the two production lines removed and the tests kept, 135 pass / 1 fails —
CreateFromOptions_WhenAttachmentIsRelativeAndExists_ShouldOfferItToTheNativeReporter.Runtime/BugSplat.cswas restored and verified byte-identical withcmp. The two negative tests pass either way; they guard against over-attaching rather than evidence the fix works.Verified on device: attaching files to a native Android crash report.
Not yet verified — macOS:
PersistentDataFileAttachmentPathswith two existing files,UseNativeCrashReportingForMacon: crash, relaunch, both files on the native report.UseNativeCrashReportingForMacoff: files on managed reports only, nothing registered natively.🤖 Generated with Claude Code