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 = "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 = "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) trait RecordFormat {
204 fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()>;
205}
206
207impl<F> RecordFormat for F
208where
209 F: Fn(&mut Formatter, &Record<'_>) -> io::Result<()>,
210{
211 fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> {
212 (self)(formatter, record)
213 }
214}
215
216pub(crate) type FormatFn = Box<dyn RecordFormat + Sync + Send>;
217
218#[derive(Default)]
219pub(crate) struct Builder {
220 pub(crate) default_format: ConfigurableFormat,
221 pub(crate) custom_format: Option<FormatFn>,
222 built: bool,
223}
224
225impl Builder {
226 pub(crate) fn build(&mut self) -> FormatFn {
232 assert!(!self.built, "attempt to re-use consumed builder");
233
234 let built = mem::replace(
235 self,
236 Builder {
237 built: true,
238 ..Default::default()
239 },
240 );
241
242 if let Some(fmt) = built.custom_format {
243 fmt
244 } else {
245 Box::new(built.default_format)
246 }
247 }
248}
249
250#[cfg(feature = "color")]
251type SubtleStyle = StyledValue<&'static str>;
252#[cfg(not(feature = "color"))]
253type SubtleStyle = &'static str;
254
255#[cfg(feature = "color")]
257struct StyledValue<T> {
258 style: style::Style,
259 value: T,
260}
261
262#[cfg(feature = "color")]
263impl<T: Display> Display for StyledValue<T> {
264 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265 let style = self.style;
266
267 write!(f, "{style}")?;
270 self.value.fmt(f)?;
271 write!(f, "{style:#}")?;
272 Ok(())
273 }
274}
275
276#[cfg(not(feature = "color"))]
277type StyledValue<T> = T;
278
279pub struct ConfigurableFormat {
281 pub(crate) timestamp: Option<TimestampPrecision>,
283 pub(crate) module_path: bool,
284 pub(crate) target: bool,
285 pub(crate) level: bool,
286 pub(crate) source_file: bool,
287 pub(crate) source_line_number: bool,
288 pub(crate) indent: Option<usize>,
289 pub(crate) suffix: &'static str,
290 #[cfg(feature = "kv")]
291 pub(crate) kv_format: Option<Box<KvFormatFn>>,
292}
293
294impl ConfigurableFormat {
295 pub fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> {
297 let fmt = ConfigurableFormatWriter {
298 format: self,
299 buf: formatter,
300 written_header_value: false,
301 };
302
303 fmt.write(record)
304 }
305}
306
307impl ConfigurableFormat {
308 pub fn level(&mut self, write: bool) -> &mut Self {
310 self.level = write;
311 self
312 }
313
314 pub fn file(&mut self, write: bool) -> &mut Self {
316 self.source_file = write;
317 self
318 }
319
320 pub fn line_number(&mut self, write: bool) -> &mut Self {
324 self.source_line_number = write;
325 self
326 }
327
328 pub fn module_path(&mut self, write: bool) -> &mut Self {
330 self.module_path = write;
331 self
332 }
333
334 pub fn target(&mut self, write: bool) -> &mut Self {
336 self.target = write;
337 self
338 }
339
340 pub fn indent(&mut self, indent: Option<usize>) -> &mut Self {
343 self.indent = indent;
344 self
345 }
346
347 pub fn timestamp(&mut self, timestamp: Option<TimestampPrecision>) -> &mut Self {
349 self.timestamp = timestamp;
350 self
351 }
352
353 pub fn suffix(&mut self, suffix: &'static str) -> &mut Self {
355 self.suffix = suffix;
356 self
357 }
358
359 #[cfg(feature = "kv")]
370 pub fn key_values<F>(&mut self, format: F) -> &mut Self
371 where
372 F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static,
373 {
374 self.kv_format = Some(Box::new(format));
375 self
376 }
377}
378
379impl Default for ConfigurableFormat {
380 fn default() -> Self {
381 Self {
382 timestamp: Some(Default::default()),
383 module_path: false,
384 target: true,
385 level: true,
386 source_file: false,
387 source_line_number: false,
388 indent: Some(4),
389 suffix: "\n",
390 #[cfg(feature = "kv")]
391 kv_format: None,
392 }
393 }
394}
395
396impl RecordFormat for ConfigurableFormat {
397 fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> {
398 self.format(formatter, record)
399 }
400}
401
402struct ConfigurableFormatWriter<'a> {
406 format: &'a ConfigurableFormat,
407 buf: &'a mut Formatter,
408 written_header_value: bool,
409}
410
411impl ConfigurableFormatWriter<'_> {
412 fn write(mut self, record: &Record<'_>) -> io::Result<()> {
413 self.write_timestamp()?;
414 self.write_level(record)?;
415 self.write_module_path(record)?;
416 self.write_source_location(record)?;
417 self.write_target(record)?;
418 self.finish_header()?;
419
420 self.write_args(record)?;
421 #[cfg(feature = "kv")]
422 self.write_kv(record)?;
423 write!(self.buf, "{}", self.format.suffix)
424 }
425
426 fn subtle_style(&self, text: &'static str) -> SubtleStyle {
427 #[cfg(feature = "color")]
428 {
429 StyledValue {
430 style: if self.buf.write_style == WriteStyle::Never {
431 style::Style::new()
432 } else {
433 style::AnsiColor::BrightBlack.on_default()
434 },
435 value: text,
436 }
437 }
438 #[cfg(not(feature = "color"))]
439 {
440 text
441 }
442 }
443
444 fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
445 where
446 T: Display,
447 {
448 if !self.written_header_value {
449 self.written_header_value = true;
450
451 let open_brace = self.subtle_style("[");
452 write!(self.buf, "{open_brace}{value}")
453 } else {
454 write!(self.buf, " {value}")
455 }
456 }
457
458 fn write_level(&mut self, record: &Record<'_>) -> io::Result<()> {
459 if !self.format.level {
460 return Ok(());
461 }
462
463 let level = {
464 let level = record.level();
465 #[cfg(feature = "color")]
466 {
467 StyledValue {
468 style: self.buf.default_level_style(level),
469 value: level,
470 }
471 }
472 #[cfg(not(feature = "color"))]
473 {
474 level
475 }
476 };
477
478 self.write_header_value(format_args!("{level:<5}"))
479 }
480
481 fn write_timestamp(&mut self) -> io::Result<()> {
482 #[cfg(feature = "humantime")]
483 {
484 use self::TimestampPrecision::{Micros, Millis, Nanos, Seconds};
485 let ts = match self.format.timestamp {
486 None => return Ok(()),
487 Some(Seconds) => self.buf.timestamp_seconds(),
488 Some(Millis) => self.buf.timestamp_millis(),
489 Some(Micros) => self.buf.timestamp_micros(),
490 Some(Nanos) => self.buf.timestamp_nanos(),
491 };
492
493 self.write_header_value(ts)
494 }
495 #[cfg(not(feature = "humantime"))]
496 {
497 let _ = self.format.timestamp;
500 Ok(())
501 }
502 }
503
504 fn write_module_path(&mut self, record: &Record<'_>) -> io::Result<()> {
505 if !self.format.module_path {
506 return Ok(());
507 }
508
509 if let Some(module_path) = record.module_path() {
510 self.write_header_value(module_path)
511 } else {
512 Ok(())
513 }
514 }
515
516 fn write_source_location(&mut self, record: &Record<'_>) -> io::Result<()> {
517 if !self.format.source_file {
518 return Ok(());
519 }
520
521 if let Some(file_path) = record.file() {
522 let line = self
523 .format
524 .source_line_number
525 .then(|| record.line())
526 .flatten();
527 match line {
528 Some(line) => self.write_header_value(format_args!("{file_path}:{line}")),
529 None => self.write_header_value(file_path),
530 }
531 } else {
532 Ok(())
533 }
534 }
535
536 fn write_target(&mut self, record: &Record<'_>) -> io::Result<()> {
537 if !self.format.target {
538 return Ok(());
539 }
540
541 match record.target() {
542 "" => Ok(()),
543 target => self.write_header_value(target),
544 }
545 }
546
547 fn finish_header(&mut self) -> io::Result<()> {
548 if self.written_header_value {
549 let close_brace = self.subtle_style("]");
550 write!(self.buf, "{close_brace} ")
551 } else {
552 Ok(())
553 }
554 }
555
556 fn write_args(&mut self, record: &Record<'_>) -> io::Result<()> {
557 match self.format.indent {
558 None => write!(self.buf, "{}", record.args()),
560
561 Some(indent_count) => {
562 struct IndentWrapper<'a, 'b> {
565 fmt: &'a mut ConfigurableFormatWriter<'b>,
566 indent_count: usize,
567 }
568
569 impl Write for IndentWrapper<'_, '_> {
570 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
571 let mut first = true;
572 for chunk in buf.split(|&x| x == b'\n') {
573 if !first {
574 write!(
575 self.fmt.buf,
576 "{}{:width$}",
577 self.fmt.format.suffix,
578 "",
579 width = self.indent_count
580 )?;
581 }
582 self.fmt.buf.write_all(chunk)?;
583 first = false;
584 }
585
586 Ok(buf.len())
587 }
588
589 fn flush(&mut self) -> io::Result<()> {
590 self.fmt.buf.flush()
591 }
592 }
593
594 {
596 let mut wrapper = IndentWrapper {
597 fmt: self,
598 indent_count,
599 };
600 write!(wrapper, "{}", record.args())?;
601 }
602
603 Ok(())
604 }
605 }
606 }
607
608 #[cfg(feature = "kv")]
609 fn write_kv(&mut self, record: &Record<'_>) -> io::Result<()> {
610 let format = self
611 .format
612 .kv_format
613 .as_deref()
614 .unwrap_or(&default_kv_format);
615 format(self.buf, record.key_values())
616 }
617}
618
619#[cfg(test)]
620mod tests {
621 use super::*;
622
623 use log::{Level, Record};
624
625 fn write_record(record: Record<'_>, fmt: ConfigurableFormatWriter<'_>) -> String {
626 let buf = fmt.buf.buf.clone();
627
628 fmt.write(&record).expect("failed to write record");
629
630 let buf = buf.borrow();
631 String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
632 }
633
634 fn write_target(target: &str, fmt: ConfigurableFormatWriter<'_>) -> String {
635 write_record(
636 Record::builder()
637 .args(format_args!("log\nmessage"))
638 .level(Level::Info)
639 .file(Some("test.rs"))
640 .line(Some(144))
641 .module_path(Some("test::path"))
642 .target(target)
643 .build(),
644 fmt,
645 )
646 }
647
648 fn write(fmt: ConfigurableFormatWriter<'_>) -> String {
649 write_target("", fmt)
650 }
651
652 fn formatter() -> Formatter {
653 let writer = writer::Builder::new()
654 .write_style(WriteStyle::Never)
655 .build();
656
657 Formatter::new(&writer)
658 }
659
660 #[test]
661 fn format_with_header() {
662 let mut f = formatter();
663
664 let written = write(ConfigurableFormatWriter {
665 format: &ConfigurableFormat {
666 timestamp: None,
667 module_path: true,
668 target: false,
669 level: true,
670 source_file: false,
671 source_line_number: false,
672 #[cfg(feature = "kv")]
673 kv_format: Some(Box::new(hidden_kv_format)),
674 indent: None,
675 suffix: "\n",
676 },
677 written_header_value: false,
678 buf: &mut f,
679 });
680
681 assert_eq!("[INFO test::path] log\nmessage\n", written);
682 }
683
684 #[test]
685 fn format_no_header() {
686 let mut f = formatter();
687
688 let written = write(ConfigurableFormatWriter {
689 format: &ConfigurableFormat {
690 timestamp: None,
691 module_path: false,
692 target: false,
693 level: false,
694 source_file: false,
695 source_line_number: false,
696 #[cfg(feature = "kv")]
697 kv_format: Some(Box::new(hidden_kv_format)),
698 indent: None,
699 suffix: "\n",
700 },
701 written_header_value: false,
702 buf: &mut f,
703 });
704
705 assert_eq!("log\nmessage\n", written);
706 }
707
708 #[test]
709 fn format_indent_spaces() {
710 let mut f = formatter();
711
712 let written = write(ConfigurableFormatWriter {
713 format: &ConfigurableFormat {
714 timestamp: None,
715 module_path: true,
716 target: false,
717 level: true,
718 source_file: false,
719 source_line_number: false,
720 #[cfg(feature = "kv")]
721 kv_format: Some(Box::new(hidden_kv_format)),
722 indent: Some(4),
723 suffix: "\n",
724 },
725 written_header_value: false,
726 buf: &mut f,
727 });
728
729 assert_eq!("[INFO test::path] log\n message\n", written);
730 }
731
732 #[test]
733 fn format_indent_zero_spaces() {
734 let mut f = formatter();
735
736 let written = write(ConfigurableFormatWriter {
737 format: &ConfigurableFormat {
738 timestamp: None,
739 module_path: true,
740 target: false,
741 level: true,
742 source_file: false,
743 source_line_number: false,
744 #[cfg(feature = "kv")]
745 kv_format: Some(Box::new(hidden_kv_format)),
746 indent: Some(0),
747 suffix: "\n",
748 },
749 written_header_value: false,
750 buf: &mut f,
751 });
752
753 assert_eq!("[INFO test::path] log\nmessage\n", written);
754 }
755
756 #[test]
757 fn format_indent_spaces_no_header() {
758 let mut f = formatter();
759
760 let written = write(ConfigurableFormatWriter {
761 format: &ConfigurableFormat {
762 timestamp: None,
763 module_path: false,
764 target: false,
765 level: false,
766 source_file: false,
767 source_line_number: false,
768 #[cfg(feature = "kv")]
769 kv_format: Some(Box::new(hidden_kv_format)),
770 indent: Some(4),
771 suffix: "\n",
772 },
773 written_header_value: false,
774 buf: &mut f,
775 });
776
777 assert_eq!("log\n message\n", written);
778 }
779
780 #[test]
781 fn format_suffix() {
782 let mut f = formatter();
783
784 let written = write(ConfigurableFormatWriter {
785 format: &ConfigurableFormat {
786 timestamp: None,
787 module_path: false,
788 target: false,
789 level: false,
790 source_file: false,
791 source_line_number: false,
792 #[cfg(feature = "kv")]
793 kv_format: Some(Box::new(hidden_kv_format)),
794 indent: None,
795 suffix: "\n\n",
796 },
797 written_header_value: false,
798 buf: &mut f,
799 });
800
801 assert_eq!("log\nmessage\n\n", written);
802 }
803
804 #[test]
805 fn format_suffix_with_indent() {
806 let mut f = formatter();
807
808 let written = write(ConfigurableFormatWriter {
809 format: &ConfigurableFormat {
810 timestamp: None,
811 module_path: false,
812 target: false,
813 level: false,
814 source_file: false,
815 source_line_number: false,
816 #[cfg(feature = "kv")]
817 kv_format: Some(Box::new(hidden_kv_format)),
818 indent: Some(4),
819 suffix: "\n\n",
820 },
821 written_header_value: false,
822 buf: &mut f,
823 });
824
825 assert_eq!("log\n\n message\n\n", written);
826 }
827
828 #[test]
829 fn format_target() {
830 let mut f = formatter();
831
832 let written = write_target(
833 "target",
834 ConfigurableFormatWriter {
835 format: &ConfigurableFormat {
836 timestamp: None,
837 module_path: true,
838 target: true,
839 level: true,
840 source_file: false,
841 source_line_number: false,
842 #[cfg(feature = "kv")]
843 kv_format: Some(Box::new(hidden_kv_format)),
844 indent: None,
845 suffix: "\n",
846 },
847 written_header_value: false,
848 buf: &mut f,
849 },
850 );
851
852 assert_eq!("[INFO test::path target] log\nmessage\n", written);
853 }
854
855 #[test]
856 fn format_empty_target() {
857 let mut f = formatter();
858
859 let written = write(ConfigurableFormatWriter {
860 format: &ConfigurableFormat {
861 timestamp: None,
862 module_path: true,
863 target: true,
864 level: true,
865 source_file: false,
866 source_line_number: false,
867 #[cfg(feature = "kv")]
868 kv_format: Some(Box::new(hidden_kv_format)),
869 indent: None,
870 suffix: "\n",
871 },
872 written_header_value: false,
873 buf: &mut f,
874 });
875
876 assert_eq!("[INFO test::path] log\nmessage\n", written);
877 }
878
879 #[test]
880 fn format_no_target() {
881 let mut f = formatter();
882
883 let written = write_target(
884 "target",
885 ConfigurableFormatWriter {
886 format: &ConfigurableFormat {
887 timestamp: None,
888 module_path: true,
889 target: false,
890 level: true,
891 source_file: false,
892 source_line_number: false,
893 #[cfg(feature = "kv")]
894 kv_format: Some(Box::new(hidden_kv_format)),
895 indent: None,
896 suffix: "\n",
897 },
898 written_header_value: false,
899 buf: &mut f,
900 },
901 );
902
903 assert_eq!("[INFO test::path] log\nmessage\n", written);
904 }
905
906 #[test]
907 fn format_with_source_file_and_line_number() {
908 let mut f = formatter();
909
910 let written = write(ConfigurableFormatWriter {
911 format: &ConfigurableFormat {
912 timestamp: None,
913 module_path: false,
914 target: false,
915 level: true,
916 source_file: true,
917 source_line_number: true,
918 #[cfg(feature = "kv")]
919 kv_format: Some(Box::new(hidden_kv_format)),
920 indent: None,
921 suffix: "\n",
922 },
923 written_header_value: false,
924 buf: &mut f,
925 });
926
927 assert_eq!("[INFO test.rs:144] log\nmessage\n", written);
928 }
929
930 #[cfg(feature = "kv")]
931 #[test]
932 fn format_kv_default() {
933 let kvs = &[("a", 1u32), ("b", 2u32)][..];
934 let mut f = formatter();
935 let record = Record::builder()
936 .args(format_args!("log message"))
937 .level(Level::Info)
938 .module_path(Some("test::path"))
939 .key_values(&kvs)
940 .build();
941
942 let written = write_record(
943 record,
944 ConfigurableFormatWriter {
945 format: &ConfigurableFormat {
946 timestamp: None,
947 module_path: false,
948 target: false,
949 level: true,
950 source_file: false,
951 source_line_number: false,
952 kv_format: Some(Box::new(default_kv_format)),
953 indent: None,
954 suffix: "\n",
955 },
956 written_header_value: false,
957 buf: &mut f,
958 },
959 );
960
961 assert_eq!("[INFO ] log message a=1 b=2\n", written);
962 }
963
964 #[cfg(feature = "kv")]
965 #[test]
966 fn format_kv_default_full() {
967 let kvs = &[("a", 1u32), ("b", 2u32)][..];
968 let mut f = formatter();
969 let record = Record::builder()
970 .args(format_args!("log\nmessage"))
971 .level(Level::Info)
972 .module_path(Some("test::path"))
973 .target("target")
974 .file(Some("test.rs"))
975 .line(Some(42))
976 .key_values(&kvs)
977 .build();
978
979 let written = write_record(
980 record,
981 ConfigurableFormatWriter {
982 format: &ConfigurableFormat {
983 timestamp: None,
984 module_path: true,
985 target: true,
986 level: true,
987 source_file: true,
988 source_line_number: true,
989 kv_format: Some(Box::new(default_kv_format)),
990 indent: None,
991 suffix: "\n",
992 },
993 written_header_value: false,
994 buf: &mut f,
995 },
996 );
997
998 assert_eq!(
999 "[INFO test::path test.rs:42 target] log\nmessage a=1 b=2\n",
1000 written
1001 );
1002 }
1003}