Bevy debugging and profiling
When to use
Load this when a running app should print logs or record frame time, entity count, or process CPU and memory.
ECS shape
- Logs:
LogPluginis part ofDefaultPlugins. Override it withDefaultPlugins.set(LogPlugin { level, filter, .. }). The page's example usesLevel::DEBUGand a filter string such aswgpu=error,bevy_render=info,bevy_ecs=trace. The same filter works asRUST_LOG. To bring your own collector, disable this plugin onDefaultPlugins. - Diagnostics core:
DiagnosticsPluginadds the core resources.DiagnosticsStoreholds the collection.Diagnosticis a timeline ofDiagnosticMeasurementvalues addressed byDiagnosticPath.RegisterDiagnosticaddsregister_diagnosticonApp. - Built-in plugins:
FrameTimeDiagnosticsPluginadds frame time, fps, and frame count.EntityCountDiagnosticsPluginadds an entity count.SystemInformationDiagnosticsPluginaddscpu_usageandmem_usagepercent.LogDiagnosticsPluginprints diagnostics to the console.FrameCount/FrameCountPlugincount frames since start. - Order: the log-diagnostics example adds these plugins after
DefaultPluginsbecause they use the time plugin for timestamps. - Schedule: built-in plugins measure on their own. Read
DiagnosticsStorewhen a HUD needs the latest sample. Register aDiagnosticonly for a value you measure yourself.
Examples
[dependencies]
bevy = "0.20"
LogPlugin ships inside DefaultPlugins. Replace its level and filter when the default info stream is too quiet or too loud.
use bevy::log::{Level, LogPlugin};
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(LogPlugin {
filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".into(),
level: Level::DEBUG,
..default()
}))
.run();
}
Frame time, entity count, process CPU and memory, and a console dump are four plugins. Add them after DefaultPlugins so timestamps exist.
use bevy::diagnostic::{
EntityCountDiagnosticsPlugin, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin,
SystemInformationDiagnosticsPlugin,
};
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
FrameTimeDiagnosticsPlugin::default(),
EntityCountDiagnosticsPlugin {
max_history_length: 60,
},
SystemInformationDiagnosticsPlugin,
LogDiagnosticsPlugin::default(),
))
.run();
}
A HUD reads the smoothed frame-time sample from the store. The path constants live on the plugin that recorded them.
use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
fn read_frame_time(store: Res<DiagnosticsStore>) {
let Some(frame) = store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else {
return;
};
let Some(ms) = frame.smoothed() else {
return;
};
info!("frame {ms:.2} ms");
}
A custom counter is a DiagnosticPath plus register_diagnostic. One system writes the measurement; nothing else mutates that path.
use bevy::diagnostic::{Diagnostic, DiagnosticPath, Diagnostics, RegisterDiagnostic};
const LIVE_ENEMIES: DiagnosticPath = DiagnosticPath::const_new("game/live_enemies");
#[derive(Component)]
struct Enemy;
fn install(app: &mut App) {
app.register_diagnostic(Diagnostic::new(LIVE_ENEMIES).with_max_history_length(60));
}
fn measure_enemies(mut diagnostics: Diagnostics, enemies: Query<(), With<Enemy>>) {
let count = enemies.iter().count() as f64;
diagnostics.add_measurement(&LIVE_ENEMIES, || count);
}
Scope
- Per-system flame graphs and Tracy stay outside this skill.
LogPlugincovers level and filter. - Custom measurements use
Diagnostic,DiagnosticPath, andregister_diagnosticas in the example above. - This skill does not forward diagnostics into the render app.
Expert Knowledge Delta
Target: Bevy 0.20+.
Core ECS types: Component, Resource, Query, Commands, Startup, Update, Fixed / FixedMain (bevy::time), .chain(), Plugin, DefaultPlugins, MinimalPlugins. Messages and observers are an ownership slot. See bevy-events-messaging for their APIs. Systems run in parallel unless .chain() orders the ones that conflict.
Who owns state
| Piece | Owner | Who may mutate | Schedule |
|---|---|---|---|
| Data on one entity | Component |
One system via Query |
Update or FixedMain |
| One value for the whole app | Resource |
One system | Startup to insert, then the schedule that reads it |
| Spawn | Commands |
The spawning system | Startup for the initial world; later schedules for runtime spawn |
| Order that must not race | .chain() |
The chained systems only | Same schedule |
| Plugin boundary | Plugin |
build registers systems and resources |
Before the app runs |
Borrow rule: two systems that both mutably query the same component must be ordered (.chain()) or split so only one writes. Unrelated systems stay parallel. That is the ECS page rule, not a guess at a system-set API.
Decision
| If you need | Do this | Do not |
|---|---|---|
| Per-entity data | Component + Query |
One mutable blob shared by every entity |
| App-wide config or clock | Resource |
Copy the value onto every entity |
| Stable simulation step | FixedMain and the Fixed clock |
Tie simulation to raw frame Update |
| Startup spawn | Commands in Startup |
Spawn from a render system |
| Cross-system notice that is not shared state | Message or observer slot in bevy-events-messaging |
A new event type invented in this file |
| An API outside the documented set | Read the official Bevy pages | A made-up crate or API |
Load references/ecs-ownership.md for the ownership table.
NEVER
-
Do not pin
/0.20.0/in docs URLs. WHY: links stay onlatest; the 0.20 target is stated once in prose. -
Do not mutably query the same component from two unordered systems. WHY: the schedule runs systems in parallel unless
.chain()orders them. -
Do not invent a physics, navigation, networking, or XR crate here. WHY: this skill only names documented Bevy crates and modules.
-
No Tracy or external profiler setup this file did not open. WHY: Bevy code stays idiomatic ECS and uses only documented Bevy APIs.
-
No third-party profiling crates. WHY: Bevy code stays idiomatic ECS and uses only documented Bevy APIs.
Reference
The library pin on bevy-master stays 0.20.0. Official pages stay on the unversioned site.
Official Documentation
- Log diagnostics example -
LogPlugin, frame time, entity count, system info, and the log dump. - Custom diagnostic example -
DiagnosticPath,register_diagnostic, andDiagnostics. - Examples - Example tree on the
latestbranch.
Related Skills
Prerequisites
- bevy-master - Who owns which piece of state.
- bevy-project-foundations -
DefaultPluginsandApp.
Complements
- bevy-performance - What the frame-time numbers are for.
Downstream / consumers
- None yet.
Master
- bevy-master - Bevy router.
