Macro spdlog::formatter::pattern

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

Build a pattern from a template 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}");
pattern!("custom: {$my_pattern}", {$my_pattern} => MyPattern::default);

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 pattern string and converts it into a zero-cost pattern:

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

let formatter = PatternFormatter::new(pattern!("pattern 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}"));

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

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}"));

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

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}"));

info!(logger: doctest, "Interesting log message");
/* Output */ "[info] Interesting log message"
//            ^^^^^^ <- 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,
        _ctx: &mut PatternContext,
    ) -> spdlog::Result<()> {
        write!(dest, "My own pattern").map_err(spdlog::Error::FormatRecord)
    }
}

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

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

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,
        _ctx: &mut PatternContext,
    ) -> spdlog::Result<()> {
        write!(dest, "{}", self.id).map_err(spdlog::Error::FormatRecord)
    }
}

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

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

Of course, you can have multiple custom patterns:

let pat = pattern!("[{level}] {payload} - {$mypat} {$myotherpat}",
    {$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}",
    {$mypat} => MyPattern::new,
    // Error: name conflicts with another custom pattern
    {$mypat} => MyOtherPattern::new,
);
let pattern = pattern!("[{level}] {payload} - {$day}",
    // Error: name conflicts with a built-in pattern
    {$day} => MyPattern::new,
);

Appendix: A 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.