Skip to main content

qubit_lock/double_checked/
execution_logger.rs

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