better_tracing/fmt/time/
mod.rs

1//! Formatters for event timestamps.
2use crate::fmt::format::Writer;
3use std::fmt;
4use std::time as stdtime;
5use std::time::Instant;
6
7mod datetime;
8
9#[cfg(feature = "time")]
10mod time_crate;
11
12#[cfg(feature = "time")]
13#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
14pub use time_crate::UtcTime;
15
16#[cfg(feature = "local-time")]
17#[cfg_attr(docsrs, doc(cfg(all(unsound_local_offset, feature = "local-time"))))]
18pub use time_crate::LocalTime;
19
20#[cfg(feature = "time")]
21#[cfg_attr(docsrs, doc(cfg(feature = "time")))]
22pub use time_crate::OffsetTime;
23
24/// [`chrono`]-based implementation for [`FormatTime`].
25#[cfg(feature = "chrono")]
26mod chrono_crate;
27
28#[cfg(feature = "chrono")]
29#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
30pub use chrono_crate::ChronoLocal;
31
32#[cfg(feature = "chrono")]
33#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
34pub use chrono_crate::ChronoUtc;
35
36/// A type that can measure and format the current time.
37///
38/// This trait is used by `Format` to include a timestamp with each `Event` when it is logged.
39///
40/// Notable default implementations of this trait are `SystemTime` and `()`. The former prints the
41/// current time as reported by `std::time::SystemTime`, and the latter does not print the current
42/// time at all. `FormatTime` is also automatically implemented for any function pointer with the
43/// appropriate signature.
44///
45/// The full list of provided implementations can be found in [`time`].
46///
47/// [`time`]: self
48pub trait FormatTime {
49    /// Measure and write out the current time.
50    ///
51    /// When `format_time` is called, implementors should get the current time using their desired
52    /// mechanism, and write it out to the given `fmt::Write`. Implementors must insert a trailing
53    /// space themselves if they wish to separate the time from subsequent log message text.
54    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result;
55}
56
57// --- Core time architecture: Clock + Formatter + Timer -----------------------
58
59/// Captures the notion of "now" and returns a snapshot value.
60/// Captures the notion of time ("now") and returns a snapshot value.
61pub trait Clock {
62    /// The concrete timestamp representation captured by this clock.
63    type Snapshot;
64    /// Get the current time snapshot.
65    fn now(&self) -> Self::Snapshot;
66}
67
68/// Formats a captured snapshot into the Writer without allocating.
69/// Formats a captured time snapshot into the writer with no allocations.
70pub trait TimestampFormatter<Input> {
71    /// Write a textual representation of `input` into `w`.
72    fn format(&self, input: &Input, w: &mut Writer<'_>) -> fmt::Result;
73}
74
75/// A combinator that implements `FormatTime` as `F(C::now())`.
76/// Composes a `Clock` and a `TimestampFormatter` to implement `FormatTime`.
77#[derive(Debug, Clone, Copy, Default)]
78pub struct Timer<C, F>(pub C, pub F);
79
80impl<C, F> FormatTime for Timer<C, F>
81where
82    C: Clock,
83    F: TimestampFormatter<C::Snapshot>,
84{
85    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
86        let snap = self.0.now();
87        self.1.format(&snap, w)
88    }
89}
90
91/// System wall-clock now.
92/// A `Clock` that returns `std::time::SystemTime::now()`.
93#[derive(Debug, Clone, Copy, Default)]
94pub struct SystemClock;
95
96impl Clock for SystemClock {
97    type Snapshot = stdtime::SystemTime;
98    fn now(&self) -> Self::Snapshot {
99        stdtime::SystemTime::now()
100    }
101}
102
103/// RFC3339 formatter with configurable fractional digits and optional 'Z'.
104#[derive(Debug, Clone, Copy, Default)]
105pub struct Rfc3339<const DIGITS: u8, const Z: bool>;
106
107impl<const D: u8, const Z: bool> TimestampFormatter<stdtime::SystemTime> for Rfc3339<D, Z> {
108    fn format(&self, input: &stdtime::SystemTime, w: &mut Writer<'_>) -> fmt::Result {
109        // Leverage the existing no-deps DateTime to render RFC3339 with truncation.
110        let dt = datetime::DateTime::from(*input);
111        let digits = if D > 9 { 9 } else { D } as u8;
112        dt.fmt_rfc3339_with_subsec_to(w, digits, Z)
113    }
114}
115
116/// Returns a new `SystemTime` timestamp provider.
117///
118/// This can then be configured further to determine how timestamps should be
119/// configured.
120///
121/// This is equivalent to calling
122/// ```rust
123/// # fn timer() -> better_tracing::fmt::time::SystemTime {
124/// better_tracing::fmt::time::SystemTime::default()
125/// # }
126/// ```
127pub fn time() -> SystemTime {
128    SystemTime
129}
130
131/// Returns a new `Uptime` timestamp provider.
132///
133/// With this timer, timestamps will be formatted with the amount of time
134/// elapsed since the timestamp provider was constructed.
135///
136/// This can then be configured further to determine how timestamps should be
137/// configured.
138///
139/// This is equivalent to calling
140/// ```rust
141/// # fn timer() -> better_tracing::fmt::time::Uptime {
142/// better_tracing::fmt::time::Uptime::default()
143/// # }
144/// ```
145pub fn uptime() -> Uptime {
146    Uptime::default()
147}
148
149impl<F> FormatTime for &F
150where
151    F: FormatTime,
152{
153    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
154        (*self).format_time(w)
155    }
156}
157
158impl FormatTime for () {
159    fn format_time(&self, _: &mut Writer<'_>) -> fmt::Result {
160        Ok(())
161    }
162}
163
164impl FormatTime for fn(&mut Writer<'_>) -> fmt::Result {
165    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
166        (*self)(w)
167    }
168}
169
170/// Retrieve and print the current wall-clock time.
171#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
172pub struct SystemTime;
173
174/// Retrieve and print the relative elapsed wall-clock time since an epoch.
175///
176/// The `Default` implementation for `Uptime` makes the epoch the current time.
177#[derive(Debug, Clone, Copy, Eq, PartialEq)]
178pub struct Uptime {
179    epoch: Instant,
180}
181
182impl Default for Uptime {
183    fn default() -> Self {
184        Uptime {
185            epoch: Instant::now(),
186        }
187    }
188}
189
190impl From<Instant> for Uptime {
191    fn from(epoch: Instant) -> Self {
192        Uptime { epoch }
193    }
194}
195
196impl FormatTime for SystemTime {
197    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
198        // Delegate to the unified path: SystemClock + RFC3339 micros with Z.
199        Timer(SystemClock, Rfc3339::<6, true>).format_time(w)
200    }
201}
202
203impl FormatTime for Uptime {
204    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
205        let e = self.epoch.elapsed();
206        write!(w, "{:4}.{:09}s", e.as_secs(), e.subsec_nanos())
207    }
208}
209
210// --- Built-in, no-deps formatters and ergonomic constructors ------------------
211
212/// Seconds since UNIX epoch (UTC), using floor semantics for pre-epoch values.
213#[derive(Debug, Clone, Copy, Default)]
214pub struct UnixSeconds;
215/// Milliseconds since UNIX epoch (UTC), using floor semantics for pre-epoch values.
216#[derive(Debug, Clone, Copy, Default)]
217pub struct UnixMillis;
218/// Microseconds since UNIX epoch (UTC), using floor semantics for pre-epoch values.
219#[derive(Debug, Clone, Copy, Default)]
220pub struct UnixMicros;
221/// Nanoseconds since UNIX epoch (UTC), using floor semantics for pre-epoch values.
222#[derive(Debug, Clone, Copy, Default)]
223pub struct UnixNanos;
224
225impl TimestampFormatter<stdtime::SystemTime> for UnixSeconds {
226    fn format(&self, input: &stdtime::SystemTime, w: &mut Writer<'_>) -> fmt::Result {
227        let v = unix_units(input, 1_000_000_000, 0);
228        write!(w, "{}", v)
229    }
230}
231impl TimestampFormatter<stdtime::SystemTime> for UnixMillis {
232    fn format(&self, input: &stdtime::SystemTime, w: &mut Writer<'_>) -> fmt::Result {
233        let v = unix_units(input, 1_000_000_000, 1_000_000);
234        write!(w, "{}", v)
235    }
236}
237impl TimestampFormatter<stdtime::SystemTime> for UnixMicros {
238    fn format(&self, input: &stdtime::SystemTime, w: &mut Writer<'_>) -> fmt::Result {
239        let v = unix_units(input, 1_000_000_000, 1_000);
240        write!(w, "{}", v)
241    }
242}
243impl TimestampFormatter<stdtime::SystemTime> for UnixNanos {
244    fn format(&self, input: &stdtime::SystemTime, w: &mut Writer<'_>) -> fmt::Result {
245        let v = unix_units(input, 1_000_000_000, 1);
246        write!(w, "{}", v)
247    }
248}
249
250fn unix_units(ts: &stdtime::SystemTime, base_nanos: u32, unit_div: u32) -> i128 {
251    match ts.duration_since(stdtime::UNIX_EPOCH) {
252        Ok(d) => {
253            let secs = d.as_secs() as i128;
254            let nanos = d.subsec_nanos() as i128;
255            if unit_div == 0 {
256                secs
257            } else {
258                secs * (base_nanos as i128 / unit_div as i128) + nanos / unit_div as i128
259            }
260        }
261        Err(e) => {
262            let d = e.duration();
263            let secs = d.as_secs() as i128;
264            let nanos = d.subsec_nanos() as i128;
265            if unit_div == 0 {
266                // floor for negative values: if any fractional part, subtract one.
267                -secs - if nanos == 0 { 0 } else { 1 }
268            } else {
269                let unit = base_nanos as i128 / unit_div as i128;
270                let whole = secs * unit;
271                let frac = nanos / unit_div as i128;
272                if frac == 0 {
273                    -whole
274                } else {
275                    -(whole + frac)
276                }
277            }
278        }
279    }
280}
281
282impl SystemTime {
283    /// RFC3339 with no fractional seconds and 'Z'.
284    pub const fn rfc3339_seconds() -> Timer<SystemClock, Rfc3339<0, true>> {
285        Timer(SystemClock, Rfc3339)
286    }
287    /// RFC3339 with 3 fractional digits (milliseconds) and 'Z'.
288    pub const fn rfc3339_millis() -> Timer<SystemClock, Rfc3339<3, true>> {
289        Timer(SystemClock, Rfc3339)
290    }
291
292    /// RFC3339 with 9 fractional digits (nanoseconds) and 'Z'.
293    pub const fn rfc3339_nanos() -> Timer<SystemClock, Rfc3339<9, true>> {
294        Timer(SystemClock, Rfc3339)
295    }
296
297    /// Seconds since UNIX epoch (UTC).
298    pub const fn unix_seconds() -> Timer<SystemClock, UnixSeconds> {
299        Timer(SystemClock, UnixSeconds)
300    }
301
302    /// Milliseconds since UNIX epoch (UTC).
303    pub const fn unix_millis() -> Timer<SystemClock, UnixMillis> {
304        Timer(SystemClock, UnixMillis)
305    }
306
307    /// Microseconds since UNIX epoch (UTC).
308    pub const fn unix_micros() -> Timer<SystemClock, UnixMicros> {
309        Timer(SystemClock, UnixMicros)
310    }
311
312    /// Nanoseconds since UNIX epoch (UTC).
313    pub const fn unix_nanos() -> Timer<SystemClock, UnixNanos> {
314        Timer(SystemClock, UnixNanos)
315    }
316}
317
318/// Used internally for time-of-day formatting. May change in future without deprecation.
319#[doc(hidden)]
320#[derive(Debug, Clone, Copy, Default)]
321pub struct TimeOfDay<const DIGITS: u8>;
322
323impl<const D: u8> TimestampFormatter<stdtime::SystemTime> for TimeOfDay<D> {
324    fn format(&self, input: &stdtime::SystemTime, w: &mut Writer<'_>) -> fmt::Result {
325        let dt = datetime::DateTime::from(*input);
326        let digits = if D > 9 { 9 } else { D } as u8;
327        // Emit time-of-day without a timezone suffix.
328        dt.fmt_time_of_day_to(w, digits, false)
329    }
330}
331
332impl SystemTime {
333    /// Time-of-day with whole seconds, no suffix: HH:MM:SS
334    pub const fn time_only_secs() -> Timer<SystemClock, TimeOfDay<0>> {
335        Timer(SystemClock, TimeOfDay)
336    }
337
338    /// Time-of-day with milliseconds, no suffix: HH:MM:SS.mmm
339    pub const fn time_only_millis() -> Timer<SystemClock, TimeOfDay<3>> {
340        Timer(SystemClock, TimeOfDay)
341    }
342
343    /// Time-of-day with microseconds, no suffix: HH:MM:SS.uuuuuu
344    pub const fn time_only_micros() -> Timer<SystemClock, TimeOfDay<6>> {
345        Timer(SystemClock, TimeOfDay)
346    }
347}