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    ///
87    /// # Returns
88    ///
89    /// The optional log level for unmet-condition events.
90    #[inline]
91    pub fn unmet_condition_level(&self) -> Option<log::Level> {
92        self.unmet_condition_level
93    }
94
95    /// Returns the message used for unmet-condition logging.
96    ///
97    /// # Returns
98    ///
99    /// The stored unmet-condition log message.
100    #[inline]
101    pub fn unmet_condition_message(&self) -> &str {
102        &self.unmet_condition_message
103    }
104
105    /// Returns the configured level for prepare-action failures.
106    ///
107    /// [`None`] means the event does not emit a log line.
108    ///
109    /// # Returns
110    ///
111    /// The optional log level for prepare-action failure events.
112    #[inline]
113    pub fn prepare_failed_level(&self) -> Option<log::Level> {
114        self.prepare_failed_level
115    }
116
117    /// Returns the message prefix used for prepare-action failures.
118    ///
119    /// # Returns
120    ///
121    /// The prefix placed before prepare-action failure text.
122    #[inline]
123    pub fn prepare_failed_message(&self) -> &str {
124        &self.prepare_failed_message
125    }
126
127    /// Returns the configured level for prepare-commit failures.
128    ///
129    /// [`None`] means the event does not emit a log line.
130    ///
131    /// # Returns
132    ///
133    /// The optional log level for prepare-commit failure events.
134    #[inline]
135    pub fn prepare_commit_failed_level(&self) -> Option<log::Level> {
136        self.prepare_commit_failed_level
137    }
138
139    /// Returns the message prefix used for prepare-commit failures.
140    ///
141    /// # Returns
142    ///
143    /// The prefix placed before prepare-commit failure text.
144    #[inline]
145    pub fn prepare_commit_failed_message(&self) -> &str {
146        &self.prepare_commit_failed_message
147    }
148
149    /// Returns the configured level for prepare-rollback failures.
150    ///
151    /// [`None`] means the event does not emit a log line.
152    ///
153    /// # Returns
154    ///
155    /// The optional log level for prepare-rollback failure events.
156    #[inline]
157    pub fn prepare_rollback_failed_level(&self) -> Option<log::Level> {
158        self.prepare_rollback_failed_level
159    }
160
161    /// Returns the message prefix used for prepare-rollback failures.
162    ///
163    /// # Returns
164    ///
165    /// The prefix placed before prepare-rollback failure text.
166    #[inline]
167    pub fn prepare_rollback_failed_message(&self) -> &str {
168        &self.prepare_rollback_failed_message
169    }
170
171    /// Updates logging for the case where the double-checked condition is not met
172    /// (the tester returns `false` before or after taking the lock).
173    ///
174    /// When [`Self::unmet_condition_level`] is [`None`], [`Self::log_unmet_condition`]
175    /// becomes a no-op. The `message` is still stored and used if the level is set
176    /// to [`Some`] later.
177    ///
178    /// # Parameters
179    ///
180    /// * `level` - Optional severity for the line written through the `log` crate,
181    ///   or [`None`] to disable this event.
182    /// * `message` - Full line text (not a prefix); passed to [`log::log!`] as the
183    ///   format argument when logging runs.
184    #[inline]
185    pub fn set_unmet_condition(&mut self, level: Option<log::Level>, message: impl Into<String>) {
186        self.unmet_condition_level = level;
187        self.unmet_condition_message = message.into();
188    }
189
190    /// Disables logging for unmet double-checked conditions.
191    ///
192    /// This keeps the stored message unchanged so a later call to
193    /// [`Self::set_unmet_condition`] can re-enable the event with a new message.
194    #[inline]
195    pub fn disable_unmet_condition(&mut self) {
196        self.unmet_condition_level = None;
197    }
198
199    /// Updates logging for a failed optional prepare action (before the lock is taken).
200    ///
201    /// When [`Self::prepare_failed_level`] is [`None`], [`Self::log_prepare_failed`]
202    /// becomes a no-op.
203    ///
204    /// # Parameters
205    ///
206    /// * `level` - Optional severity for the diagnostic line, or [`None`] to disable.
207    /// * `message_prefix` - Text placed before the error; the emitted line has the
208    ///   form `"{prefix}: {error}"`.
209    #[inline]
210    pub fn set_prepare_failure(
211        &mut self,
212        level: Option<log::Level>,
213        message_prefix: impl Into<String>,
214    ) {
215        self.prepare_failed_level = level;
216        self.prepare_failed_message = message_prefix.into();
217    }
218
219    /// Disables logging for prepare-action failures.
220    #[inline]
221    pub fn disable_prepare_failure(&mut self) {
222        self.prepare_failed_level = None;
223    }
224
225    /// Updates logging for a failed prepare commit action (after a successful task
226    /// when prepare had completed).
227    ///
228    /// When [`Self::prepare_commit_failed_level`] is [`None`],
229    /// [`Self::log_prepare_commit_failed`] becomes a no-op.
230    ///
231    /// # Parameters
232    ///
233    /// * `level` - Optional severity for the diagnostic line, or [`None`] to disable.
234    /// * `message_prefix` - Text placed before the error; the emitted line has the
235    ///   form `"{prefix}: {error}"`.
236    #[inline]
237    pub fn set_prepare_commit_failure(
238        &mut self,
239        level: Option<log::Level>,
240        message_prefix: impl Into<String>,
241    ) {
242        self.prepare_commit_failed_level = level;
243        self.prepare_commit_failed_message = message_prefix.into();
244    }
245
246    /// Disables logging for prepare-commit failures.
247    #[inline]
248    pub fn disable_prepare_commit_failure(&mut self) {
249        self.prepare_commit_failed_level = None;
250    }
251
252    /// Updates logging for a failed prepare rollback action (after a failed second
253    /// check or task when prepare had completed).
254    ///
255    /// When [`Self::prepare_rollback_failed_level`] is [`None`],
256    /// [`Self::log_prepare_rollback_failed`] becomes a no-op.
257    ///
258    /// # Parameters
259    ///
260    /// * `level` - Optional severity for the diagnostic line, or [`None`] to disable.
261    /// * `message_prefix` - Text placed before the error; the emitted line has the
262    ///   form `"{prefix}: {error}"`.
263    #[inline]
264    pub fn set_prepare_rollback_failure(
265        &mut self,
266        level: Option<log::Level>,
267        message_prefix: impl Into<String>,
268    ) {
269        self.prepare_rollback_failed_level = level;
270        self.prepare_rollback_failed_message = message_prefix.into();
271    }
272
273    /// Disables logging for prepare-rollback failures.
274    #[inline]
275    pub fn disable_prepare_rollback_failure(&mut self) {
276        self.prepare_rollback_failed_level = None;
277    }
278
279    /// Emits the condition-unmet log line if enabled.
280    ///
281    /// Does nothing when [`Self::unmet_condition_level`] is [`None`]. Otherwise
282    /// writes [`Self::unmet_condition_message`] through the `log` facade at the
283    /// configured level, subject to the crate-wide maximum log level (for example
284    /// set via [`log::set_max_level`] or compile-time filters).
285    #[inline]
286    pub fn log_unmet_condition(&self) {
287        let Some(level) = self.unmet_condition_level else {
288            return;
289        };
290        log::log!(level, "{}", self.unmet_condition_message);
291    }
292
293    /// Emits a diagnostic line when the prepare action fails.
294    ///
295    /// Does nothing when [`Self::prepare_failed_level`] is [`None`]. Otherwise
296    /// logs `"{prefix}: {err}"` at the configured level via the `log` facade,
297    /// where `prefix` is [`Self::prepare_failed_message`], subject to the
298    /// crate-wide maximum log level.
299    ///
300    /// # Type Parameters
301    ///
302    /// * `E` - Displayable error or message value appended after the prefix.
303    ///
304    /// # Parameters
305    ///
306    /// * `err` - Failure to record next to the configured prefix.
307    #[inline]
308    pub fn log_prepare_failed<E: fmt::Display>(&self, err: E) {
309        let Some(level) = self.prepare_failed_level else {
310            return;
311        };
312        log::log!(level, "{}: {}", self.prepare_failed_message, err);
313    }
314
315    /// Emits a diagnostic line when the prepare commit action fails.
316    ///
317    /// Does nothing when [`Self::prepare_commit_failed_level`] is [`None`].
318    /// Otherwise logs `"{prefix}: {err}"` at the configured level, where `prefix`
319    /// is [`Self::prepare_commit_failed_message`], subject to the crate-wide
320    /// maximum log level.
321    ///
322    /// # Type Parameters
323    ///
324    /// * `E` - Displayable error or message value appended after the prefix.
325    ///
326    /// # Parameters
327    ///
328    /// * `err` - Commit failure to record next to the configured prefix.
329    #[inline]
330    pub fn log_prepare_commit_failed<E: fmt::Display>(&self, err: E) {
331        let Some(level) = self.prepare_commit_failed_level else {
332            return;
333        };
334        log::log!(level, "{}: {}", self.prepare_commit_failed_message, err);
335    }
336
337    /// Emits a diagnostic line when the prepare rollback action fails.
338    ///
339    /// Does nothing when [`Self::prepare_rollback_failed_level`] is [`None`].
340    /// Otherwise logs `"{prefix}: {err}"` at the configured level, where `prefix`
341    /// is [`Self::prepare_rollback_failed_message`], subject to the crate-wide
342    /// maximum log level.
343    ///
344    /// # Type Parameters
345    ///
346    /// * `E` - Displayable error or message value appended after the prefix.
347    ///
348    /// # Parameters
349    ///
350    /// * `err` - Rollback failure to record next to the configured prefix.
351    #[inline]
352    pub fn log_prepare_rollback_failed<E: fmt::Display>(&self, err: E) {
353        let Some(level) = self.prepare_rollback_failed_level else {
354            return;
355        };
356        log::log!(level, "{}: {}", self.prepare_rollback_failed_message, err);
357    }
358}