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
Placeholders | Description | Example |
---|---|---|
{weekday_name} | Abbreviated weekday name | Mon , Tue |
{weekday_name_full} | Weekday name | Monday , Tuesday |
{month_name} | Abbreviated month name | Jan , Feb |
{month_name_full} | Month name | January , February |
{datetime} | Full date time | Thu Aug 23 15:35:46 2014 |
{year_short} | Short year | 22 , 20 |
{year} | Year | 2022 , 2021 |
{date_short} | Short date | 04/01/22 , 12/31/21 |
{date} | Date (ISO 8601) | 2022-04-01 , 2021-12-31 |
{month} | Month | 01 , 12 |
{day} | Day in month | 01 , 12 , 31 , 30 |
{hour} | Hour in 24-hour | 01 , 12 , 23 |
{hour_12} | Hour in 12-hour | 01 , 12 |
{minute} | Minute | 00 , 05 , 59 |
{second} | Second | 00 , 05 , 59 |
{millisecond} | Millisecond | 231 |
{microsecond} | Microseconds within a second | 372152 |
{nanosecond} | Nanoseconds within a second | 482930154 |
{am_pm} | AM / PM | AM , PM |
{time_12} | Time in 12-hour format | 02:55:02 PM |
{time_short} | Short time | 22:28 , 09:53 |
{time} | Time | 22:28:02 , 09:53:41 |
{tz_offset} | Timezone offset | +08:00 , +00:00 , -06:00 |
{unix_timestamp} | Unix timestamp | 1528834770 |
{full} | Full log message | See FullFormatter |
{level} | Log level | critical , error , warn |
{level_short} | Short log level | C , E , W |
{source} | Source file and line | path/to/main.rs:30 1 |
{file_name} | Source file name | main.rs 1 |
{file} | Source file path | path/to/main.rs 1 |
{line} | Source file line | 30 1 |
{column} | Source file column | 20 1 |
{module_path} | Source module path | mod::module 1 |
{logger} | Logger name | my-logger |
{payload} | Log payload | log message |
{pid} | Process ID | 3824 |
{tid} | Thread ID | 3132 |
{eol} | End of line | \n (on non-Windows) or \r\n (on Windows) |
Patterns related to source location require that feature
source-location
is enabled, otherwise the output is empty. ↩