tracing_glog/
format.rs

1#[cfg(feature = "ansi")]
2use nu_ansi_term::{Color, Style};
3use std::fmt;
4use tracing::{Level, Metadata};
5use tracing_subscriber::fmt::{format::Writer, time::FormatTime};
6
7use tracing_subscriber::fmt::time::{ChronoLocal, ChronoUtc};
8
9pub struct FormatLevelChars {
10    pub trace: &'static str,
11    pub debug: &'static str,
12    pub info: &'static str,
13    pub warn: &'static str,
14    pub error: &'static str,
15}
16
17impl FormatLevelChars {
18    pub const fn const_default() -> FormatLevelChars {
19        FormatLevelChars {
20            trace: "T",
21            debug: "D",
22            info: "I",
23            warn: "W",
24            error: "E",
25        }
26    }
27}
28
29impl Default for FormatLevelChars {
30    fn default() -> FormatLevelChars {
31        FormatLevelChars::const_default()
32    }
33}
34
35pub(crate) const DEFAULT_FORMAT_LEVEL_CHARS: FormatLevelChars = FormatLevelChars::const_default();
36
37pub(crate) struct FmtLevel {
38    pub level: Level,
39    pub chars: &'static FormatLevelChars,
40    #[cfg(feature = "ansi")]
41    pub ansi: bool,
42}
43
44impl FmtLevel {
45    pub(crate) fn format_level(
46        level: Level,
47        chars: &'static FormatLevelChars,
48        ansi: bool,
49    ) -> FmtLevel {
50        #[cfg(not(feature = "ansi"))]
51        let _ = ansi;
52        FmtLevel {
53            level,
54            chars,
55            #[cfg(feature = "ansi")]
56            ansi,
57        }
58    }
59}
60
61impl fmt::Display for FmtLevel {
62    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63        let chars = self.chars;
64        #[cfg(feature = "ansi")]
65        if self.ansi {
66            return match self.level {
67                Level::TRACE => write!(f, "{}", Color::Purple.paint(chars.trace)),
68                Level::DEBUG => write!(f, "{}", Color::Blue.paint(chars.debug)),
69                Level::INFO => write!(f, "{}", Color::Green.paint(chars.info)),
70                Level::WARN => write!(f, "{}", Color::Yellow.paint(chars.warn)),
71                Level::ERROR => write!(f, "{}", Color::Red.paint(chars.error)),
72            };
73        }
74        match self.level {
75            Level::TRACE => f.pad(chars.trace),
76            Level::DEBUG => f.pad(chars.debug),
77            Level::INFO => f.pad(chars.info),
78            Level::WARN => f.pad(chars.warn),
79            Level::ERROR => f.pad(chars.error),
80        }
81    }
82}
83
84/// Formats the current [UTC time] using [`chrono` crate].
85///
86/// To format the current local time instead, use the [`LocalTime`]
87/// or the [`LocalTime`] type.
88///
89/// [UTC time]: ChronoUtc
90/// [`chrono` crate]: chrono
91#[derive(Clone, Debug)]
92pub struct UtcTime {
93    time: ChronoUtc,
94}
95
96impl FormatTime for UtcTime {
97    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
98        #[cfg(feature = "ansi")]
99        if w.has_ansi_escapes() {
100            let style = Style::new().dimmed();
101            write!(w, "{}", style.prefix())?;
102            self.time.format_time(w)?;
103            write!(w, "{}", style.suffix())?;
104            return Ok(());
105        }
106
107        self.time.format_time(w)
108    }
109}
110
111impl Default for UtcTime {
112    fn default() -> Self {
113        let fmt_string = String::from("%m%d %H:%M:%S%.6f");
114        Self {
115            time: ChronoUtc::new(fmt_string),
116        }
117    }
118}
119
120/// Formats the current [`local time`] using [`chrono` crate].
121///
122/// To format the UTC time instead, use the [`UtcTime`]
123/// or the [`crate::time_crate::UtcTime`] type.
124///
125/// [`local time`]: tracing_subscriber::fmt::time::ChronoLocal
126/// [`chrono` crate]: chrono
127pub struct LocalTime {
128    time: ChronoLocal,
129}
130
131impl FormatTime for LocalTime {
132    fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result {
133        #[cfg(feature = "ansi")]
134        if w.has_ansi_escapes() {
135            let style = Style::new().dimmed();
136            write!(w, "{}", style.prefix())?;
137            self.time.format_time(w)?;
138            write!(w, "{}", style.suffix())?;
139            return Ok(());
140        }
141
142        self.time.format_time(w)
143    }
144}
145
146impl Default for LocalTime {
147    fn default() -> Self {
148        let fmt_string = String::from("%m%d %H:%M:%S%.6f");
149        Self {
150            time: ChronoLocal::new(fmt_string),
151        }
152    }
153}
154
155pub(crate) struct FormatProcessData<'a> {
156    pub(crate) pid: u32,
157    pub(crate) thread_name: Option<&'a str>,
158    pub(crate) with_thread_names: bool,
159    pub(crate) metadata: &'a Metadata<'a>,
160    pub(crate) with_target: bool,
161    #[cfg(feature = "ansi")]
162    pub(crate) ansi: bool,
163}
164
165impl<'a> fmt::Display for FormatProcessData<'a> {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        let thread_name = self.thread_name;
168        let target = self.metadata.target();
169        let file = self.metadata.file().unwrap_or("");
170        let line = match self.metadata.line() {
171            Some(line) => format!("{line}"),
172            None => String::new(),
173        };
174        // write the always unstyled PID
175        write!(f, " {pid:>5}", pid = self.pid)?;
176
177        #[cfg(feature = "ansi")]
178        if self.ansi {
179            let style = Style::new().bold();
180            // start by bolding all the expected data
181            write!(f, "{}", style.prefix())?;
182            if let Some(name) = thread_name {
183                if self.with_thread_names {
184                    write!(f, " {name}")?
185                }
186            }
187
188            if self.with_target {
189                write!(f, " [{target}]")?;
190            }
191
192            write!(f, " {file}:{line}")?;
193
194            // end bolding
195            write!(f, "{}", style.suffix())?;
196
197            return Ok(());
198        }
199        if let Some(name) = thread_name {
200            if self.with_thread_names {
201                write!(f, " {name}")?
202            }
203        }
204
205        if self.with_target {
206            write!(f, " [{target}]")?;
207        }
208
209        write!(f, " {file}:{line}")?;
210        Ok(())
211    }
212}
213
214/// Docs!
215pub(crate) struct FormatSpanFields<'a> {
216    span_name: &'static str,
217    fields: Option<&'a str>,
218    #[cfg(feature = "ansi")]
219    pub ansi: bool,
220    print_span_names: bool,
221}
222
223impl<'a> FormatSpanFields<'a> {
224    pub(crate) fn format_fields(
225        span_name: &'static str,
226        fields: Option<&'a str>,
227        ansi: bool,
228        print_span_names: bool,
229    ) -> Self {
230        #[cfg(not(feature = "ansi"))]
231        let _ = ansi;
232        Self {
233            span_name,
234            fields,
235            #[cfg(feature = "ansi")]
236            ansi,
237            print_span_names,
238        }
239    }
240}
241
242impl<'a> fmt::Display for FormatSpanFields<'a> {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        #[cfg(feature = "ansi")]
245        if self.ansi {
246            let bold = Style::new().bold();
247
248            if self.print_span_names {
249                write!(f, "{}", bold.paint(self.span_name))?;
250            }
251
252            let italic = Style::new().italic();
253            if let Some(fields) = self.fields {
254                if self.print_span_names {
255                    write!(f, "{{{}}}", italic.paint(fields))?;
256                } else {
257                    write!(f, "{}", italic.paint(fields))?;
258                }
259            };
260            return Ok(());
261        }
262
263        if self.print_span_names {
264            write!(f, "{}", self.span_name)?;
265        }
266        if let Some(fields) = self.fields {
267            if self.print_span_names {
268                write!(f, "{{{fields}}}")?;
269            } else {
270                write!(f, "{fields}")?;
271            }
272        };
273
274        Ok(())
275    }
276}