Skip to main content

qubit_dcl/double_checked/
execution_logger.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! # Execution Logger
11//!
12//! Logging configuration and helpers for the double-checked lock executor.
13//!
14
15use std::fmt;
16
17/// Logger for double-checked execution (condition unmet, prepare failures,
18/// prepare commit failures, and prepare rollback failures).
19///
20/// Each event has its own optional [`log::Level`] and message. `None` means
21/// that event does not emit logs. For prepare-style events the message is a
22/// prefix formatted as `"{prefix}: {error}"`.
23///
24/// [`ExecutionLogger::default`] matches the previous `Option` logger unset
25/// behavior: condition-unmet is silent (`None`); prepare lifecycle lines use
26/// [`log::Level::Error`] with English default prefixes.
27///
28#[derive(Debug, Clone)]
29pub struct ExecutionLogger {
30    /// Log level for the condition-unmet message; `None` skips it.
31    unmet_condition_level: Option<log::Level>,
32
33    /// Message logged when the execution condition is not met.
34    unmet_condition_message: String,
35
36    /// Log level for prepare-action failure lines; `None` skips them.
37    prepare_failed_level: Option<log::Level>,
38
39    /// Prefix for prepare-failure lines, formatted as `"{prefix}: {error}"`.
40    prepare_failed_message: String,
41
42    /// Log level for prepare-commit failure lines; `None` skips them.
43    prepare_commit_failed_level: Option<log::Level>,
44
45    /// Prefix for prepare-commit failure lines, formatted as `"{prefix}: {error}"`.
46    prepare_commit_failed_message: String,
47
48    /// Log level for prepare-rollback failure lines; `None` skips them.
49    prepare_rollback_failed_level: Option<log::Level>,
50
51    /// Prefix for prepare-rollback failure lines, formatted as
52    /// `"{prefix}: {error}"`.
53    prepare_rollback_failed_message: String,
54}
55
56impl Default for ExecutionLogger {
57    /// Returns the logger configuration used when the executor builder does not
58    /// apply any logging overrides.
59    ///
60    /// Condition-unmet logging is disabled ([`ExecutionLogger::unmet_condition_level`]
61    /// is [`None`]). Prepare lifecycle failures log at [`log::Level::Error`] with
62    /// short English default prefixes (see the field defaults on [`ExecutionLogger`]).
63    ///
64    /// # Returns
65    ///
66    /// A new [`ExecutionLogger`] with the values described above.
67    #[inline]
68    fn default() -> Self {
69        Self {
70            unmet_condition_level: None,
71            unmet_condition_message: String::new(),
72            prepare_failed_level: Some(log::Level::Error),
73            prepare_failed_message: "Prepare action failed".to_string(),
74            prepare_commit_failed_level: Some(log::Level::Error),
75            prepare_commit_failed_message: "Prepare commit action failed".to_string(),
76            prepare_rollback_failed_level: Some(log::Level::Error),
77            prepare_rollback_failed_message: "Prepare rollback action failed".to_string(),
78        }
79    }
80}
81
82impl ExecutionLogger {
83    /// Returns the configured level for unmet-condition logging.
84    ///
85    /// [`None`] means the event does not emit a log line.
86    #[inline]
87    pub fn unmet_condition_level(&self) -> Option<log::Level> {
88        self.unmet_condition_level
89    }
90
91    /// Returns the message used for unmet-condition logging.
92    #[inline]
93    pub fn unmet_condition_message(&self) -> &str {
94        &self.unmet_condition_message
95    }
96
97    /// Returns the configured level for prepare-action failures.
98    ///
99    /// [`None`] means the event does not emit a log line.
100    #[inline]
101    pub fn prepare_failed_level(&self) -> Option<log::Level> {
102        self.prepare_failed_level
103    }
104
105    /// Returns the message prefix used for prepare-action failures.
106    #[inline]
107    pub fn prepare_failed_message(&self) -> &str {
108        &self.prepare_failed_message
109    }
110
111    /// Returns the configured level for prepare-commit failures.
112    ///
113    /// [`None`] means the event does not emit a log line.
114    #[inline]
115    pub fn prepare_commit_failed_level(&self) -> Option<log::Level> {
116        self.prepare_commit_failed_level
117    }
118
119    /// Returns the message prefix used for prepare-commit failures.
120    #[inline]
121    pub fn prepare_commit_failed_message(&self) -> &str {
122        &self.prepare_commit_failed_message
123    }
124
125    /// Returns the configured level for prepare-rollback failures.
126    ///
127    /// [`None`] means the event does not emit a log line.
128    #[inline]
129    pub fn prepare_rollback_failed_level(&self) -> Option<log::Level> {
130        self.prepare_rollback_failed_level
131    }
132
133    /// Returns the message prefix used for prepare-rollback failures.
134    #[inline]
135    pub fn prepare_rollback_failed_message(&self) -> &str {
136        &self.prepare_rollback_failed_message
137    }
138
139    /// Updates logging for the case where the double-checked condition is not met
140    /// (the tester returns `false` before or after taking the lock).
141    ///
142    /// When [`Self::unmet_condition_level`] is [`None`], [`Self::log_unmet_condition`]
143    /// becomes a no-op. The `message` is still stored and used if the level is set
144    /// to [`Some`] later.
145    ///
146    /// # Parameters
147    ///
148    /// * `level` - Optional severity for the line written through the `log` crate,
149    ///   or [`None`] to disable this event.
150    /// * `message` - Full line text (not a prefix); passed to [`log::log!`] as the
151    ///   format argument when logging runs.
152    #[inline]
153    pub fn set_unmet_condition(&mut self, level: Option<log::Level>, message: impl Into<String>) {
154        self.unmet_condition_level = level;
155        self.unmet_condition_message = message.into();
156    }
157
158    /// Disables logging for unmet double-checked conditions.
159    ///
160    /// This keeps the stored message unchanged so a later call to
161    /// [`Self::set_unmet_condition`] can re-enable the event with a new message.
162    #[inline]
163    pub fn disable_unmet_condition(&mut self) {
164        self.unmet_condition_level = None;
165    }
166
167    /// Updates logging for a failed optional prepare action (before the lock is taken).
168    ///
169    /// When [`Self::prepare_failed_level`] is [`None`], [`Self::log_prepare_failed`]
170    /// becomes a no-op.
171    ///
172    /// # Parameters
173    ///
174    /// * `level` - Optional severity for the diagnostic line, or [`None`] to disable.
175    /// * `message_prefix` - Text placed before the error; the emitted line has the
176    ///   form `"{prefix}: {error}"`.
177    #[inline]
178    pub fn set_prepare_failure(
179        &mut self,
180        level: Option<log::Level>,
181        message_prefix: impl Into<String>,
182    ) {
183        self.prepare_failed_level = level;
184        self.prepare_failed_message = message_prefix.into();
185    }
186
187    /// Disables logging for prepare-action failures.
188    #[inline]
189    pub fn disable_prepare_failure(&mut self) {
190        self.prepare_failed_level = None;
191    }
192
193    /// Updates logging for a failed prepare commit action (after a successful task
194    /// when prepare had completed).
195    ///
196    /// When [`Self::prepare_commit_failed_level`] is [`None`],
197    /// [`Self::log_prepare_commit_failed`] becomes a no-op.
198    ///
199    /// # Parameters
200    ///
201    /// * `level` - Optional severity for the diagnostic line, or [`None`] to disable.
202    /// * `message_prefix` - Text placed before the error; the emitted line has the
203    ///   form `"{prefix}: {error}"`.
204    #[inline]
205    pub fn set_prepare_commit_failure(
206        &mut self,
207        level: Option<log::Level>,
208        message_prefix: impl Into<String>,
209    ) {
210        self.prepare_commit_failed_level = level;
211        self.prepare_commit_failed_message = message_prefix.into();
212    }
213
214    /// Disables logging for prepare-commit failures.
215    #[inline]
216    pub fn disable_prepare_commit_failure(&mut self) {
217        self.prepare_commit_failed_level = None;
218    }
219
220    /// Updates logging for a failed prepare rollback action (after a failed second
221    /// check or task when prepare had completed).
222    ///
223    /// When [`Self::prepare_rollback_failed_level`] is [`None`],
224    /// [`Self::log_prepare_rollback_failed`] becomes a no-op.
225    ///
226    /// # Parameters
227    ///
228    /// * `level` - Optional severity for the diagnostic line, or [`None`] to disable.
229    /// * `message_prefix` - Text placed before the error; the emitted line has the
230    ///   form `"{prefix}: {error}"`.
231    #[inline]
232    pub fn set_prepare_rollback_failure(
233        &mut self,
234        level: Option<log::Level>,
235        message_prefix: impl Into<String>,
236    ) {
237        self.prepare_rollback_failed_level = level;
238        self.prepare_rollback_failed_message = message_prefix.into();
239    }
240
241    /// Disables logging for prepare-rollback failures.
242    #[inline]
243    pub fn disable_prepare_rollback_failure(&mut self) {
244        self.prepare_rollback_failed_level = None;
245    }
246
247    /// Emits the condition-unmet log line if enabled.
248    ///
249    /// Does nothing when [`Self::unmet_condition_level`] is [`None`]. Otherwise
250    /// writes [`Self::unmet_condition_message`] through the `log` facade at the
251    /// configured level, subject to the crate-wide maximum log level (for example
252    /// set via [`log::set_max_level`] or compile-time filters).
253    #[inline]
254    pub fn log_unmet_condition(&self) {
255        let Some(level) = self.unmet_condition_level else {
256            return;
257        };
258        log::log!(level, "{}", self.unmet_condition_message);
259    }
260
261    /// Emits a diagnostic line when the prepare action fails.
262    ///
263    /// Does nothing when [`Self::prepare_failed_level`] is [`None`]. Otherwise
264    /// logs `"{prefix}: {err}"` at the configured level via the `log` facade,
265    /// where `prefix` is [`Self::prepare_failed_message`], subject to the
266    /// crate-wide maximum log level.
267    ///
268    /// # Type Parameters
269    ///
270    /// * `E` - Displayable error or message value appended after the prefix.
271    ///
272    /// # Parameters
273    ///
274    /// * `err` - Failure to record next to the configured prefix.
275    #[inline]
276    pub fn log_prepare_failed<E: fmt::Display>(&self, err: E) {
277        let Some(level) = self.prepare_failed_level else {
278            return;
279        };
280        log::log!(level, "{}: {}", self.prepare_failed_message, err);
281    }
282
283    /// Emits a diagnostic line when the prepare commit action fails.
284    ///
285    /// Does nothing when [`Self::prepare_commit_failed_level`] is [`None`].
286    /// Otherwise logs `"{prefix}: {err}"` at the configured level, where `prefix`
287    /// is [`Self::prepare_commit_failed_message`], subject to the crate-wide
288    /// maximum log level.
289    ///
290    /// # Type Parameters
291    ///
292    /// * `E` - Displayable error or message value appended after the prefix.
293    ///
294    /// # Parameters
295    ///
296    /// * `err` - Commit failure to record next to the configured prefix.
297    #[inline]
298    pub fn log_prepare_commit_failed<E: fmt::Display>(&self, err: E) {
299        let Some(level) = self.prepare_commit_failed_level else {
300            return;
301        };
302        log::log!(level, "{}: {}", self.prepare_commit_failed_message, err);
303    }
304
305    /// Emits a diagnostic line when the prepare rollback action fails.
306    ///
307    /// Does nothing when [`Self::prepare_rollback_failed_level`] is [`None`].
308    /// Otherwise logs `"{prefix}: {err}"` at the configured level, where `prefix`
309    /// is [`Self::prepare_rollback_failed_message`], subject to the crate-wide
310    /// maximum log level.
311    ///
312    /// # Type Parameters
313    ///
314    /// * `E` - Displayable error or message value appended after the prefix.
315    ///
316    /// # Parameters
317    ///
318    /// * `err` - Rollback failure to record next to the configured prefix.
319    #[inline]
320    pub fn log_prepare_rollback_failed<E: fmt::Display>(&self, err: E) {
321        let Some(level) = self.prepare_rollback_failed_level else {
322            return;
323        };
324        log::log!(level, "{}: {}", self.prepare_rollback_failed_message, err);
325    }
326}