]> git.proxmox.com Git - rustc.git/blame - vendor/env_logger-0.8.4/src/fmt/mod.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / vendor / env_logger-0.8.4 / src / fmt / mod.rs
CommitLineData
3c0e092e
XL
1//! Formatting for log records.
2//!
3//! This module contains a [`Formatter`] that can be used to format log records
4//! into without needing temporary allocations. Usually you won't need to worry
5//! about the contents of this module and can use the `Formatter` like an ordinary
6//! [`Write`].
7//!
8//! # Formatting log records
9//!
10//! The format used to print log records can be customised using the [`Builder::format`]
11//! method.
12//! Custom formats can apply different color and weight to printed values using
13//! [`Style`] builders.
14//!
15//! ```
16//! use std::io::Write;
17//!
18//! let mut builder = env_logger::Builder::new();
19//!
20//! builder.format(|buf, record| {
21//! writeln!(buf, "{}: {}",
22//! record.level(),
23//! record.args())
24//! });
25//! ```
26//!
27//! [`Formatter`]: struct.Formatter.html
28//! [`Style`]: struct.Style.html
29//! [`Builder::format`]: ../struct.Builder.html#method.format
30//! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
31
32use std::cell::RefCell;
33use std::fmt::Display;
34use std::io::prelude::*;
35use std::rc::Rc;
36use std::{fmt, io, mem};
37
38use log::Record;
39
40mod humantime;
41pub(crate) mod writer;
42
43pub use self::humantime::glob::*;
44pub use self::writer::glob::*;
45
46use self::writer::{Buffer, Writer};
47
48pub(crate) mod glob {
49 pub use super::{Target, TimestampPrecision, WriteStyle};
50}
51
52/// Formatting precision of timestamps.
53///
54/// Seconds give precision of full seconds, milliseconds give thousands of a
55/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
56/// digits) and nanoseconds are billionth of a second (9 decimal digits).
57#[derive(Copy, Clone, Debug)]
58pub enum TimestampPrecision {
59 /// Full second precision (0 decimal digits)
60 Seconds,
61 /// Millisecond precision (3 decimal digits)
62 Millis,
63 /// Microsecond precision (6 decimal digits)
64 Micros,
65 /// Nanosecond precision (9 decimal digits)
66 Nanos,
67}
68
69/// The default timestamp precision is seconds.
70impl Default for TimestampPrecision {
71 fn default() -> Self {
72 TimestampPrecision::Seconds
73 }
74}
75
76/// A formatter to write logs into.
77///
78/// `Formatter` implements the standard [`Write`] trait for writing log records.
79/// It also supports terminal colors, through the [`style`] method.
80///
81/// # Examples
82///
83/// Use the [`writeln`] macro to format a log record.
84/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
85///
86/// ```
87/// use std::io::Write;
88///
89/// let mut builder = env_logger::Builder::new();
90///
91/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
92/// ```
93///
94/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
95/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html
96/// [`style`]: #method.style
97pub struct Formatter {
98 buf: Rc<RefCell<Buffer>>,
99 write_style: WriteStyle,
100}
101
102impl Formatter {
103 pub(crate) fn new(writer: &Writer) -> Self {
104 Formatter {
105 buf: Rc::new(RefCell::new(writer.buffer())),
106 write_style: writer.write_style(),
107 }
108 }
109
110 pub(crate) fn write_style(&self) -> WriteStyle {
111 self.write_style
112 }
113
114 pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
115 writer.print(&self.buf.borrow())
116 }
117
118 pub(crate) fn clear(&mut self) {
119 self.buf.borrow_mut().clear()
120 }
121}
122
123impl Write for Formatter {
124 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
125 self.buf.borrow_mut().write(buf)
126 }
127
128 fn flush(&mut self) -> io::Result<()> {
129 self.buf.borrow_mut().flush()
130 }
131}
132
133impl fmt::Debug for Formatter {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 f.debug_struct("Formatter").finish()
136 }
137}
138
139pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
140
141pub(crate) struct Builder {
142 pub format_timestamp: Option<TimestampPrecision>,
143 pub format_module_path: bool,
144 pub format_level: bool,
145 pub format_indent: Option<usize>,
146 pub custom_format: Option<FormatFn>,
147 pub format_suffix: &'static str,
148 built: bool,
149}
150
151impl Default for Builder {
152 fn default() -> Self {
153 Builder {
154 format_timestamp: Some(Default::default()),
155 format_module_path: true,
156 format_level: true,
157 format_indent: Some(4),
158 custom_format: None,
159 format_suffix: "\n",
160 built: false,
161 }
162 }
163}
164
165impl Builder {
166 /// Convert the format into a callable function.
167 ///
168 /// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
169 /// If the `custom_format` is `None`, then a default format is returned.
170 /// Any `default_format` switches set to `false` won't be written by the format.
171 pub fn build(&mut self) -> FormatFn {
172 assert!(!self.built, "attempt to re-use consumed builder");
173
174 let built = mem::replace(
175 self,
176 Builder {
177 built: true,
178 ..Default::default()
179 },
180 );
181
182 if let Some(fmt) = built.custom_format {
183 fmt
184 } else {
185 Box::new(move |buf, record| {
186 let fmt = DefaultFormat {
187 timestamp: built.format_timestamp,
188 module_path: built.format_module_path,
189 level: built.format_level,
190 written_header_value: false,
191 indent: built.format_indent,
192 suffix: built.format_suffix,
193 buf,
194 };
195
196 fmt.write(record)
197 })
198 }
199 }
200}
201
202#[cfg(feature = "termcolor")]
203type SubtleStyle = StyledValue<'static, &'static str>;
204#[cfg(not(feature = "termcolor"))]
205type SubtleStyle = &'static str;
206
207/// The default format.
208///
209/// This format needs to work with any combination of crate features.
210struct DefaultFormat<'a> {
211 timestamp: Option<TimestampPrecision>,
212 module_path: bool,
213 level: bool,
214 written_header_value: bool,
215 indent: Option<usize>,
216 buf: &'a mut Formatter,
217 suffix: &'a str,
218}
219
220impl<'a> DefaultFormat<'a> {
221 fn write(mut self, record: &Record) -> io::Result<()> {
222 self.write_timestamp()?;
223 self.write_level(record)?;
224 self.write_module_path(record)?;
225 self.finish_header()?;
226
227 self.write_args(record)
228 }
229
230 fn subtle_style(&self, text: &'static str) -> SubtleStyle {
231 #[cfg(feature = "termcolor")]
232 {
233 self.buf
234 .style()
235 .set_color(Color::Black)
236 .set_intense(true)
237 .clone()
238 .into_value(text)
239 }
240 #[cfg(not(feature = "termcolor"))]
241 {
242 text
243 }
244 }
245
246 fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
247 where
248 T: Display,
249 {
250 if !self.written_header_value {
251 self.written_header_value = true;
252
253 let open_brace = self.subtle_style("[");
254 write!(self.buf, "{}{}", open_brace, value)
255 } else {
256 write!(self.buf, " {}", value)
257 }
258 }
259
260 fn write_level(&mut self, record: &Record) -> io::Result<()> {
261 if !self.level {
262 return Ok(());
263 }
264
265 let level = {
266 #[cfg(feature = "termcolor")]
267 {
268 self.buf.default_styled_level(record.level())
269 }
270 #[cfg(not(feature = "termcolor"))]
271 {
272 record.level()
273 }
274 };
275
276 self.write_header_value(format_args!("{:<5}", level))
277 }
278
279 fn write_timestamp(&mut self) -> io::Result<()> {
280 #[cfg(feature = "humantime")]
281 {
282 use self::TimestampPrecision::*;
283 let ts = match self.timestamp {
284 None => return Ok(()),
285 Some(Seconds) => self.buf.timestamp_seconds(),
286 Some(Millis) => self.buf.timestamp_millis(),
287 Some(Micros) => self.buf.timestamp_micros(),
288 Some(Nanos) => self.buf.timestamp_nanos(),
289 };
290
291 self.write_header_value(ts)
292 }
293 #[cfg(not(feature = "humantime"))]
294 {
295 // Trick the compiler to think we have used self.timestamp
296 // Workaround for "field is never used: `timestamp`" compiler nag.
297 let _ = self.timestamp;
298 Ok(())
299 }
300 }
301
302 fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
303 if !self.module_path {
304 return Ok(());
305 }
306
307 if let Some(module_path) = record.module_path() {
308 self.write_header_value(module_path)
309 } else {
310 Ok(())
311 }
312 }
313
314 fn finish_header(&mut self) -> io::Result<()> {
315 if self.written_header_value {
316 let close_brace = self.subtle_style("]");
317 write!(self.buf, "{} ", close_brace)
318 } else {
319 Ok(())
320 }
321 }
322
323 fn write_args(&mut self, record: &Record) -> io::Result<()> {
324 match self.indent {
325 // Fast path for no indentation
326 None => write!(self.buf, "{}{}", record.args(), self.suffix),
327
328 Some(indent_count) => {
329 // Create a wrapper around the buffer only if we have to actually indent the message
330
331 struct IndentWrapper<'a, 'b: 'a> {
332 fmt: &'a mut DefaultFormat<'b>,
333 indent_count: usize,
334 }
335
336 impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
337 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
338 let mut first = true;
339 for chunk in buf.split(|&x| x == b'\n') {
340 if !first {
341 write!(
342 self.fmt.buf,
343 "{}{:width$}",
344 self.fmt.suffix,
345 "",
346 width = self.indent_count
347 )?;
348 }
349 self.fmt.buf.write_all(chunk)?;
350 first = false;
351 }
352
353 Ok(buf.len())
354 }
355
356 fn flush(&mut self) -> io::Result<()> {
357 self.fmt.buf.flush()
358 }
359 }
360
361 // The explicit scope here is just to make older versions of Rust happy
362 {
363 let mut wrapper = IndentWrapper {
364 fmt: self,
365 indent_count,
366 };
367 write!(wrapper, "{}", record.args())?;
368 }
369
370 write!(self.buf, "{}", self.suffix)?;
371
372 Ok(())
373 }
374 }
375 }
376}
377
378#[cfg(test)]
379mod tests {
380 use super::*;
381
382 use log::{Level, Record};
383
384 fn write(fmt: DefaultFormat) -> String {
385 let buf = fmt.buf.buf.clone();
386
387 let record = Record::builder()
388 .args(format_args!("log\nmessage"))
389 .level(Level::Info)
390 .file(Some("test.rs"))
391 .line(Some(144))
392 .module_path(Some("test::path"))
393 .build();
394
395 fmt.write(&record).expect("failed to write record");
396
397 let buf = buf.borrow();
398 String::from_utf8(buf.bytes().to_vec()).expect("failed to read record")
399 }
400
401 #[test]
402 fn format_with_header() {
403 let writer = writer::Builder::new()
404 .write_style(WriteStyle::Never)
405 .build();
406
407 let mut f = Formatter::new(&writer);
408
409 let written = write(DefaultFormat {
410 timestamp: None,
411 module_path: true,
412 level: true,
413 written_header_value: false,
414 indent: None,
415 suffix: "\n",
416 buf: &mut f,
417 });
418
419 assert_eq!("[INFO test::path] log\nmessage\n", written);
420 }
421
422 #[test]
423 fn format_no_header() {
424 let writer = writer::Builder::new()
425 .write_style(WriteStyle::Never)
426 .build();
427
428 let mut f = Formatter::new(&writer);
429
430 let written = write(DefaultFormat {
431 timestamp: None,
432 module_path: false,
433 level: false,
434 written_header_value: false,
435 indent: None,
436 suffix: "\n",
437 buf: &mut f,
438 });
439
440 assert_eq!("log\nmessage\n", written);
441 }
442
443 #[test]
444 fn format_indent_spaces() {
445 let writer = writer::Builder::new()
446 .write_style(WriteStyle::Never)
447 .build();
448
449 let mut f = Formatter::new(&writer);
450
451 let written = write(DefaultFormat {
452 timestamp: None,
453 module_path: true,
454 level: true,
455 written_header_value: false,
456 indent: Some(4),
457 suffix: "\n",
458 buf: &mut f,
459 });
460
461 assert_eq!("[INFO test::path] log\n message\n", written);
462 }
463
464 #[test]
465 fn format_indent_zero_spaces() {
466 let writer = writer::Builder::new()
467 .write_style(WriteStyle::Never)
468 .build();
469
470 let mut f = Formatter::new(&writer);
471
472 let written = write(DefaultFormat {
473 timestamp: None,
474 module_path: true,
475 level: true,
476 written_header_value: false,
477 indent: Some(0),
478 suffix: "\n",
479 buf: &mut f,
480 });
481
482 assert_eq!("[INFO test::path] log\nmessage\n", written);
483 }
484
485 #[test]
486 fn format_indent_spaces_no_header() {
487 let writer = writer::Builder::new()
488 .write_style(WriteStyle::Never)
489 .build();
490
491 let mut f = Formatter::new(&writer);
492
493 let written = write(DefaultFormat {
494 timestamp: None,
495 module_path: false,
496 level: false,
497 written_header_value: false,
498 indent: Some(4),
499 suffix: "\n",
500 buf: &mut f,
501 });
502
503 assert_eq!("log\n message\n", written);
504 }
505
506 #[test]
507 fn format_suffix() {
508 let writer = writer::Builder::new()
509 .write_style(WriteStyle::Never)
510 .build();
511
512 let mut f = Formatter::new(&writer);
513
514 let written = write(DefaultFormat {
515 timestamp: None,
516 module_path: false,
517 level: false,
518 written_header_value: false,
519 indent: None,
520 suffix: "\n\n",
521 buf: &mut f,
522 });
523
524 assert_eq!("log\nmessage\n\n", written);
525 }
526
527 #[test]
528 fn format_suffix_with_indent() {
529 let writer = writer::Builder::new()
530 .write_style(WriteStyle::Never)
531 .build();
532
533 let mut f = Formatter::new(&writer);
534
535 let written = write(DefaultFormat {
536 timestamp: None,
537 module_path: false,
538 level: false,
539 written_header_value: false,
540 indent: Some(4),
541 suffix: "\n\n",
542 buf: &mut f,
543 });
544
545 assert_eq!("log\n\n message\n\n", written);
546 }
547}