bevy_ecs/error/
handler.rs

1#[cfg(feature = "configurable_error_handler")]
2use bevy_platform::sync::OnceLock;
3use core::fmt::Display;
4
5use crate::{component::Tick, error::BevyError};
6use alloc::borrow::Cow;
7
8/// Context for a [`BevyError`] to aid in debugging.
9#[derive(Debug, PartialEq, Eq, Clone)]
10pub enum ErrorContext {
11    /// The error occurred in a system.
12    System {
13        /// The name of the system that failed.
14        name: Cow<'static, str>,
15        /// The last tick that the system was run.
16        last_run: Tick,
17    },
18    /// The error occurred in a run condition.
19    RunCondition {
20        /// The name of the run condition that failed.
21        name: Cow<'static, str>,
22        /// The last tick that the run condition was evaluated.
23        last_run: Tick,
24    },
25    /// The error occurred in a command.
26    Command {
27        /// The name of the command that failed.
28        name: Cow<'static, str>,
29    },
30    /// The error occurred in an observer.
31    Observer {
32        /// The name of the observer that failed.
33        name: Cow<'static, str>,
34        /// The last tick that the observer was run.
35        last_run: Tick,
36    },
37}
38
39impl Display for ErrorContext {
40    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        match self {
42            Self::System { name, .. } => {
43                write!(f, "System `{}` failed", name)
44            }
45            Self::Command { name } => write!(f, "Command `{}` failed", name),
46            Self::Observer { name, .. } => {
47                write!(f, "Observer `{}` failed", name)
48            }
49            Self::RunCondition { name, .. } => {
50                write!(f, "Run condition `{}` failed", name)
51            }
52        }
53    }
54}
55
56impl ErrorContext {
57    /// The name of the ECS construct that failed.
58    pub fn name(&self) -> &str {
59        match self {
60            Self::System { name, .. }
61            | Self::Command { name, .. }
62            | Self::Observer { name, .. }
63            | Self::RunCondition { name, .. } => name,
64        }
65    }
66
67    /// A string representation of the kind of ECS construct that failed.
68    ///
69    /// This is a simpler helper used for logging.
70    pub fn kind(&self) -> &str {
71        match self {
72            Self::System { .. } => "system",
73            Self::Command { .. } => "command",
74            Self::Observer { .. } => "observer",
75            Self::RunCondition { .. } => "run condition",
76        }
77    }
78}
79
80/// A global error handler. This can be set at startup, as long as it is set before
81/// any uses. This should generally be configured _before_ initializing the app.
82///
83/// This should be set inside of your `main` function, before initializing the Bevy app.
84/// The value of this error handler can be accessed using the [`default_error_handler`] function,
85/// which calls [`OnceLock::get_or_init`] to get the value.
86///
87/// **Note:** this is only available when the `configurable_error_handler` feature of `bevy_ecs` (or `bevy`) is enabled!
88///
89/// # Example
90///
91/// ```
92/// # use bevy_ecs::error::{GLOBAL_ERROR_HANDLER, warn};
93/// GLOBAL_ERROR_HANDLER.set(warn).expect("The error handler can only be set once, globally.");
94/// // initialize Bevy App here
95/// ```
96///
97/// To use this error handler in your app for custom error handling logic:
98///
99/// ```rust
100/// use bevy_ecs::error::{default_error_handler, GLOBAL_ERROR_HANDLER, BevyError, ErrorContext, panic};
101///
102/// fn handle_errors(error: BevyError, ctx: ErrorContext) {
103///    let error_handler = default_error_handler();
104///    error_handler(error, ctx);        
105/// }
106/// ```
107///
108/// # Warning
109///
110/// As this can *never* be overwritten, library code should never set this value.
111#[cfg(feature = "configurable_error_handler")]
112pub static GLOBAL_ERROR_HANDLER: OnceLock<fn(BevyError, ErrorContext)> = OnceLock::new();
113
114/// The default error handler. This defaults to [`panic()`],
115/// but if set, the [`GLOBAL_ERROR_HANDLER`] will be used instead, enabling error handler customization.
116/// The `configurable_error_handler` feature must be enabled to change this from the panicking default behavior,
117/// as there may be runtime overhead.
118#[inline]
119pub fn default_error_handler() -> fn(BevyError, ErrorContext) {
120    #[cfg(not(feature = "configurable_error_handler"))]
121    return panic;
122
123    #[cfg(feature = "configurable_error_handler")]
124    return *GLOBAL_ERROR_HANDLER.get_or_init(|| panic);
125}
126
127macro_rules! inner {
128    ($call:path, $e:ident, $c:ident) => {
129        $call!(
130            "Encountered an error in {} `{}`: {}",
131            $c.kind(),
132            $c.name(),
133            $e
134        );
135    };
136}
137
138/// Error handler that panics with the system error.
139#[track_caller]
140#[inline]
141pub fn panic(error: BevyError, ctx: ErrorContext) {
142    inner!(panic, error, ctx);
143}
144
145/// Error handler that logs the system error at the `error` level.
146#[track_caller]
147#[inline]
148pub fn error(error: BevyError, ctx: ErrorContext) {
149    inner!(log::error, error, ctx);
150}
151
152/// Error handler that logs the system error at the `warn` level.
153#[track_caller]
154#[inline]
155pub fn warn(error: BevyError, ctx: ErrorContext) {
156    inner!(log::warn, error, ctx);
157}
158
159/// Error handler that logs the system error at the `info` level.
160#[track_caller]
161#[inline]
162pub fn info(error: BevyError, ctx: ErrorContext) {
163    inner!(log::info, error, ctx);
164}
165
166/// Error handler that logs the system error at the `debug` level.
167#[track_caller]
168#[inline]
169pub fn debug(error: BevyError, ctx: ErrorContext) {
170    inner!(log::debug, error, ctx);
171}
172
173/// Error handler that logs the system error at the `trace` level.
174#[track_caller]
175#[inline]
176pub fn trace(error: BevyError, ctx: ErrorContext) {
177    inner!(log::trace, error, ctx);
178}
179
180/// Error handler that ignores the system error.
181#[track_caller]
182#[inline]
183pub fn ignore(_: BevyError, _: ErrorContext) {}