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