spdlog/formatter/
full_formatter.rs

1//! Provides a full info formatter.
2
3use std::fmt::{self, Write};
4
5use crate::{
6    formatter::{fmt_with_time, Formatter, FormatterContext, TimeDate},
7    Error, Record, StringBuf, __EOL,
8};
9
10#[rustfmt::skip]
11/// Full information logs formatter.
12///
13/// It is the default formatter for most sinks.
14///
15/// Log messages formatted by it look like:
16///
17///  - Default:
18///
19///    <pre>
20///    [2022-11-02 09:23:12.263] [<font color="#0DBC79">info</font>] hello, world! { key1=value1 key2=value2 }
21///    </pre>
22///
23///  - If the logger has a name:
24///
25///    <pre>
26///    [2022-11-02 09:23:12.263] [logger-name] [<font color="#0DBC79">info</font>] hello, world! { key1=value1 key2=value2 }
27///    </pre>
28/// 
29///  - If crate feature `source-location` is enabled:
30///
31///    <pre>
32///    [2022-11-02 09:23:12.263] [logger-name] [<font color="#0DBC79">info</font>] [mod::path, src/main.rs:4] hello, world! { key1=value1 key2=value2 }
33///    </pre>
34#[derive(Clone)]
35pub struct FullFormatter {
36    with_eol: bool,
37}
38
39impl FullFormatter {
40    /// Constructs a `FullFormatter`.
41    #[must_use]
42    pub fn new() -> FullFormatter {
43        FullFormatter { with_eol: true }
44    }
45
46    #[must_use]
47    pub(crate) fn without_eol() -> Self {
48        Self { with_eol: false }
49    }
50
51    fn format_impl(
52        &self,
53        record: &Record,
54        dest: &mut StringBuf,
55        ctx: &mut FormatterContext,
56    ) -> Result<(), fmt::Error> {
57        #[cfg(not(feature = "flexible-string"))]
58        dest.reserve(crate::string_buf::RESERVE_SIZE);
59
60        fmt_with_time(
61            ctx,
62            record,
63            |mut time: TimeDate| -> Result<(), fmt::Error> {
64                dest.write_str("[")?;
65                dest.write_str(time.full_second_str())?;
66                dest.write_str(".")?;
67                write!(dest, "{:03}", time.millisecond())?;
68                dest.write_str("] [")?;
69                Ok(())
70            },
71        )?;
72
73        if let Some(logger_name) = record.logger_name() {
74            dest.write_str(logger_name)?;
75            dest.write_str("] [")?;
76        }
77
78        let style_range_begin = dest.len();
79
80        dest.write_str(record.level().as_str())?;
81
82        let style_range_end = dest.len();
83
84        if let Some(srcloc) = record.source_location() {
85            dest.write_str("] [")?;
86            dest.write_str(srcloc.module_path())?;
87            dest.write_str(", ")?;
88            dest.write_str(srcloc.file())?;
89            dest.write_str(":")?;
90            write!(dest, "{}", srcloc.line())?;
91        }
92
93        dest.write_str("] ")?;
94        dest.write_str(record.payload())?;
95
96        record.key_values().write_to(dest, true)?;
97
98        if self.with_eol {
99            dest.write_str(__EOL)?;
100        }
101
102        ctx.set_style_range(Some(style_range_begin..style_range_end));
103        Ok(())
104    }
105}
106
107impl Formatter for FullFormatter {
108    fn format(
109        &self,
110        record: &Record,
111        dest: &mut StringBuf,
112        ctx: &mut FormatterContext,
113    ) -> crate::Result<()> {
114        self.format_impl(record, dest, ctx)
115            .map_err(Error::FormatRecord)
116    }
117}
118
119impl Default for FullFormatter {
120    fn default() -> FullFormatter {
121        FullFormatter::new()
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use chrono::prelude::*;
128
129    use super::*;
130    use crate::{kv, Level, __EOL};
131
132    #[test]
133    fn format() {
134        let kvs = [
135            (kv::Key::__from_static_str("k1"), kv::Value::from(114)),
136            (kv::Key::__from_static_str("k2"), kv::Value::from("514")),
137        ];
138        let record = Record::new(Level::Warn, "test log content", None, None, &kvs);
139        let mut buf = StringBuf::new();
140        let mut ctx = FormatterContext::new();
141        FullFormatter::new()
142            .format(&record, &mut buf, &mut ctx)
143            .unwrap();
144
145        let local_time: DateTime<Local> = record.time().into();
146        assert_eq!(
147            format!(
148                "[{}] [warn] test log content {{ k1=114 k2=514 }}{}",
149                local_time.format("%Y-%m-%d %H:%M:%S.%3f"),
150                __EOL
151            ),
152            buf
153        );
154        assert_eq!(Some(27..31), ctx.style_range());
155    }
156}