timber_rust/logger/
direct.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com
3
4use crate::{LoggerImpl, LoggerStatus, Message, Service};
5use std::any::Any;
6
7/// A synchronous logging implementation that performs immediate writes.
8///
9/// [`SyncLogger`][`Direct`] blocks the current thread until the [`Service`] successfully
10/// processes the [`Message`] or the `max_retries` threshold is reached.
11///
12/// Because it implements [`LoggerImpl`], it can be wrapped in the primary
13/// [`Logger`][`crate::Logger`] struct and shared across threads.
14pub struct Direct {
15    service: Box<dyn Service + Send + Sync>,
16    max_retries: usize,
17}
18
19impl Direct {
20    /// Creates a new [`DirectLogger`][`Direct`] with a specified [backend service][`Service`] and retry policy.
21    ///
22    /// # Arguments
23    /// * `service` - The backend execution logic (e.g., File, Console).
24    /// * `max_retries` - How many additional times to try if the first attempt fails.
25    pub fn new(service: Box<dyn Service + Send + Sync>, max_retries: usize) -> Box<Self> {
26        Box::new(Self {
27            service,
28            max_retries,
29        })
30    }
31
32    /// Returns the base service
33    pub fn get_service(&self) -> &dyn Service {
34        self.service.as_ref()
35    }
36
37    /// Takes the base service and destroys the logger implementation
38    pub fn take_service(self) -> Box<dyn Service + Send + Sync> {
39        self.service
40    }
41}
42
43impl LoggerImpl for Direct {
44    /// Delegates the health check to the underlying [Service].
45    fn status(&self) -> LoggerStatus {
46        self.service.status()
47    }
48
49    /// Attempts to log the message synchronously.
50    ///
51    /// If the first attempt fails, it will retry up to `max_retries` times.
52    /// Since the high-level API is fire-and-forget, failures after all
53    /// retries are exhausted are currently dropped silently to maintain
54    /// the [`LoggerImpl`] contract.
55    ///
56    /// If all retries fail the fallback is used.
57    fn log(&self, message: Message) {
58        // Initial attempt
59        let mut result = self.service.work(&message);
60        if result.is_ok() {
61            return;
62        }
63
64        // Retry logic
65        for _ in 0..self.max_retries {
66            result = self.service.work(&message);
67            if result.is_ok() {
68                return;
69            }
70        }
71
72        self.service.fallback(&result.unwrap_err(), &message);
73    }
74
75    /// Enables runtime downcasting to [`DirectLogger`][`Direct`].
76    fn as_any(&self) -> &dyn Any {
77        self
78    }
79}