timber_rust/service/write/
msgformatter.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com
3
4use crate::Message;
5use crate::service::ServiceError;
6use chrono::{DateTime, SecondsFormat, Utc};
7
8/// Trait defining the behavior for formatting log messages.
9///
10/// Implementations are responsible for defining the layout (timestamp, level, content)
11/// and writing the result to an I/O sink.
12pub trait MessageFormatter: Send + Sync + Default {
13    /// Formats and writes the message to the provided I/O sink.
14    ///
15    /// ### Implementation Requirements
16    /// - **Atomicity**: To ensure log integrity in concurrent environments, implementations
17    ///   should minimize the number of calls to the writer. Using a single `write!` macro
18    ///   or a buffered approach is highly recommended.
19    /// - **Thread Safety**: The `writer` is guaranteed to be `Send + Sync`. However,
20    ///   some global sinks (like `std::io::stdout()`) may not support explicit locking
21    ///   while maintaining these bounds.
22    ///
23    /// # Errors
24    /// Returns [`ServiceError`] if formatting fails or the writer encounters an I/O error.
25    fn format_io(
26        &mut self,
27        message: &Message,
28        write: &mut (dyn std::io::Write + Send + Sync),
29    ) -> Result<(), ServiceError>;
30
31    /// Formats a message specifically for fmt-based destinations.
32    fn format_fmt(
33        &mut self,
34        message: &Message,
35        write: &mut (dyn std::fmt::Write + Send + Sync),
36    ) -> Result<(), ServiceError>;
37}
38
39/// A high-performance, stateful formatter that produces RFC 3339 compliant logs.
40///
41/// ### Output Format
42/// The formatter produces a single line per message using the following pattern:
43/// `[Timestamp] [ [Level] ] [Message]`
44///
45/// * **Timestamp**: Generated in UTC using [RFC 3339](https://tools.ietf.org/html/rfc3339)
46///   format with nanosecond precision (e.g., `2026-03-14T14:48:02.609225083+00:00`).
47/// * **Level**: The log level is uppercase, padded with single spaces inside brackets
48///   (e.g., `[ DEBUG ]`, `[ INFO  ]`, `[ ERROR ]`).
49/// * **Message**: The raw message content followed by a newline (`\n`).
50///
51/// ### Example Output
52/// ```text
53/// 2026-03-14T15:30:00.123456789+00:00 [ INFO  ] Service started successfully
54/// 2026-03-14T15:30:05.000000000+00:00 [ DEBUG ] Connecting to Loki at localhost:3100
55/// ```
56#[derive(Default)]
57pub struct StandardMessageFormatter {}
58
59impl StandardMessageFormatter {
60    /// Creates a formatter with a default buffer capacity of 128 bytes.
61    pub fn new() -> Self {
62        StandardMessageFormatter {}
63    }
64}
65
66impl MessageFormatter for StandardMessageFormatter {
67    fn format_io(
68        &mut self,
69        message: &Message,
70        write: &mut (dyn std::io::Write + Send + Sync),
71    ) -> Result<(), ServiceError> {
72        let instant: DateTime<Utc> = message.instant().into();
73        write!(
74            write,
75            "{} [ {} ] {}\n",
76            instant.to_rfc3339_opts(SecondsFormat::Nanos, true),
77            message.level(),
78            message.content()
79        )?;
80        Ok(())
81    }
82
83    fn format_fmt(
84        &mut self,
85        message: &Message,
86        write: &mut (dyn std::fmt::Write + Send + Sync),
87    ) -> Result<(), ServiceError> {
88        let instant: DateTime<Utc> = message.instant().into();
89        write!(
90            write,
91            "{} [ {} ] {}\n",
92            instant.to_rfc3339_opts(SecondsFormat::Nanos, true),
93            message.level(),
94            message.content()
95        )?;
96        Ok(())
97    }
98}
99
100/// A high-performance, stateful formatter that produces undated logs.
101///
102/// ### Output Format
103/// The formatter produces a single line per message using the following pattern:
104/// `[ [Level] ] [Message]`
105///
106/// * **Level**: The log level is uppercase, padded with single spaces inside brackets
107///   (e.g., `[ DEBUG ]`, `[ INFO  ]`, `[ ERROR ]`).
108/// * **Message**: The raw message content followed by a newline (`\n`).
109///
110/// ### Example Output
111/// ```text
112/// [ INFO  ] Service started successfully
113/// [ DEBUG ] Connecting to Loki at localhost:3100
114/// ```
115#[derive(Default)]
116pub struct AtemporalMessageFormatter {}
117
118impl AtemporalMessageFormatter {
119    /// Creates a formatter with a default buffer capacity of 128 bytes.
120    pub fn new() -> Self {
121        AtemporalMessageFormatter {}
122    }
123}
124
125impl MessageFormatter for AtemporalMessageFormatter {
126    fn format_io(
127        &mut self,
128        message: &Message,
129        write: &mut (dyn std::io::Write + Send + Sync),
130    ) -> Result<(), ServiceError> {
131        write!(write, "[ {} ] {}\n", message.level(), message.content())?;
132        Ok(())
133    }
134
135    fn format_fmt(
136        &mut self,
137        message: &Message,
138        write: &mut (dyn std::fmt::Write + Send + Sync),
139    ) -> Result<(), ServiceError> {
140        write!(write, "[ {} ] {}\n", message.level(), message.content())?;
141        Ok(())
142    }
143}