1mod config;
40mod diagnostics;
41mod events;
42mod machine_info;
43mod metrics;
44mod otlp;
45mod panic_handler;
46mod publisher;
47mod session;
48
49pub use config::{FleetConfig, FleetPlugin, OtlpConfig};
50pub use diagnostics::FleetDiagnostic;
51pub use events::{FleetEvent, FleetEventBuffer, forward_serialized_events};
52pub use machine_info::{GpuInfo, MachineInfo};
53pub use metrics::FleetMetric;
54pub use panic_handler::PanicInfo;
55pub use session::{SessionInfo, SessionStats};
56
57use bevy::prelude::*;
58
59fn setup_panic_handler_with_session(
61 session_info: Res<SessionInfo>,
62 sender_res: Res<publisher::TelemetrySender>,
63 config: Res<FleetConfig>,
64 machine_info: Option<Res<MachineInfo>>,
65) {
66 panic_handler::setup_panic_handler();
67 panic_handler::set_session_id(session_info.session_id.clone());
68
69 #[cfg(not(target_arch = "wasm32"))]
71 let panic_sender = sender_res.sender.clone();
72 #[cfg(target_arch = "wasm32")]
73 let panic_sender = sender_res.clone();
74 let session_id = session_info.session_id.clone();
75 let session_start_time = session_info.session_start_time;
76 let session_stats_base = session_info.stats.clone();
77 let app_id = config.app_id.clone();
78 let machine_info_clone = machine_info.map(|info| (*info).clone());
79
80 panic_handler::set_panic_flush_callback(move || {
82 let panics = panic_handler::get_panics();
84 if panics.is_empty() {
85 return;
86 }
87
88 let mut session_stats = session_stats_base.clone();
90 session_stats.panics_captured += panics.len() as u64;
91
92 let payload = publisher::TelemetryPayload {
94 app_id: app_id.clone(),
95 timestamp: std::time::SystemTime::now()
96 .duration_since(std::time::UNIX_EPOCH)
97 .unwrap_or_default()
98 .as_secs(),
99 session_id: session_id.clone(),
100 session_start_time,
101 session_stats,
102 machine_info: machine_info_clone.clone(),
103 metrics: vec![],
104 events: vec![],
105 diagnostics: vec![],
106 panics,
107 };
108
109 #[cfg(not(target_arch = "wasm32"))]
112 {
113 if let Err(err) = panic_sender.blocking_send(payload) {
114 eprintln!("Fleet: Failed to send panic telemetry: {}", err);
115 }
116 }
117
118 #[cfg(target_arch = "wasm32")]
119 {
120 if let Err(err) = panic_sender.send_now(payload) {
121 eprintln!("Fleet: Failed to send panic telemetry: {}", err);
122 }
123 }
124 });
125
126 info!(
127 target: "bevy_fleet",
128 "Fleet telemetry enabled for session {}",
129 session_info.session_id
130 );
131}
132
133impl Plugin for FleetPlugin {
135 fn build(&self, app: &mut App) {
136 if !self.config.enabled {
137 info!(target: "bevy_fleet", "Fleet plugin is disabled");
138 return;
139 }
140
141 session::initialize_session(app);
143
144 app.insert_resource(self.config.clone())
146 .insert_resource(events::FleetEventBuffer::default())
147 .add_message::<FleetEvent>()
148 .add_systems(Startup, publisher::setup_publisher)
149 .add_systems(
151 Startup,
152 setup_panic_handler_with_session.after(publisher::setup_publisher),
153 )
154 .add_systems(
155 PostUpdate,
156 (events::collect_fleet_events, publisher::publish_telemetry).chain(),
157 )
158 .add_systems(Update, publisher::log_publisher_stats);
159
160 machine_info::collect_machine_info(app);
162 }
163}