Quick answer: Use setContext() to maintain a current-state snapshot and addBreadcrumb() to build a trail of recent events; together they answer the single most valuable question in crash diagnosis — what was the player doing when it happened.
A stack trace tells you where your game crashed. It rarely tells you why. The “why” almost always lives in the game state at crash time: which level the player was in, which quest was active, how full their inventory was, what the last three things they did were. Without that context, debugging a crash is like diagnosing a car breakdown from a photo of the engine warning light — technically relevant information, but not enough to fix anything. Custom game events are how you attach the game state to the crash report so that the “why” travels with the “where.”
Breadcrumbs vs. Context: Two Complementary Tools
Crash reporting SDKs typically provide two distinct mechanisms for attaching game-specific information to crash reports, and understanding the difference between them is the foundation of effective custom event logging.
Breadcrumbs are a chronological trail of recent events. Each time you call addBreadcrumb(), you append a timestamped entry to an in-memory ring buffer — typically the last 20 to 50 events. When a crash occurs, the entire breadcrumb trail is captured and attached to the report. The trail shows you what happened in the seconds and minutes before the crash: “player entered Level 3,” “checkpoint reached,” “combat started,” “boss phase 2 triggered,” “crash.” This sequence is often more valuable than the stack trace itself.
Context is a key-value snapshot of the current game state. Each call to setContext() overwrites the previous value for that key. At crash time, the current context — all the key-value pairs you’ve set — is captured as a snapshot. This gives you a precise picture of the game state at the moment of failure: current level, player health, active quest ID, flags, and any other state you care to track.
Use both. Breadcrumbs answer “what happened leading up to the crash?” Context answers “what was the state at crash time?” Together they give you a complete picture.
What to Log: The Most Useful Events
Not every event in your game deserves a breadcrumb. Logging too much floods the ring buffer with noise and displaces the events that actually matter. The goal is to capture the key state transitions that would help you reproduce and understand a crash, not a complete record of every frame.
High-value breadcrumb events include:
- Scene and level transitions: “Loaded scene: dungeon_level_3” — this is almost always relevant to a crash
- Checkpoint and save events: “Checkpoint reached: village_gate” tells you how far into a level the player was
- Significant combat events: boss spawns, phase transitions, player death events
- Dialogue and cutscene triggers: useful for crashes in scripted sequences
- Inventory and equipment changes: “Equipped: sword_of_flame” catches cases where a specific item combination triggers a crash
- Settings changes: “Graphics quality changed to: low” can identify settings-related crashes
- Network events: connection state changes, matchmaking events, for multiplayer games
High-value context keys include: current_level, player_health, active_quest, game_version, save_slot, difficulty, player_position, enemies_alive.
Unity C# Example
In Unity, you can integrate Bugnet’s custom event API throughout your game systems. Here’s a pattern for a level management system:
// In your level manager, set context when the level changes
public void LoadLevel(string levelName)
{
Bugnet.SetContext("current_level", levelName);
Bugnet.SetContext("level_load_time", Time.time.ToString());
Bugnet.AddBreadcrumb($"Level loaded: {levelName}");
SceneManager.LoadScene(levelName);
}
// In your player health component, update context on damage
public void TakeDamage(float amount)
{
health -= amount;
Bugnet.SetContext("player_health", health.ToString("F1"));
if (health <= 0)
Bugnet.AddBreadcrumb("Player died");
}
// In your checkpoint system
public void ReachCheckpoint(string checkpointId)
{
Bugnet.SetContext("last_checkpoint", checkpointId);
Bugnet.AddBreadcrumb($"Checkpoint: {checkpointId}");
}
GDScript Example
In Godot, the same patterns apply using GDScript. Here’s how you’d instrument a quest system and a combat manager:
# In your quest manager autoload
func start_quest(quest_id: String) -> void:
active_quest = quest_id
Bugnet.set_context("active_quest", quest_id)
Bugnet.add_breadcrumb("Quest started: " + quest_id)
func complete_objective(objective_id: String) -> void:
Bugnet.add_breadcrumb("Objective complete: " + objective_id)
# In your combat manager
func spawn_boss(boss_name: String) -> void:
Bugnet.set_context("active_boss", boss_name)
Bugnet.add_breadcrumb("Boss spawned: " + boss_name)
func boss_phase_change(phase: int) -> void:
Bugnet.set_context("boss_phase", str(phase))
Bugnet.add_breadcrumb("Boss phase: " + str(phase))
What Not to Log
Two categories of data should never appear in your crash reports: personally identifiable information and large serialized objects.
PII includes usernames, email addresses, real names, payment information, and precise geolocation. Even if a player consented to crash reporting, they did not consent to having their name or email attached to every crash event. Use opaque identifiers — a UUID you generate at first launch — if you need to track whether multiple crashes came from the same player. Bugnet’s SDK does this automatically via its anonymous session ID.
Large serialized objects — full inventory dumps, complete game state JSON, serialized save files — should not go into breadcrumbs or context even when they don’t contain PII. They bloat the crash report payload, slow down transmission, and make the crash dashboard harder to read. Instead, log the key identifiers: inventory_count, equipped_weapon_id, active_buffs as a comma-separated list of IDs. Enough to understand the state, not a full serialization.
Building the Logging Habit During Development
The worst time to add custom event logging is after launch, when you’re staring at a crash report with no context and wishing you’d instrumented the relevant system. The best time is during development, as a habit integrated into how you write game systems.
A practical approach: every time you write a new significant game system — a quest manager, a dialogue system, a combat manager — add a “what would I want to know if this crashed?” step before considering the system complete. Add the setContext() and addBreadcrumb() calls that answer that question. This adds less than five minutes per system and pays dividends every time a crash report comes in for that system post-launch.
Make custom event logging part of your code review checklist for new features. If a PR adds a new game system and doesn’t include any crash context instrumentation, that’s a review comment: “add breadcrumbs for the key state transitions in this system.”
Reading the Context in Bugnet
Once you’ve instrumented your game, the payoff appears in the Bugnet crash detail view. Each crash report shows the full context snapshot at crash time and the breadcrumb trail in reverse-chronological order. The combination answers the question every developer asks when they see a crash report: “what was the player doing?”
In practice, a crash report that previously read “NullReferenceException in CombatManager.Update at line 247” now reads: “NullReferenceException in CombatManager.Update at line 247 — context: level=dungeon_3, active_boss=elder_lich, boss_phase=2, player_health=12.5; breadcrumbs: Boss spawned: elder_lich, Boss phase: 2, Player used: healing_potion, Boss phase: 2 (second transition triggered within 1 second).” That’s a reproducible bug. The double phase transition is the trigger. Without context, it’s a mystery.
Instrument your systems during development and you’ll spend hours on post-launch bugs instead of weeks.“The stack trace tells you where the crash happened. The breadcrumbs tell you how the player got there. You need both.”