spdlog/formatter/pattern_formatter/
runtime.rs

1use spdlog_internal::pattern_parser::{
2    error::TemplateError,
3    parse::{Template, TemplateToken},
4    BuiltInFormatter, BuiltInFormatterInner, Error as PatternParserError,
5    PatternKind as GenericPatternKind, PatternRegistry as GenericPatternRegistry,
6    Result as PatternParserResult,
7};
8
9use super::{Pattern, PatternContext, __pattern as pattern};
10use crate::{
11    error::{BuildPatternError, Error},
12    Record, Result, StringBuf,
13};
14
15type Patterns = Vec<Box<dyn Pattern>>;
16type PatternCreator = Box<dyn Fn() -> Box<dyn Pattern>>;
17type PatternRegistry = GenericPatternRegistry<PatternCreator>;
18type PatternKind = GenericPatternKind<PatternCreator>;
19
20/// Builds a pattern from a template string at runtime.
21///
22/// It accepts inputs in the form:
23///
24/// ```ignore
25/// // This is not exactly a valid declarative macro, just for intuition.
26/// macro_rules! runtime_pattern {
27///     ( $template:expr $(,)? ) => {};
28///     ( $template:expr, $( {$$custom:ident} => $ctor:expr ),+ $(,)? ) => {};
29/// }
30/// ```
31///
32/// The only difference between `runtime_pattern!` macro and [`pattern!`] macro
33/// is that [`pattern!`] macro only accepts a string literal as the pattern
34/// template, while `runtime_pattern!` macro accepts an expression that can be
35/// evaluated to the pattern template string at runtime.
36///
37/// The returen type of `runtime_pattern!` macro is
38/// `Result<RuntimePattern, spdlog::Error>`. An error will be returned when
39/// parsing of the template string fails. If any of the custom patterns given
40/// are invalid, a compilation error will be triggered.
41///
42/// For the input formats and more usages, please refer to [`pattern!`] macro.
43///
44/// # Example
45///
46/// ```
47/// use spdlog::formatter::{runtime_pattern, PatternFormatter};
48///
49/// # type MyPattern = spdlog::formatter::__pattern::Level;
50/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
51/// let template = String::from("[{level}] {payload} - {$mypat}{eol}");
52/// let pat = runtime_pattern!(&template, {$mypat} => MyPattern::default)?;
53/// let formatter = PatternFormatter::new(pat);
54/// # Ok(()) }
55/// ```
56///
57/// [`pattern!`]: crate::formatter::pattern
58pub use spdlog_macros::runtime_pattern;
59
60#[rustfmt::skip] // rustfmt currently breaks some empty lines if `#[doc = include_str!("xxx")]` exists
61/// Runtime pattern built via [`runtime_pattern!`] macro.
62///
63/// ## Basic Usage
64/// 
65/// ```
66/// # use spdlog::formatter::{runtime_pattern, PatternFormatter};
67/// use spdlog::info;
68///
69/// #
70#[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))]
71/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
72/// let formatter = PatternFormatter::new(runtime_pattern!("[{level}] {payload}{eol}")?);
73/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(
74/// #     Box::new(formatter),
75/// #     None
76/// # );
77///
78/// info!(logger: doctest, "Interesting log message");
79/// # assert_eq!(
80/// #     sink.clone_string().replace("\r", ""),
81/// /* Output */ "[info] Interesting log message\n"
82/// # );
83/// # Ok(()) }
84/// ```
85/// 
86/// ## With Custom Patterns
87/// 
88/// ```
89/// use std::fmt::Write;
90///
91/// use spdlog::{
92///     formatter::{pattern, Pattern, PatternContext, PatternFormatter, runtime_pattern, RuntimePattern},
93///     Record, StringBuf, info
94/// };
95/// 
96/// #[derive(Default, Clone)]
97/// struct MyPattern;
98/// 
99/// impl Pattern for MyPattern {
100///    fn format(&self, record: &Record, dest: &mut StringBuf, _: &mut PatternContext) -> spdlog::Result<()> {
101///        write!(dest, "My own pattern").map_err(spdlog::Error::FormatRecord)
102///    }
103/// }
104///
105#[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))]
106/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
107/// let template = "[{level}] {payload} - {$mypat1} {$mypat2}{eol}";
108/// # // TODO: Directly pass the closure to runtime_pattern! macro
109/// fn pat() -> impl Pattern { pattern!("[{level_short}-{level}]") }
110/// let formatter = PatternFormatter::new(
111///     runtime_pattern!(
112///         template,
113///         {$mypat1} => MyPattern::default,
114///         {$mypat2} => pat
115///     )?
116/// );
117/// # let (doctest, sink) = test_utils::echo_logger_from_formatter(
118/// #     Box::new(formatter),
119/// #     None
120/// # );
121///
122/// info!(logger: doctest, "Interesting log message");
123/// # assert_eq!(
124/// #     sink.clone_string().replace("\r", ""),
125/// /* Output */ "[info] Interesting log message - My own pattern [I-info]\n"
126/// # );
127/// # Ok(()) }
128/// ```
129/// 
130/// [`pattern!`]: crate::formatter::pattern
131#[derive(Clone)]
132pub struct RuntimePattern(Patterns);
133
134impl RuntimePattern {
135    // Private function, do not use in your code directly.
136    #[doc(hidden)]
137    pub fn __with_custom_patterns(template: &str, registry: PatternRegistry) -> Result<Self> {
138        Template::parse(template)
139            .and_then(|template| {
140                Synthesiser::new(registry)
141                    .synthesize(template)
142                    .map(RuntimePattern)
143            })
144            .map_err(|err| Error::BuildPattern(BuildPatternError(err)))
145    }
146}
147
148impl Pattern for RuntimePattern {
149    fn format(
150        &self,
151        record: &Record,
152        dest: &mut StringBuf,
153        ctx: &mut PatternContext,
154    ) -> Result<()> {
155        for pattern in &self.0 {
156            pattern.format(record, dest, ctx)?;
157        }
158        Ok(())
159    }
160}
161
162struct Synthesiser {
163    registry: PatternRegistry,
164}
165
166impl Synthesiser {
167    fn new(registry: PatternRegistry) -> Self {
168        Self { registry }
169    }
170
171    fn synthesize(&self, template: Template) -> PatternParserResult<Patterns> {
172        self.build_patterns(template, false)
173    }
174
175    fn build_patterns(
176        &self,
177        template: Template,
178        mut style_range_seen: bool,
179    ) -> PatternParserResult<Patterns> {
180        let mut patterns = Patterns::new();
181
182        for token in template.tokens {
183            let pattern = match token {
184                TemplateToken::Literal(t) => Box::new(t.literal),
185                TemplateToken::Formatter(t) => {
186                    let pattern = self.registry.find(t.has_custom_prefix, t.placeholder)?;
187                    match pattern {
188                        PatternKind::BuiltIn(builtin) => build_builtin_pattern(builtin),
189                        PatternKind::Custom { factory, .. } => factory(),
190                    }
191                }
192                TemplateToken::StyleRange(style_range) => {
193                    if style_range_seen {
194                        return Err(PatternParserError::Template(
195                            TemplateError::MultipleStyleRange,
196                        ));
197                    }
198                    style_range_seen = true;
199                    Box::new(pattern::StyleRange::new(
200                        self.build_patterns(style_range.body, true)?,
201                    ))
202                }
203            };
204            patterns.push(pattern);
205        }
206
207        Ok(patterns)
208    }
209}
210
211fn build_builtin_pattern(builtin: &BuiltInFormatter) -> Box<dyn Pattern> {
212    macro_rules! match_builtin {
213        (  $($name:ident),+ $(,)? ) => {
214            match builtin.inner() {
215                $(BuiltInFormatterInner::$name => Box::<pattern::$name>::default()),+
216            }
217        };
218    }
219
220    match_builtin!(
221        AbbrWeekdayName,
222        WeekdayName,
223        AbbrMonthName,
224        MonthName,
225        FullDateTime,
226        ShortYear,
227        Year,
228        ShortDate,
229        Date,
230        Month,
231        Day,
232        Hour,
233        Hour12,
234        Minute,
235        Second,
236        Millisecond,
237        Microsecond,
238        Nanosecond,
239        AmPm,
240        Time12,
241        ShortTime,
242        Time,
243        TzOffset,
244        UnixTimestamp,
245        Full,
246        Level,
247        ShortLevel,
248        Source,
249        SourceFilename,
250        SourceFile,
251        SourceLine,
252        SourceColumn,
253        SourceModulePath,
254        LoggerName,
255        Payload,
256        ProcessId,
257        ThreadId,
258        Eol
259    )
260}