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}