tracing_glog/
time_crate.rs

1#[cfg(feature = "ansi")]
2use crate::nu_ansi_term::Style;
3use std::{fmt, io};
4use time::{format_description::FormatItem, formatting::Formattable, OffsetDateTime};
5use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
6
7/// A bridge between `fmt::Write` and `io::Write`.
8///
9/// This is used by the timestamp formatting implementation for the `time`
10/// crate and by the JSON formatter. In both cases, this is needed because
11/// `tracing-subscriber`'s `FormatEvent`/`FormatTime` traits expect a
12/// `fmt::Write` implementation, while `serde_json::Serializer` and `time`'s
13/// `format_into` methods expect an `io::Write`.
14pub(crate) struct WriteAdaptor<'a> {
15    fmt_write: &'a mut dyn fmt::Write,
16}
17
18#[cfg(feature = "time")]
19fn format_datetime(
20    into: &mut Writer<'_>,
21    now: OffsetDateTime,
22    fmt: &impl Formattable,
23) -> fmt::Result {
24    let mut into = WriteAdaptor::new(into);
25    now.format_into(&mut into, fmt)
26        .map_err(|_| fmt::Error)
27        .map(|_| ())
28}
29
30impl<'a> WriteAdaptor<'a> {
31    pub(crate) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self {
32        Self { fmt_write }
33    }
34}
35
36impl<'a> std::io::Write for WriteAdaptor<'a> {
37    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
38        let s = std::str::from_utf8(buf)
39            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
40
41        self.fmt_write
42            .write_str(s)
43            .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
44
45        Ok(s.as_bytes().len())
46    }
47
48    fn flush(&mut self) -> io::Result<()> {
49        Ok(())
50    }
51}
52
53impl<'a> fmt::Debug for WriteAdaptor<'a> {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        f.pad("WriteAdaptor { .. }")
56    }
57}
58
59/// Formats the current [UTC time] using a [formatter] from the [`time` crate].
60///
61/// To format the current [local time] instead, use the [`LocalTime`] type.
62///
63/// [UTC time]: time::OffsetDateTime::now_utc
64/// [formatter]: time::formatting::Formattable
65/// [`time` crate]: time
66/// [local time]: time::OffsetDateTime::now_local
67#[derive(Clone, Debug)]
68pub struct UtcTime<F = Vec<FormatItem<'static>>> {
69    format: F,
70}
71
72impl<F> FormatTime for UtcTime<F>
73where
74    F: Formattable,
75{
76    fn format_time(&self, writer: &mut Writer<'_>) -> fmt::Result {
77        let now = OffsetDateTime::now_utc();
78
79        #[cfg(feature = "ansi")]
80        if writer.has_ansi_escapes() {
81            let style = Style::new().dimmed();
82            write!(writer, "{}", style.prefix())?;
83            format_datetime(writer, now, &self.format)?;
84            write!(writer, "{}", style.suffix())?;
85            return Ok(());
86        }
87
88        format_datetime(writer, now, &self.format)
89    }
90}
91
92impl Default for UtcTime {
93    fn default() -> Self {
94        let format: Vec<FormatItem> = time::format_description::parse(
95            "[month][day] [hour]:[minute]:[second].[subsecond digits:6]",
96        )
97        .expect("Unable to make time formatter");
98        Self { format }
99    }
100}
101
102/// Formats the current [local time] using a [formatter] from the [`time` crate].
103///
104/// To format the current [UTC time] instead, use the [`UtcTime`] type.
105///
106/// <div class="example-wrap" style="display:inline-block">
107/// <pre class="compile_fail" style="white-space:normal;font:inherit;">
108///     <strong>Warning</strong>: The <a href = "https://docs.rs/time/0.3/time/"><code>time</code>
109///     crate</a> must be compiled with <code>--cfg unsound_local_offset</code> in order to use
110///     local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
111///     events will be logged without timestamps.
112///
113///    See the <a href="https://docs.rs/time/0.3.4/time/#feature-flags"><code>time</code>
114///    documentation</a> for more details.
115/// </pre></div>
116///
117/// [local time]: time::OffsetDateTime::now_local
118/// [formatter]: time::formatting::Formattable
119/// [`time` crate]: time
120/// [UTC time]: time::OffsetDateTime::now_utc
121#[derive(Clone, Debug)]
122#[cfg(feature = "local-time")]
123pub struct LocalTime<F = Vec<FormatItem<'static>>> {
124    format: F,
125}
126
127#[cfg(feature = "local-time")]
128impl Default for LocalTime {
129    fn default() -> Self {
130        let format: Vec<FormatItem> = time::format_description::parse(
131            "[month][day] [hour]:[minute]:[second].[subsecond digits:6]",
132        )
133        .expect("Unable to make time formatter");
134        Self { format }
135    }
136}
137
138#[cfg(feature = "local-time")]
139impl<F> FormatTime for LocalTime<F>
140where
141    F: Formattable,
142{
143    fn format_time(&self, writer: &mut Writer<'_>) -> fmt::Result {
144        let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?;
145
146        #[cfg(feature = "ansi")]
147        if writer.has_ansi_escapes() {
148            let style = Style::new().dimmed();
149            write!(writer, "{}", style.prefix())?;
150            format_datetime(writer, now, &self.format)?;
151            // necessary to provide space between the time and the PID
152            write!(writer, "{}", style.suffix())?;
153            return Ok(());
154        }
155
156        format_datetime(writer, now, &self.format)
157    }
158}