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}