Building an In-Game Bug Reporter for Unity — What to Build and Where to Stop
Trigger, capture, send, and identify — four independent decisions, the difficulty cliff between them, and the traps that show up when you build your own bug reporter.
- unity
- bug-report
- qa
"Build a bug reporter" sounds like one task. It's actually four independent decisions, and getting any single one wrong makes the rest useless no matter how well they're built. This post maps those four decisions, and why one of them is an order of magnitude harder than the others.
Decision 1. Trigger — how does someone call it
The obvious choice is a hotkey in dev builds. The problem is that QA and players have no way to reach that hotkey in release builds. On mobile, a shake gesture or a three-finger tap is the realistic fallback; on PC titles, a button in the in-game menu works. If you want the state right before a crash, you also need a path with no manual trigger at all — the exception itself is the trigger — because in practice the app dies before anyone gets a chance to press a button.
If you do use a hotkey, check it against the game's own input bindings first. Overlap it with the inventory key and QA learns to press it twice, every time.
Decision 2. Capture — what goes in, and where the difficulty jumps
Screenshots and logs are a day of work. The log callback hook and a ring buffer of the last N lines are laid out in full in the minimal capture guide. What to put in the game-state snapshot — scene name, progress, recent input — is its own post, covered here.
Video is a different order of magnitude. Reading pixels back from the GPU is never free (AsyncGPUReadback avoids the worst of it, but you still need a design that dodges pipeline stalls), and real-time encoding means touching a different hardware encoder per platform. Add a ring buffer that permanently holds the last N seconds in memory, and building a screenshot-plus-log reporter versus one that also captures it is effectively a different project. Logs and state alone already move the needle on reproducibility — it's the item you're allowed to defer to "later."
Decision 3. Send — where does it go
Email, webhooks, a direct issue-tracker integration, or your own backend — each has a different authentication cost and a different ongoing maintenance bill, and this choice affects QA's day-to-day workflow more directly than any of the others. A capacity/auth/searchability/maintenance comparison of all four is in a separate post.
Decision 4. Identify — who, when, on which build
Once reports start piling up, you eventually can't answer "which build did this start in." Seed Application.version, a build number, a session ID, and a device identifier from day one — and treat the device identifier as the one item that needs a platform-policy and privacy check before it ships. Starting with an anonymous session ID and only expanding to consented user identity when you actually need it is the safer default.
Three common traps
| Trap | Symptom | Fix |
|---|---|---|
| The report UI photographs itself | The bug-report button or overlay shows up in its own screenshot | Hide the reporter UI, wait a frame, then capture |
| A failed send silently loses the report | Reports vanish when offline or the server is down | Queue locally, retry, delete only on confirmed success |
| The reporter itself causes a crash | Code meant to catch a bug introduces a new one | Wrap all collection and send code defensively — a failure here should never take the game down |
The third one is the worst. If the reporter crashes while trying to report a crash, nothing survives to the moment you actually needed it.
Where to stop
You don't have to build all of it. Three questions usually settle it.
- Team size and QA headcount: with one or two QA people, a webhook with instant notifications is often enough. As headcount grows, you start needing search and history.
- Platform count: a single platform keeps capture-SDK options open. Across multiple platforms, a custom implementation can actually lower total integration cost.
- How often "can't reproduce" closes tickets: if that's a real, felt problem, video is worth the investment. If not, logs and state may be all you need.
Summary
Trigger, capture, send, and identify are independent variables you can decide separately. Screenshots, logs, and game state are a day or two of work; video is a project of its own. The transport comparison continues in part 3, and snapshot design in part 4. How much of these four decisions Unity's own reporting package covers for you is examined in part 2. A comparison of recording methods themselves will be covered in a separate post.
Our own Rekon has already made these four calls and bundled them into a single package — one hotkey captures video, logs, and game state on the same timeline. If you're building your own, follow the order above and leave video for last.