timber_rust 2.0.1

A high-performance, asynchronous logging library with support for Grafana Loki and AWS CloudWatch.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com

use crate::service::{StandardWriteMessageFormatter, WriteMessageFormatter};
use crate::{service, Concurrency, DirectLogger, Logger, QueuedLogger};

/// A specialized factory for creating loggers that write to memory-oriented destinations.
///
/// The `FmtFactory` acts as the entry point for any output that implements [`std::fmt::Write`].
/// It allows you to configure global retry and threading policies before selecting a
/// specific output target (like a String writer).
///
/// ### Default Configuration
/// - **Max Retries**: 3 (Attempts to re-send if a write failure occurs).
/// - **Worker Count**: 1 (Single background thread to maintain message order).
pub struct FmtWrite {
    max_retries: usize,
    worker_count: usize,
}

/// A concrete builder state for a specific writer type `W`.
///
/// Once a writer is provided (via [`FmtWrite::string`], etc.),
/// this struct allows you to finalize the logger by choosing a [`Concurrency`] model.
pub struct TypedFmtWrite<W>
where
    W: std::fmt::Write + Send + Sync + 'static,
{
    writer: W,
    max_retries: usize,
    worker_count: usize,
}

/// A concrete builder state for a specific boxed writer type `W`.
///
/// Once a writer is provided (via [`FmtWrite::boxed`], etc.),
/// this struct allows you to finalize the logger by choosing a [`Concurrency`] model.
pub struct BoxedFmtWrite {
    writer: Box<dyn std::fmt::Write + Send + Sync>,
    max_retries: usize,
    worker_count: usize,
}

/// A pre-configured factory for logging directly to a fmt [`String`].
pub type StringFmt = TypedFmtWrite<String>;

impl FmtWrite {
    /// Specializes the factory to log directly to a [`String`].
    ///
    /// This is the most direct path for string logging. Each log entry is
    /// appended directly into an string.
    pub fn string(self) -> StringFmt {
        StringFmt {
            writer: String::with_capacity(1024),
            max_retries: self.max_retries,
            worker_count: self.worker_count,
        }
    }

    /// Specializes the factory to log directly to a [`String`].
    /// Allows to set a custom capacity.
    ///
    /// This is the most direct path for string logging. Each log entry is
    /// appended directly into an string.
    pub fn string_with_capacity(self, capacity: usize) -> StringFmt {
        StringFmt {
            writer: String::with_capacity(capacity),
            max_retries: self.max_retries,
            worker_count: self.worker_count,
        }
    }

    /// Specializes the factory to log directly to a [`String`].
    /// Allows to set an already existing string as the destiny.
    ///
    /// This is the most direct path for string logging. Each log entry is
    /// appended directly into an string.
    pub fn string_sullied(self, writer: String) -> StringFmt {
        StringFmt {
            writer,
            max_retries: self.max_retries,
            worker_count: self.worker_count,
        }
    }

    /// Specializes the factory for any generic type implementing [`std::fmt::Write`].
    pub fn writer<W>(self, writer: W) -> TypedFmtWrite<W>
    where
        W: std::fmt::Write + Send + Sync + 'static,
    {
        TypedFmtWrite {
            writer,
            max_retries: self.max_retries,
            worker_count: self.worker_count,
        }
    }

    /// Specializes the factory for any generic type implementing `Box<dyn std::fmt::Write + Send + Sync>`.
    pub fn boxed(self, writer: Box<dyn std::fmt::Write + Send + Sync>) -> BoxedFmtWrite {
        BoxedFmtWrite {
            writer,
            max_retries: self.max_retries,
            worker_count: self.worker_count,
        }
    }

    /// Configures the maximum number of retries for the resulting service.
    pub fn max_retries(self, max_retries: usize) -> Self {
        Self {
            max_retries,
            ..self
        }
    }

    /// Configures the background worker thread count for asynchronous logging.
    pub fn worker_count(self, worker_count: usize) -> Self {
        Self {
            worker_count,
            ..self
        }
    }

    /// Finalizes the logger using a specific writer and a [`Concurrency`] strategy.
    /// This uses the default [`StandardWriteMessageFormatter`].
    pub fn build<W>(self, concurrency: Concurrency, writer: W) -> Logger
    where
        W: std::fmt::Write + Send + Sync + 'static,
    {
        match concurrency {
            Concurrency::Sync => Logger::new(self.build_impl_direct(writer)),
            Concurrency::Async => Logger::new(self.build_impl_queued(writer)),
        }
    }

