timber_rust/
manager.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com
3
4use crate::service::FvnBuildHasher;
5use crate::{logger::Loggable, Logger, LoggerFactory};
6use std::collections::HashMap;
7
8/// [`LogManager`] acts as a registry and dispatcher for multiple logging channels.
9///
10/// It maintains a `default_logger` for general use and a [`HashMap`] of specialized
11/// channels, using a high-performance FNV hasher for fast lookups.
12///
13/// ### Key Features
14/// * **Default Channel**: The default channels is always set. By default, an implementation of [`crate::SilentLogger`].
15/// * **Channel Routing**: Dispatch logs to specific destinations via string identifiers.
16/// * **Fluent Interface**: Methods like [`log()`][LogManager::log()] return `&Self` to allow for chaining.
17/// * **FVN-1 Hash**: Uses FVN-1 hash to store the logs. Be careful about collisions!
18pub struct LogManager {
19    default_logger: Logger,
20    /// Uses a non-cryptographic FVN hasher for O(1) lookups on channel names.
21    channel_loggers: HashMap<String, Logger, FvnBuildHasher>,
22}
23
24impl LogManager {
25    /// Creates a new [`LogManager`] with a [`SilentLogger`][crate::SilentLogger] implementation as the default.
26    /// Logs sent to a new manager will be discarded until a default is set.
27    pub fn new() -> Self {
28        LogManager {
29            default_logger: LoggerFactory::silent(),
30            channel_loggers: HashMap::default(),
31        }
32    }
33
34    /// Creates a manager with a pre-configured default [`Logger`].
35    pub fn new_default(default_logger: Logger) -> Self {
36        LogManager {
37            default_logger,
38            channel_loggers: HashMap::default(),
39        }
40    }
41
42    /// Retrieves a [`Logger`] for a specific channel.
43    ///
44    /// If the channel name is `"default"`, it returns the default [`Logger`].
45    /// Returns [`None`] if the requested channel has not been registered.
46    ///
47    /// # Arguments
48    /// * `channel` - A string slice or type that can be referenced as a string.
49    pub fn get_logger<Q>(&self, channel: &Q) -> Option<Logger>
50    where
51        Q: AsRef<str> + ?Sized,
52    {
53        let channel = channel.as_ref();
54        if channel == "default" {
55            return Some(self.default_logger.clone());
56        }
57        self.channel_loggers.get(channel).cloned() // Standard way to clone the inner Logger
58    }
59
60    /// Registers or updates a [`Logger`] for a specific channel name.
61    ///
62    /// Using the name `"default"` will overwrite the `default_logger`.
63    pub fn set_logger<S>(&mut self, channel: S, logger: Logger)
64    where
65        S: Into<String>,
66    {
67        let channel = channel.into();
68        if channel == "default" {
69            self.default_logger = logger;
70        } else {
71            self.channel_loggers.insert(channel, logger);
72        }
73    }
74
75    /// Registers or updates a [`Logger`] for a specific channel name.
76    ///
77    /// Using the name `"default"` will do nothing and return [`None`].
78    pub fn remove_logger<Q>(&mut self, channel: &Q) -> Option<Logger>
79    where
80        Q: AsRef<str> + ?Sized,
81    {
82        let channel = channel.as_ref();
83        if channel != "default" {
84            return self.channel_loggers.remove(channel);
85        }
86        None
87    }
88
89    /// Returns a cloned instance of the current primary (default) [`Logger`].
90    pub fn get_default_logger(&self) -> Logger {
91        self.default_logger.clone()
92    }
93
94    /// Directly updates the default [`Logger`], bypassing the channel map.
95    pub fn set_default_logger(&mut self, default_logger: Logger) {
96        self.default_logger = default_logger;
97    }
98
99    /// Dispatches a message to the default [`Logger`].
100    ///
101    /// # Example
102    /// ```
103    /// # use timber_rust::{LogLevel, LogManager};
104    /// let log_manager = LogManager::new();
105    /// log_manager.log(("INFO","System started"))
106    ///     .log((LogLevel::Info,"Initializing..."));
107    /// ```
108    pub fn log<T: Loggable>(&self, message: T) -> &Self {
109        let message = message.to_message();
110        self.default_logger.log(message);
111        self
112    }
113
114    /// Dispatches a message to a specific named channel.
115    ///
116    /// **Warning**: If the channel does not exist, the log will be silently dropped.
117    /// Use [`get_logger()`][LogManager::get_logger()] beforehand if you need to guarantee delivery.
118    ///
119    /// # Example
120    /// ```
121    /// # use timber_rust::{LogLevel, LogManager};
122    /// let log_manager = LogManager::new();
123    /// log_manager.log_channel("default", ("INFO","System started"))
124    ///     .log_channel("default", (LogLevel::Info,"Initializing..."));
125    /// ```
126    pub fn log_channel<Q, L>(&self, channel: &Q, message: L) -> &Self
127    where
128        Q: AsRef<str> + ?Sized,
129        L: Loggable,
130    {
131        let message = message.to_message();
132        if let Some(logger) = self.get_logger(channel) {
133            logger.log(message);
134        }
135        self
136    }
137}