Skip to main content

ferrilog_core/
builder.rs

1use std::{io, time::Duration};
2
3use crate::{Level, QueueFullCallback, QueueFullPolicy, logger, output::RotationConfig};
4
5/// RAII guard returned by [`FlashLogBuilder::start`]. Automatically stops the
6/// polling thread and flushes all pending output when dropped.
7pub struct FlashLogGuard {
8    _private: (),
9}
10
11impl Drop for FlashLogGuard {
12    fn drop(&mut self) {
13        let _ = logger::stop_polling_thread();
14        let _ = logger::poll(true);
15    }
16}
17
18/// Builder for configuring and starting the ferrilog logger.
19///
20/// # Example
21///
22/// ```ignore
23/// let _guard = ferrilog::builder()
24///     .level(ferrilog::INFO)
25///     .log_file("/var/log/app.log")
26///     .rotation(RotationConfig::by_size(100_000_000, 5))
27///     .flush_delay(Duration::from_secs(3))
28///     .flush_buffer_size(8192)
29///     .flush_on(ferrilog::ERR)
30///     .poller_interval(Duration::from_millis(1))
31///     .poller_core_affinity(3)
32///     .start()?;
33/// // guard drop automatically stops poller and flushes
34/// ```
35pub struct FlashLogBuilder {
36    level: Level,
37    flush_delay: Duration,
38    flush_buffer_size: u32,
39    flush_level: Level,
40    queue_full_policy: QueueFullPolicy,
41    queue_full_callback: Option<QueueFullCallback>,
42    log_file: Option<String>,
43    rotation: Option<RotationConfig>,
44    header_pattern: Option<String>,
45    poller_interval: Duration,
46    poller_core_affinity: Option<usize>,
47}
48
49impl Default for FlashLogBuilder {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl FlashLogBuilder {
56    /// Creates a new builder with default settings.
57    pub fn new() -> Self {
58        Self {
59            level: Level::DBG,
60            flush_delay: Duration::from_secs(3),
61            flush_buffer_size: 8 * 1024,
62            flush_level: Level::OFF,
63            queue_full_policy: QueueFullPolicy::Drop,
64            queue_full_callback: None,
65            log_file: None,
66            rotation: None,
67            header_pattern: None,
68            poller_interval: Duration::from_millis(1),
69            poller_core_affinity: None,
70        }
71    }
72
73    /// Set the minimum log level.
74    pub fn level(mut self, level: Level) -> Self {
75        self.level = level;
76        self
77    }
78
79    /// Set the flush delay duration.
80    pub fn flush_delay(mut self, delay: Duration) -> Self {
81        self.flush_delay = delay;
82        self
83    }
84
85    /// Set the flush buffer size in bytes.
86    pub fn flush_buffer_size(mut self, bytes: u32) -> Self {
87        self.flush_buffer_size = bytes;
88        self
89    }
90
91    /// Set the level at or above which an immediate flush is triggered.
92    pub fn flush_on(mut self, level: Level) -> Self {
93        self.flush_level = level;
94        self
95    }
96
97    /// Set the queue-full policy.
98    pub fn queue_full_policy(mut self, policy: QueueFullPolicy) -> Self {
99        self.queue_full_policy = policy;
100        self
101    }
102
103    /// Set the queue-full callback.
104    pub fn queue_full_callback(mut self, callback: QueueFullCallback) -> Self {
105        self.queue_full_callback = Some(callback);
106        self
107    }
108
109    /// Set the log output file path.
110    pub fn log_file(mut self, path: impl Into<String>) -> Self {
111        self.log_file = Some(path.into());
112        self
113    }
114
115    /// Set the log file rotation config. Requires `log_file` to be set.
116    pub fn rotation(mut self, config: RotationConfig) -> Self {
117        self.rotation = Some(config);
118        self
119    }
120
121    /// Set a custom header pattern string.
122    pub fn header_pattern(mut self, pattern: impl Into<String>) -> Self {
123        self.header_pattern = Some(pattern.into());
124        self
125    }
126
127    /// Set the poller thread interval.
128    pub fn poller_interval(mut self, interval: Duration) -> Self {
129        self.poller_interval = interval;
130        self
131    }
132
133    /// Pin the poller thread to the given CPU core (Linux only; ignored on
134    /// other platforms).
135    pub fn poller_core_affinity(mut self, core_id: usize) -> Self {
136        self.poller_core_affinity = Some(core_id);
137        self
138    }
139
140    /// Apply all configuration and start the background polling thread.
141    ///
142    /// Returns a [`FlashLogGuard`] that stops the poller and flushes when
143    /// dropped.
144    pub fn start(self) -> io::Result<FlashLogGuard> {
145        // Pre-allocate the calling thread's buffer to avoid first-log overhead
146        crate::thread_buffer::preallocate();
147
148        logger::set_level(self.level);
149        logger::set_flush_delay(self.flush_delay.as_nanos() as i64);
150        logger::set_flush_buffer_size(self.flush_buffer_size);
151        logger::flush_on(self.flush_level);
152        logger::set_queue_full_policy(self.queue_full_policy);
153        logger::set_queue_full_callback(self.queue_full_callback);
154
155        if let Some(pattern) = &self.header_pattern {
156            logger::set_header_pattern(pattern)?;
157        }
158
159        if let Some(path) = &self.log_file {
160            if let Some(rotation) = self.rotation {
161                logger::set_log_file_with_rotation(path, rotation)?;
162            } else {
163                logger::set_log_file(path)?;
164            }
165        }
166
167        let interval_ns = self.poller_interval.as_nanos() as i64;
168        if let Some(core_id) = self.poller_core_affinity {
169            logger::start_polling_thread_on_core(interval_ns, core_id)?;
170        } else {
171            logger::start_polling_thread(interval_ns)?;
172        }
173
174        Ok(FlashLogGuard { _private: () })
175    }
176}
177
178/// Creates a new [`FlashLogBuilder`] for configuring the logger.
179pub fn builder() -> FlashLogBuilder {
180    FlashLogBuilder::new()
181}