mod config;
mod diagnostics;
mod events;
mod machine_info;
mod metrics;
mod otlp;
mod panic_handler;
mod publisher;
mod session;
pub use config::{FleetConfig, FleetPlugin, OtlpConfig};
pub use diagnostics::FleetDiagnostic;
pub use events::{FleetEvent, FleetEventBuffer, forward_serialized_events};
pub use machine_info::{GpuInfo, MachineInfo};
pub use metrics::FleetMetric;
pub use panic_handler::PanicInfo;
pub use session::{SessionInfo, SessionStats};
use bevy::prelude::*;
fn setup_panic_handler_with_session(
session_info: Res<SessionInfo>,
sender_res: Res<publisher::TelemetrySender>,
config: Res<FleetConfig>,
machine_info: Option<Res<MachineInfo>>,
) {
panic_handler::setup_panic_handler();
panic_handler::set_session_id(session_info.session_id.clone());
#[cfg(not(target_arch = "wasm32"))]
let panic_sender = sender_res.sender.clone();
#[cfg(target_arch = "wasm32")]
let panic_sender = sender_res.clone();
let session_id = session_info.session_id.clone();
let session_start_time = session_info.session_start_time;
let session_stats_base = session_info.stats.clone();
let app_id = config.app_id.clone();
let machine_info_clone = machine_info.map(|info| (*info).clone());
panic_handler::set_panic_flush_callback(move || {
let panics = panic_handler::get_panics();
if panics.is_empty() {
return;
}
let mut session_stats = session_stats_base.clone();
session_stats.panics_captured += panics.len() as u64;
let payload = publisher::TelemetryPayload {
app_id: app_id.clone(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
session_id: session_id.clone(),
session_start_time,
session_stats,
machine_info: machine_info_clone.clone(),
metrics: vec![],
events: vec![],
diagnostics: vec![],
panics,
};
#[cfg(not(target_arch = "wasm32"))]
{
if let Err(err) = panic_sender.blocking_send(payload) {
eprintln!("Fleet: Failed to send panic telemetry: {}", err);
}
}
#[cfg(target_arch = "wasm32")]
{
if let Err(err) = panic_sender.send_now(payload) {
eprintln!("Fleet: Failed to send panic telemetry: {}", err);
}
}
});
info!(
target: "bevy_fleet",
"Fleet telemetry enabled for session {}",
session_info.session_id
);
}
impl Plugin for FleetPlugin {
fn build(&self, app: &mut App) {
if !self.config.enabled {
info!(target: "bevy_fleet", "Fleet plugin is disabled");
return;
}
session::initialize_session(app);
app.insert_resource(self.config.clone())
.insert_resource(events::FleetEventBuffer::default())
.add_message::<FleetEvent>()
.add_systems(Startup, publisher::setup_publisher)
.add_systems(
Startup,
setup_panic_handler_with_session.after(publisher::setup_publisher),
)
.add_systems(
PostUpdate,
(events::collect_fleet_events, publisher::publish_telemetry).chain(),
)
.add_systems(Update, publisher::log_publisher_stats);
machine_info::collect_machine_info(app);
}
}