1use std::cell::RefCell;
61use std::fmt::Display;
62use std::io::prelude::Write;
63use std::rc::Rc;
64use std::{fmt, io, mem};
65
66#[cfg(feature = "color")]
67use log::Level;
68use log::Record;
69
70#[cfg(feature = "humantime")]
71mod humantime;
72#[cfg(feature = "unstable-kv")]
73mod kv;
74pub(crate) mod writer;
75
76#[cfg(feature = "color")]
77pub use anstyle as style;
78
79#[cfg(feature = "humantime")]
80pub use self::humantime::Timestamp;
81#[cfg(feature = "unstable-kv")]
82pub use self::kv::*;
83pub use self::writer::Target;
84pub use self::writer::WriteStyle;
85
86use self::writer::{Buffer, Writer};
87
88#[allow(clippy::exhaustive_enums)] #[derive(Copy, Clone, Debug)]
95pub enum TimestampPrecision {
96 Seconds,
98 Millis,
100 Micros,
102 Nanos,
104}
105
106impl Default for TimestampPrecision {
108 fn default() -> Self {
109 TimestampPrecision::Seconds
110 }
111}
112
113pub struct Formatter {
134 buf: Rc<RefCell<Buffer>>,
135 write_style: WriteStyle,
136}
137
138impl Formatter {
139 pub(crate) fn new(writer: &Writer) -> Self {
140 Formatter {
141 buf: Rc::new(RefCell::new(writer.buffer())),
142 write_style: writer.write_style(),
143 }
144 }
145
146 pub(crate) fn write_style(&self) -> WriteStyle {
147 self.write_style
148 }
149
150 pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
151 writer.print(&self.buf.borrow())
152 }
153
154 pub(crate) fn clear(&mut self) {
155 self.buf.borrow_mut().clear();
156 }
157}
158
159#[cfg(feature = "color")]
160impl Formatter {
161 pub fn default_level_style(&self, level: Level) -> style::Style {
167 if self.write_style == WriteStyle::Never {
168 style::Style::new()
169 } else {
170 match level {
171 Level::Trace => style::AnsiColor::Cyan.on_default(),
172 Level::Debug => style::AnsiColor::Blue.on_default(),
173 Level::Info => style::AnsiColor::Green.on_default(),
174 Level::Warn => style::AnsiColor::Yellow.on_default(),
175 Level::Error => style::AnsiColor::Red
176 .on_default()
177 .effects(style::Effects::BOLD),
178 }
179 }
180 }
181}
182
183impl Write for Formatter {
184 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
185 self.buf.borrow_mut().write(buf)
186 }
187
188 fn flush(&mut self) -> io::Result<()> {
189 self.buf.borrow_mut().flush()
190 }
191}
192
193impl fmt::Debug for Formatter {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 let buf = self.buf.borrow();
196 f.debug_struct("Formatter")
197 .field("buf", &buf)
198 .field("write_style", &self.write_style)
199 .finish()
200 }
201}
202
203pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record<'_>) -> io::Result<()> + Sync + Send>;
204
205pub(crate) struct Builder {
206 pub(crate) format_timestamp: Option<TimestampPrecision>,
207 pub(crate) format_module_path: bool,
208 pub(crate) format_target: bool,
209 pub(crate) format_level: bool,
210 pub(crate) format_indent: Option<usize>,
211 pub(crate) custom_format: Option<FormatFn>,
212 pub(crate) format_suffix: &'static str,
213 pub(crate) format_file: bool,
214 pub(crate) format_line_number: bool,
215 #[cfg(feature = "unstable-kv")]
216 pub(crate) kv_format: Option<Box<KvFormatFn>>,
217 built: bool,
218}
219
220impl Builder {
221 pub(crate) fn build(&mut self) -> FormatFn {
227 assert!(!self.built, "attempt to re-use consumed builder");
228
229 let built = mem::replace(
230 self,
231 Builder {
232 built: true,
233 ..Default::default()
234 },
235 );
236
237 if let Some(fmt) = built.custom_format {
238 fmt
239 } else {
240 Box::new(move |buf, record| {
241 let fmt = DefaultFormat {
242 timestamp: built.format_timestamp,
243 module_path: built.format_module_path,
244 target: built.format_target,
245 level: built.format_level,
246 written_header_value: false,
247 indent: built.format_indent,
248 suffix: built.format_suffix,
249 source_file: built.format_file,
250 source_line_number: built.format_line_number,
251 #[cfg(feature = "unstable-kv")]
252 kv_format: built.kv_format.as_deref().unwrap_or(&default_kv_format),
253 buf,
254 };
255
256 fmt.write(record)
257 })
258 }
259 }
260}
261
262impl Default for Builder {
263 fn default() -> Self {
264 Builder {
265 format_timestamp: Some(Default::default()),
266 format_module_path: false,
267 format_target: true,
268 format_level: true,
269 format_file: false,
270 format_line_number: false,
271 format_indent: Some(4),
272 custom_format: None,
273 format_suffix: "\n",
274 #[cfg(feature = "unstable-kv")]
275 kv_format: None,
276 built: false,
277 }
278 }
279}
280
281#[cfg(feature = "color")]
282type SubtleStyle = StyledValue<&'static str>;
283#[cfg(not(feature = "color"))]
284type SubtleStyle = &'static str;
285
286#[cfg(feature = "color")]
288struct StyledValue<T> {
289 style: style::Style,
290 value: T,
291}
292
293#[cfg(feature = "color")]
294impl<T: Display> Display for StyledValue<T> {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 let style = self.style;
297
298 write!(f, "{style}")?;
301 self.value.fmt(f)?;
302 write!(f, "{style:#}")?;
303 Ok(())
304 }
305}
306
307#[cfg(not(feature = "color"))]
308type StyledValue<T> = T;
309
310struct DefaultFormat<'a> {
314 timestamp: Option<TimestampPrecision>,
315 module_path: bool,
316 target: bool,
317 level: bool,
318 source_file: bool,
319 source_line_number: bool,
320 written_header_value: bool,
321 indent: Option<usize>,
322 buf: &'a mut Formatter,
323 suffix: &'a str,
324 #[cfg(feature = "unstable-kv")]
325 kv_format: &'a KvFormatFn,
326}
327
328impl DefaultFormat<'_> {
329 fn write(mut self, record: &Record<'_>) -> io::Result<()> {
330 self.write_timestamp()?;
331 self.write_level(record)?;
332 self.write_module_path(record)?;
333 self.write_source_location(record)?;
334 self.write_target(record)?;
335 self.finish_header()?;
336
337 self.write_args(record)?;
338 #[cfg(feature = "unstable-kv")]
339 self.write_kv(record)?;
340 write!(self.buf, "{}", self.suffix)
341 }
342
343 fn subtle_style(&self, text: &'static str) -> SubtleStyle {
344 #[cfg(feature = "color")]
345 {
346 StyledValue {
347 style: if self.buf.write_style == WriteStyle::Never {
348 style::Style::new()
349 } else {
350 style::AnsiColor::BrightBlack.on_default()
351 },
352 value: text,
353 }
354 }
355 #[cfg(not(feature = "color"))]
356 {
357 text
358 }
359 }
360
361 fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
362 where
363 T: Display,
364 {
365 if !self.written_header_value {
366 self.written_header_value = true;
367
368 let open_brace = self.subtle_style("[");
369 write!(self.buf, "{open_brace}{value}")
370 } else {
371 write!(self.buf, " {value}")
372 }
373 }
374
375 fn write_level(&mut self, record: &Record<'_>) -> io::Result<()> {
376 if !self.level {
377 return Ok(());
378 }
379
380 let level = {
381 let level = record.level();
382 #[cfg(feature = "color")]
383 {
384 StyledValue {
385 style: self.buf.default_level_style(level),
386 value: level,
387 }
388 }
389 #[cfg(not(feature = "color"))]
390 {
391 level
392 }
393 };
394
395 self.write_header_value(format_args!("{level:<5}"))
396 }
397
398 fn write_timestamp(&mut self) -> io::Result<()> {
399 #[cfg(feature = "humantime")]
400 {
401 use self::TimestampPrecision::{Micros, Millis, Nanos, Seconds};
402 let ts = match self.timestamp {
403 None => return Ok(()),
404 Some(Seconds) => self.buf.timestamp_seconds(),
405 Some(Millis) => self.buf.timestamp_millis(),
406 Some(Micros) => self.buf.timestamp_micros(),
407 Some(Nanos) => self.buf.timestamp_nanos(),
408 };
409
410 self.write_header_value(ts)
411 }
412 #[cfg(not(feature = "humantime"))]
413 {
414 let _ = self.timestamp;
417 Ok(())
418 }
419 }
420
421 fn write_module_path(&mut self, record: &Record<'_>) -> io::Result<()> {
422 if !self.module_path {
423 return Ok(());
424 }
425
426 if let Some(module_path) = record.module_path() {
427 self.write_header_value(module_path)
428 } else {
429 Ok(())
430 }
431 }
432
433 fn write_source_location(&mut self, record: &Record<'_>) -> io::Result<()> {
434 if !self.source_file {
435 return Ok(());
436 }
437
438 if let Some(file_path) = record.file() {
439 let line = self.source_line_number.then(|| record.line()).flatten();
440 match line {
441 Some(line) => self.write_header_value(format_args!("{file_path}:{line}")),
442 None => self.write_header_value(file_path),
443 }
444 } else {
445 Ok(())
446 }
447 }
448
449 fn write_target(&mut self, record: &Record<'_>) -> io::Result<()> {
450 if !self.target {
451 return Ok(());
452 }
453
454 match record.target() {
455 "" => Ok(()),
456 target => self.write_header_value(target),
457 }
458 }
459
460 fn finish_header(&mut self) -> io::Result<()> {
461 if self.written_header_value {
462 let close_brace = self.subtle_style("]");
463 write!(self.buf, "{close_brace} ")
464 } else {
465 Ok(())
466 }
467 }
468
469 fn write_args(&mut self, record: &Record<'_>) -> io::Result<()> {
470 match self.indent {
471 None => write!(self.buf, "{}", record.args()),
473
474 Some(indent_count) => {
475 struct IndentWrapper<'a, 'b> {
478 fmt: &'a mut DefaultFormat<'b>,
479 indent_count: usize,
480 }
481
482 impl Write for IndentWrapper<'_, '_> {
483 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
484 let mut first = true;
485 for chunk in buf.split(|&x| x == b'\n') {
486 if !first {
487 write!(
488 self.fmt.buf,
489 "{}{:width$}",
490 self.fmt.suffix,
491 "",
492 width = self.indent_count
493 )?;
494 }
495 self.fmt.buf.write_all(chunk)?;
496 first = false;
497 }
498
499 Ok(buf.len())
500 }
501
502 fn flush(&mut self) -> io::Result<()> {
503 self.fmt.buf.flush()
504 }
505 }
506
507 {
509 let mut wrapper = IndentWrapper {
510 fmt: self,
511 indent_count,
512 };
513 write!(wrapper, "{}", record.args())?;
514 }
515
516 Ok(())
517 }
518 }
519 }
520
521 #[cfg(feature = "unstable-kv")]
522 fn write_kv(&mut self, record: &Record<'_>) -> io::Result<()> {
523 let format = self.kv_format;
524 format(self.buf, record.key_values())
525 }
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531
532 use log::{Level, Record};
533
534 fn write_record(record: Record<'_>, fmt: DefaultFormat<'_>) -> String {
535 let buf = fmt.buf.buf.clone();
536
537 fmt.write(&record).expect("failed to write record");
538
539 let buf = buf.borrow();
540 String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
541 }
542
543 fn write_target(target: &str, fmt: DefaultFormat<'_>) -> String {
544 write_record(
545 Record::builder()
546 .args(format_args!("log\nmessage"))
547 .level(Level::Info)
548 .file(Some("test.rs"))
549 .line(Some(144))
550 .module_path(Some("test::path"))
551 .target(target)
552 .build(),
553 fmt,
554 )
555 }
556
557 fn write(fmt: DefaultFormat<'_>) -> String {
558 write_target("", fmt)
559 }
560
561 fn formatter() -> Formatter {
562 let writer = writer::Builder::new()
563 .write_style(WriteStyle::Never)
564 .build();
565
566 Formatter::new(&writer)
567 }
568
569 #[test]
570 fn format_with_header() {
571 let mut f = formatter();
572
573 let written = write(DefaultFormat {
574 timestamp: None,
575 module_path: true,
576 target: false,
577 level: true,
578 source_file: false,
579 source_line_number: false,
580 #[cfg(feature = "unstable-kv")]
581 kv_format: &hidden_kv_format,
582 written_header_value: false,
583 indent: None,
584 suffix: "\n",
585 buf: &mut f,
586 });
587
588 assert_eq!("[INFO test::path] log\nmessage\n", written);
589 }
590
591 #[test]
592 fn format_no_header() {
593 let mut f = formatter();
594
595 let written = write(DefaultFormat {
596 timestamp: None,
597 module_path: false,
598 target: false,
599 level: false,
600 source_file: false,
601 source_line_number: false,
602 #[cfg(feature = "unstable-kv")]
603 kv_format: &hidden_kv_format,
604 written_header_value: false,
605 indent: None,
606 suffix: "\n",
607 buf: &mut f,
608 });
609
610 assert_eq!("log\nmessage\n", written);
611 }
612
613 #[test]
614 fn format_indent_spaces() {
615 let mut f = formatter();
616
617 let written = write(DefaultFormat {
618 timestamp: None,
619 module_path: true,
620 target: false,
621 level: true,
622 source_file: false,
623 source_line_number: false,
624 #[cfg(feature = "unstable-kv")]
625 kv_format: &hidden_kv_format,
626 written_header_value: false,
627 indent: Some(4),
628 suffix: "\n",
629 buf: &mut f,
630 });
631
632 assert_eq!("[INFO test::path] log\n message\n", written);
633 }
634
635 #[test]
636 fn format_indent_zero_spaces() {
637 let mut f = formatter();
638
639 let written = write(DefaultFormat {
640 timestamp: None,
641 module_path: true,
642 target: false,
643 level: true,
644 source_file: false,
645 source_line_number: false,
646 #[cfg(feature = "unstable-kv")]
647 kv_format: &hidden_kv_format,
648 written_header_value: false,
649 indent: Some(0),
650 suffix: "\n",
651 buf: &mut f,
652 });
653
654 assert_eq!("[INFO test::path] log\nmessage\n", written);
655 }
656
657 #[test]
658 fn format_indent_spaces_no_header() {
659 let mut f = formatter();
660
661 let written = write(DefaultFormat {
662 timestamp: None,
663 module_path: false,
664 target: false,
665 level: false,
666 source_file: false,
667 source_line_number: false,
668 #[cfg(feature = "unstable-kv")]
669 kv_format: &hidden_kv_format,
670 written_header_value: false,
671 indent: Some(4),
672 suffix: "\n",
673 buf: &mut f,
674 });
675
676 assert_eq!("log\n message\n", written);
677 }
678
679 #[test]
680 fn format_suffix() {
681 let mut f = formatter();
682
683 let written = write(DefaultFormat {
684 timestamp: None,
685 module_path: false,
686 target: false,
687 level: false,
688 source_file: false,
689 source_line_number: false,
690 #[cfg(feature = "unstable-kv")]
691 kv_format: &hidden_kv_format,
692 written_header_value: false,
693 indent: None,
694 suffix: "\n\n",
695 buf: &mut f,
696 });
697
698 assert_eq!("log\nmessage\n\n", written);
699 }
700
701 #[test]
702 fn format_suffix_with_indent() {
703 let mut f = formatter();
704
705 let written = write(DefaultFormat {
706 timestamp: None,
707 module_path: false,
708 target: false,
709 level: false,
710 source_file: false,
711 source_line_number: false,
712 #[cfg(feature = "unstable-kv")]
713 kv_format: &hidden_kv_format,
714 written_header_value: false,
715 indent: Some(4),
716 suffix: "\n\n",
717 buf: &mut f,
718 });
719
720 assert_eq!("log\n\n message\n\n", written);
721 }
722
723 #[test]
724 fn format_target() {
725 let mut f = formatter();
726
727 let written = write_target(
728 "target",
729 DefaultFormat {
730 timestamp: None,
731 module_path: true,
732 target: true,
733 level: true,
734 source_file: false,
735 source_line_number: false,
736 #[cfg(feature = "unstable-kv")]
737 kv_format: &hidden_kv_format,
738 written_header_value: false,
739 indent: None,
740 suffix: "\n",
741 buf: &mut f,
742 },
743 );
744
745 assert_eq!("[INFO test::path target] log\nmessage\n", written);
746 }
747
748 #[test]
749 fn format_empty_target() {
750 let mut f = formatter();
751
752 let written = write(DefaultFormat {
753 timestamp: None,
754 module_path: true,
755 target: true,
756 level: true,
757 source_file: false,
758 source_line_number: false,
759 #[cfg(feature = "unstable-kv")]
760 kv_format: &hidden_kv_format,
761 written_header_value: false,
762 indent: None,
763 suffix: "\n",
764 buf: &mut f,
765 });
766
767 assert_eq!("[INFO test::path] log\nmessage\n", written);
768 }
769
770 #[test]
771 fn format_no_target() {
772 let mut f = formatter();
773
774 let written = write_target(
775 "target",
776 DefaultFormat {
777 timestamp: None,
778 module_path: true,
779 target: false,
780 level: true,
781 source_file: false,
782 source_line_number: false,
783 #[cfg(feature = "unstable-kv")]
784 kv_format: &hidden_kv_format,
785 written_header_value: false,
786 indent: None,
787 suffix: "\n",
788 buf: &mut f,
789 },
790 );
791
792 assert_eq!("[INFO test::path] log\nmessage\n", written);
793 }
794
795 #[test]
796 fn format_with_source_file_and_line_number() {
797 let mut f = formatter();
798
799 let written = write(DefaultFormat {
800 timestamp: None,
801 module_path: false,
802 target: false,
803 level: true,
804 source_file: true,
805 source_line_number: true,
806 #[cfg(feature = "unstable-kv")]
807 kv_format: &hidden_kv_format,
808 written_header_value: false,
809 indent: None,
810 suffix: "\n",
811 buf: &mut f,
812 });
813
814 assert_eq!("[INFO test.rs:144] log\nmessage\n", written);
815 }
816
817 #[cfg(feature = "unstable-kv")]
818 #[test]
819 fn format_kv_default() {
820 let kvs = &[("a", 1u32), ("b", 2u32)][..];
821 let mut f = formatter();
822 let record = Record::builder()
823 .args(format_args!("log message"))
824 .level(Level::Info)
825 .module_path(Some("test::path"))
826 .key_values(&kvs)
827 .build();
828
829 let written = write_record(
830 record,
831 DefaultFormat {
832 timestamp: None,
833 module_path: false,
834 target: false,
835 level: true,
836 source_file: false,
837 source_line_number: false,
838 kv_format: &default_kv_format,
839 written_header_value: false,
840 indent: None,
841 suffix: "\n",
842 buf: &mut f,
843 },
844 );
845
846 assert_eq!("[INFO ] log message a=1 b=2\n", written);
847 }
848
849 #[cfg(feature = "unstable-kv")]
850 #[test]
851 fn format_kv_default_full() {
852 let kvs = &[("a", 1u32), ("b", 2u32)][..];
853 let mut f = formatter();
854 let record = Record::builder()
855 .args(format_args!("log\nmessage"))
856 .level(Level::Info)
857 .module_path(Some("test::path"))
858 .target("target")
859 .file(Some("test.rs"))
860 .line(Some(42))
861 .key_values(&kvs)
862 .build();
863
864 let written = write_record(
865 record,
866 DefaultFormat {
867 timestamp: None,
868 module_path: true,
869 target: true,
870 level: true,
871 source_file: true,
872 source_line_number: true,
873 kv_format: &default_kv_format,
874 written_header_value: false,
875 indent: None,
876 suffix: "\n",
877 buf: &mut f,
878 },
879 );
880
881 assert_eq!(
882 "[INFO test::path test.rs:42 target] log\nmessage a=1 b=2\n",
883 written
884 );
885 }
886}