    /// Builds a [`DirectLogger`] implementation wrapped in a [`Box`].
    pub fn build_impl_direct<W>(self, writer: W) -> Box<DirectLogger>
    where
        W: std::fmt::Write + Send + Sync + 'static,
    {
        let max_retries = self.max_retries;
        DirectLogger::new(self.build_service(writer), max_retries)
    }

    /// Builds a [`QueuedLogger`] implementation wrapped in a [`Box`].
    pub fn build_impl_queued<W>(self, writer: W) -> Box<QueuedLogger>
    where
        W: std::fmt::Write + Send + Sync + 'static,
    {
        let max_retries = self.max_retries;
        let worker_count = self.worker_count;
        QueuedLogger::new(self.build_service(writer), max_retries, worker_count)
    }

    /// Internal helper to construct the [`service::FmtWrite`] service with the standard formatter.
    pub fn build_service<W>(
        self,
        writer: W,
    ) -> Box<service::FmtWrite<W, StandardWriteMessageFormatter>>
    where
        W: std::fmt::Write + Send + Sync + 'static,
    {
        service::FmtWrite::new(writer)
    }

    /// Finalizes the logger using a custom formatter and a [`Concurrency`] strategy.
    pub fn build_with_formatter<W, MF>(
        self,
        concurrency: Concurrency,
        writer: W,
        formatter: MF,
    ) -> Logger
    where
        MF: WriteMessageFormatter + 'static,
        W: std::fmt::Write + Send + Sync + 'static,
    {
        match concurrency {
            Concurrency::Sync => {
                Logger::new(self.build_impl_direct_with_formatter(writer, formatter))
            }
            Concurrency::Async => {
                Logger::new(self.build_impl_queued_with_formatter(writer, formatter))
            }
        }
    }

    /// Builds a [`DirectLogger`] with a custom formatter.
    pub fn build_impl_direct_with_formatter<W, MF>(
        self,
        writer: W,
        formatter: MF,
    ) -> Box<DirectLogger>
    where
        MF: WriteMessageFormatter + 'static,
        W: std::fmt::Write + Send + Sync + 'static,
    {
        let max_retries = self.max_retries;
        DirectLogger::new(
            self.build_service_with_formatter(writer, formatter),
            max_retries,
        )
    }

    /// Builds a [`QueuedLogger`] with a custom formatter.
    pub fn build_impl_queued_with_formatter<W, MF>(
        self,
        writer: W,
        formatter: MF,
    ) -> Box<QueuedLogger>
    where
        MF: WriteMessageFormatter + 'static,
        W: std::fmt::Write + Send + Sync + 'static,
    {
        let max_retries = self.max_retries;
        let worker_count = self.worker_count;
        QueuedLogger::new(
            self.build_service_with_formatter(writer, formatter),
            max_retries,
            worker_count,
        )
    }

    /// Internal helper to construct the [`service::FmtWrite`] service with a custom formatter.
    pub fn build_service_with_formatter<W, MF>(
        self,
        writer: W,
        formatter: MF,
    ) -> Box<service::FmtWrite<W, MF>>
    where
        MF: WriteMessageFormatter + 'static,
        W: std::fmt::Write + Send + Sync + 'static,
    {
        service::FmtWrite::with_formatter(writer, formatter)
    }
}

impl Default for FmtWrite {
    /// Provides sensible defaults for byte-oriented logging.
    ///
    /// - **max_retries**: `3` (Standard resilience against transient I/O issues).
    /// - **worker_count**: `1` (Ensures sequential log ordering in asynchronous mode).
    fn default() -> Self {
        Self {
            max_retries: 3,
            worker_count: 1,
        }
    }
}

