Macro pattern

Source
pattern!() { /* proc-macro */ }
Expand description

Builds a pattern from a template literal string at compile-time.

It accepts inputs in the form:

// This is not exactly a valid declarative macro, just for intuition.
macro_rules! pattern {
    ( $template:literal $(,)? ) => {};
    ( $template:literal, $( {$$custom:ident} => $ctor:expr ),+ $(,)? ) => {};
}

Examples of valid inputs:

pattern!("text");
pattern!("current line: {line}{eol}");
pattern!("custom: {$my_pattern}{eol}", {$my_pattern} => MyPattern::default);

Its first argument accepts only a literal string that is known at compile-time. If you want to build a pattern from a runtime string, use runtime_pattern! macro instead.

§Note

The value returned by this macro is implementation details and users should not access them. If these details are changed in the future, it may not be considered as a breaking change.

§Basic Usage

In its simplest form, pattern receives a literal template string and converts it into a zero-cost pattern:

use spdlog::formatter::{pattern, PatternFormatter};

let formatter = PatternFormatter::new(pattern!("template string"));

§Using Built-in Patterns

A pattern that always outputs a fixed string is boring and useless. Luckily, the pattern template string can contain placeholders that represents built-in patterns. For example, to include the log level and payload in the pattern, we can simply use {level} and {payload} in the pattern template string:

use spdlog::info;

let formatter = PatternFormatter::new(pattern!("[{level}] {payload}{eol}"));

info!(logger: doctest, "Interesting log message");
/* Output */ "[info] Interesting log message\n"

Here, {level} and {payload} are “placeholders” that will be replaced by the output of the corresponding built-in patterns when formatting log records.

What if you want to output a literal { or } character? Simply use {{ and }}:

let formatter = PatternFormatter::new(pattern!("[{{escaped}}] {payload}{eol}"));

info!(logger: doctest, "Interesting log message");
/* Output */ "[{escaped}] Interesting log message\n"

You can find a full list of all built-in patterns and their corresponding placeholders at Appendix below.

§Using Style Range

A specific portion of a formatted log message can be specified as “style range”. Formatted text in the style range will be rendered in a different style by supported sinks. You can use {^...} to mark the style range in the pattern template string:

let formatter = PatternFormatter::new(pattern!("{^[{level}]} {payload}{eol}"));

info!(logger: doctest, "Interesting log message");
/* Output */ "[info] Interesting log message\n"
//            ^^^^^^ <- style range

§Using Your Own Patterns

Yes, you can refer your own implementation of Pattern in the pattern template string! Let’s say you have a struct that implements the Pattern trait. To refer MyPattern in the pattern template string, you need to use the extended syntax to associate MyPattern with a name so that pattern! can resolve it:

use std::fmt::Write;

use spdlog::{
    formatter::{pattern, Pattern, PatternContext, PatternFormatter},
    Record, StringBuf, info
};

#[derive(Default, Clone)]
struct MyPattern;

impl Pattern for MyPattern {
    fn format(&self, record: &Record, dest: &mut StringBuf, _: &mut PatternContext) -> spdlog::Result<()> {
        write!(dest, "My own pattern").map_err(spdlog::Error::FormatRecord)
    }
}

let pat = pattern!("[{level}] {payload} - {$mypat}{eol}",
    {$mypat} => MyPattern::default,
);
let formatter = PatternFormatter::new(pat);

info!(logger: doctest, "Interesting log message");
/* Output */ "[info] Interesting log message - My own pattern\n"

Note the special {$name} => id syntax given to the pattern macro. name is the name of your own pattern; placeholder {$name} in the template string will be replaced by the output of your own pattern. name can only be an identifier. id is a path that identifies a function that can be called with no arguments. Instances of your own pattern will be created by calling this function with no arguments.

§Custom Pattern Creation

Each placeholder results in a new pattern instance. For example, consider a custom pattern that writes a unique ID to the output. If the pattern template string contains multiple placeholders that refer to MyPattern, each placeholder will eventually be replaced by different IDs.

static NEXT_ID: AtomicU32 = AtomicU32::new(0);

#[derive(Clone)]
struct MyPattern {
    id: u32,
}

impl MyPattern {
    fn new() -> Self {
        Self {
            id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
        }
    }
}

impl Pattern for MyPattern {
    fn format(&self, record: &Record, dest: &mut StringBuf, _: &mut PatternContext) -> spdlog::Result<()> {
        write!(dest, "{}", self.id).map_err(spdlog::Error::FormatRecord)
    }
}

let pat = pattern!("[{level}] {payload} - {$mypat} {$mypat} {$mypat}{eol}",
    {$mypat} => MyPattern::new,
);
let formatter = PatternFormatter::new(pat);

info!(logger: doctest, "Interesting log message");
/* Output */ "[info] Interesting log message - 0 1 2\n"

Of course, you can have multiple custom patterns:

let pat = pattern!("[{level}] {payload} - {$mypat} {$myotherpat}{eol}",
    {$mypat} => MyPattern::default,
    {$myotherpat} => MyOtherPattern::default,
);

§Name Conflicts are Hard Errors

It’s a hard error if names of your own custom pattern conflicts with other patterns:

let pattern = pattern!("[{level}] {payload} - {$mypat}{eol}",
    {$mypat} => MyPattern::new,
    // Error: name conflicts with another custom pattern
    {$mypat} => MyOtherPattern::new,
);
let pattern = pattern!("[{level}] {payload} - {$day}{eol}",
    // Error: name conflicts with a built-in pattern
    {$day} => MyPattern::new,
);

§Appendix: Full List of Built-in Patterns

PlaceholdersDescriptionExample
{weekday_name}Abbreviated weekday nameMon, Tue
{weekday_name_full}Weekday nameMonday, Tuesday
{month_name}Abbreviated month nameJan, Feb
{month_name_full}Month nameJanuary, February
{datetime}Full date timeThu Aug 23 15:35:46 2014
{year_short}Short year22, 20
{year}Year2022, 2021
{date_short}Short date04/01/22, 12/31/21
{date}Date (ISO 8601)2022-04-01, 2021-12-31
{month}Month01, 12
{day}Day in month01, 12, 31, 30
{hour}Hour in 24-hour01, 12, 23
{hour_12}Hour in 12-hour01, 12
{minute}Minute00, 05, 59
{second}Second00, 05, 59
{millisecond}Millisecond231
{microsecond}Microseconds within a second372152
{nanosecond}Nanoseconds within a second482930154
{am_pm}AM / PMAM, PM
{time_12}Time in 12-hour format02:55:02 PM
{time_short}Short time22:28, 09:53
{time}Time22:28:02, 09:53:41
{tz_offset}Timezone offset+08:00, +00:00, -06:00
{unix_timestamp}Unix timestamp1528834770
{full}Full log messageSee FullFormatter
{level}Log levelcritical, error, warn
{level_short}Short log levelC, E, W
{source}Source file and linepath/to/main.rs:30 1
{file_name}Source file namemain.rs 1
{file}Source file pathpath/to/main.rs 1
{line}Source file line30 1
{column}Source file column20 1
{module_path}Source module pathmod::module 1
{logger}Logger namemy-logger
{payload}Log payloadlog message
{pid}Process ID3824
{tid}Thread ID3132
{eol}End of line\n (on non-Windows) or \r\n (on Windows)

  1. Patterns related to source location require that feature source-location is enabled, otherwise the output is empty.