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}