impl<W> TypedFmtWrite<W>
where
    W: std::fmt::Write + Send + Sync + 'static,
{
    /// Creates a new [`TypedFmtWrite`] with a specific writer and default policies.
    ///
    /// Defaults to 3 retries and 1 worker thread.
    pub fn new(writer: W) -> Self {
        Self {
            writer,
            max_retries: 3,
            worker_count: 1,
        }
    }

    /// Returns a reference to the underlying writer.
    pub fn get_writer(&self) -> &W {
        &self.writer
    }

    /// Returns the currently configured maximum retry attempts.
    pub fn get_max_retries(&self) -> usize {
        self.max_retries
    }

    /// Returns the currently configured background worker count.
    pub fn get_worker_count(&self) -> usize {
        self.worker_count
    }

    /// Replaces the current writer while keeping existing retry and worker configurations.
    pub fn writer(self, writer: W) -> Self {
        Self { writer, ..self }
    }

    /// Updates the maximum number of retry attempts for this specific writer.
    pub fn max_retries(self, max_retries: usize) -> Self {
        Self {
            max_retries,
            ..self
        }
    }

    /// Updates the background worker count for this specific writer.
    pub fn worker_count(self, worker_count: usize) -> Self {
        Self {
            worker_count,
            ..self
        }
    }

    /// Finalizes the builder and returns a high-level [`Logger`].
    ///
    /// This uses the default [`StandardWriteMessageFormatter`].
    pub fn build(self, concurrency: Concurrency) -> Logger {
        match concurrency {
            Concurrency::Sync => Logger::new(self.build_impl_direct()),
            Concurrency::Async => Logger::new(self.build_impl_queued()),
        }
    }

    /// Builds the underlying [`DirectLogger`] implementation for this writer.
    ///
    /// Useful if you need to bypass the [`Logger`] wrapper and manage the
    /// synchronous driver manually.
    pub fn build_impl_direct(self) -> Box<DirectLogger> {
        let max_retries = self.max_retries;
        DirectLogger::new(self.build_service(), max_retries)
    }

    /// Builds the underlying [`QueuedLogger`] implementation for this writer.
    ///
    /// Useful if you need to bypass the [`Logger`] wrapper and manage the
    /// asynchronous worker pool manually.
    pub fn build_impl_queued(self) -> Box<QueuedLogger> {
        let max_retries = self.max_retries;
        let worker_count = self.worker_count;
        QueuedLogger::new(self.build_service(), max_retries, worker_count)
    }

    /// Internal helper to construct the [`service::FmtWrite`] service for this specific writer
    /// using the standard formatter.
    pub fn build_service(self) -> Box<service::FmtWrite<W, StandardWriteMessageFormatter>> {
        service::FmtWrite::new(self.writer)
    }

    /// Finalizes the builder using a custom [`WriteMessageFormatter`].
    ///
    /// This allows you to define exactly how messages are serialized (e.g., JSON,
    /// custom text headers) before being sent to the writer.
    pub fn build_with_formatter<MF>(self, concurrency: Concurrency, formatter: MF) -> Logger
    where
        MF: WriteMessageFormatter + 'static,
    {
        match concurrency {
            Concurrency::Sync => Logger::new(self.build_impl_direct_with_formatter(formatter)),
            Concurrency::Async => Logger::new(self.build_impl_queued_with_formatter(formatter)),
        }
    }

    /// Builds a [`DirectLogger`] with a specific formatter for this writer.
    pub fn build_impl_direct_with_formatter<MF>(self, formatter: MF) -> Box<DirectLogger>
    where
        MF: WriteMessageFormatter + 'static,
    {
        let max_retries = self.max_retries;
        DirectLogger::new(self.build_service_with_formatter(formatter), max_retries)
    }

    /// Builds a [`QueuedLogger`] with a specific formatter for this writer.
    pub fn build_impl_queued_with_formatter<MF>(self, formatter: MF) -> Box<QueuedLogger>
    where
        MF: WriteMessageFormatter + 'static,
    {
        let max_retries = self.max_retries;
        let worker_count = self.worker_count;
        QueuedLogger::new(
            self.build_service_with_formatter(formatter),
            max_retries,
            worker_count,
        )
    }

    /// Internal helper to construct the [`service::FmtWrite`] service for this specific writer
    /// using a custom formatter.
    pub fn build_service_with_formatter<MF>(self, formatter: MF) -> Box<service::FmtWrite<W, MF>>
    where
        MF: WriteMessageFormatter + 'static,
    {
        service::FmtWrite::with_formatter(self.writer, formatter)
    }
}

impl BoxedFmtWrite {
    /// Creates a new [`TypedFmtWrite`] with a specific writer and default policies.
    ///
    /// Defaults to 3 retries and 1 worker thread.
    pub fn new(writer: Box<dyn std::fmt::Write + Send + Sync>) -> Self {
        Self {
            writer,
            max_retries: 3,
            worker_count: 1,
        }
    }

    /// Returns a reference to the underlying writer.
    pub fn get_writer(&self) -> &(dyn std::fmt::Write + Send + Sync) {
        self.writer.as_ref()
    }

    /// Returns the currently configured maximum retry attempts.
    pub fn get_max_retries(&self) -> usize {
        self.max_retries
    }

    /// Returns the currently configured background worker count.
    pub fn get_worker_count(&self) -> usize {
        self.worker_count
    }

    /// Replaces the current writer while keeping existing retry and worker configurations.
    pub fn writer(self, writer: Box<dyn std::fmt::Write + Send + Sync>) -> Self {
        Self { writer, ..self }
    }

    /// Updates the maximum number of retry attempts for this specific writer.
    pub fn max_retries(self, max_retries: usize) -> Self {
        Self {
            max_retries,
            ..self
        }
    }

    /// Updates the background worker count for this specific writer.
    pub fn worker_count(self, worker_count: usize) -> Self {
        Self {
            worker_count,
            ..self
        }
    }

    /// Finalizes the builder and returns a high-level [`Logger`].
    ///
    /// This uses the default [`StandardWriteMessageFormatter`].
    pub fn build(self, concurrency: Concurrency) -> Logger {
        match concurrency {
            Concurrency::Sync => Logger::new(self.build_impl_direct()),
            Concurrency::Async => Logger::new(self.build_impl_queued()),
        }
    }

    /// Builds the underlying [`DirectLogger`] implementation for this writer.
    ///
    /// Useful if you need to bypass the [`Logger`] wrapper and manage the
    /// synchronous driver manually.
    pub fn build_impl_direct(self) -> Box<DirectLogger> {
        let max_retries = self.max_retries;
        DirectLogger::new(self.build_service(), max_retries)
    }

    /// Builds the underlying [`QueuedLogger`] implementation for this writer.
    ///
    /// Useful if you need to bypass the [`Logger`] wrapper and manage the
    /// asynchronous worker pool manually.
    pub fn build_impl_queued(self) -> Box<QueuedLogger> {
        let max_retries = self.max_retries;
        let worker_count = self.worker_count;
        QueuedLogger::new(self.build_service(), max_retries, worker_count)
    }

    /// Internal helper to construct the [`service::BoxedFmtWrite`] service for this specific writer
    /// using the standard formatter.
    pub fn build_service(self) -> Box<service::BoxedFmtWrite<StandardWriteMessageFormatter>> {
        service::BoxedFmtWrite::new(self.writer)
    }

    /// Finalizes the builder using a custom [`WriteMessageFormatter`].
    ///
    /// This allows you to define exactly how messages are serialized (e.g., JSON,
    /// custom text headers) before being sent to the writer.
    pub fn build_with_formatter<MF>(self, concurrency: Concurrency, formatter: MF) -> Logger
    where
        MF: WriteMessageFormatter + 'static,
    {
        match concurrency {
            Concurrency::Sync => Logger::new(self.build_impl_direct_with_formatter(formatter)),
            Concurrency::Async => Logger::new(self.build_impl_queued_with_formatter(formatter)),
        }
    }

    /// Builds a [`DirectLogger`] with a specific formatter for this writer.
    pub fn build_impl_direct_with_formatter<MF>(self, formatter: MF) -> Box<DirectLogger>
    where
        MF: WriteMessageFormatter + 'static,
    {
        let max_retries = self.max_retries;
        DirectLogger::new(self.build_service_with_formatter(formatter), max_retries)
    }

    /// Builds a [`QueuedLogger`] with a specific formatter for this writer.
    pub fn build_impl_queued_with_formatter<MF>(self, formatter: MF) -> Box<QueuedLogger>
    where
        MF: WriteMessageFormatter + 'static,
    {
        let max_retries = self.max_retries;
        let worker_count = self.worker_count;
        QueuedLogger::new(
            self.build_service_with_formatter(formatter),
            max_retries,
            worker_count,
        )
    }

    /// Internal helper to construct the [`service::BoxedFmtWrite`] service for this specific writer
    /// using a custom formatter.
    pub fn build_service_with_formatter<MF>(self, formatter: MF) -> Box<service::BoxedFmtWrite<MF>>
    where
        MF: WriteMessageFormatter + 'static,
    {
        service::BoxedFmtWrite::with_formatter(self.writer, formatter)
    }
}