# How to Use pattern_formatter with Custom Flags in spdlog

> Learn to use spdlog pattern_formatter with custom flags. Inherit from spdlog::custom_flag_formatter and register your new flags for enhanced logging capabilities.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: how-to-guide
- Published: 2026-07-14

---

**To create custom pattern flags in spdlog, inherit from `spdlog::custom_flag_formatter`, implement the `clone()` and `format()` methods, and register your formatter with `pattern_formatter::add_flag()` before setting the pattern string.**

spdlog formats log records through the `pattern_formatter` class located in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h). While built-in flags like `%v` (message) and `%l` (level) cover common use cases, the library allows you to inject custom data by implementing your own flag formatters. This guide shows you how to extend the pattern syntax with user-defined tokens using the exact mechanism implemented in the gabime/spdlog repository.

## Pattern Formatter Architecture

The formatting pipeline relies on three core components that work together to transform log messages into output strings.

### Core Components

- **`spdlog::details::flag_formatter`**: The abstract base class that defines the interface for all pattern flags. It receives a `log_msg` structure, a `std::tm` time object, and a destination buffer.
- **`spdlog::custom_flag_formatter`**: Extends `flag_formatter` and adds a pure-virtual `clone()` method required for deep-copying when a formatter is cloned or shared between loggers.
- **`pattern_formatter`**: The orchestrator that maintains a `formatters_` vector and a `custom_handlers_` map. When you call `set_pattern()`, it parses the pattern string and instantiates the appropriate flag formatters.

### Registration and Parsing Mechanism

Custom flags are registered via the `add_flag<T>(char flag, Args&&… args)` method in [`pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/pattern_formatter.h). This method stores a `std::unique_ptr<T>` in the internal `custom_handlers_` map, where the key is the single character that will be recognized in the pattern string (e.g., `'x'` for `%x`). When `set_pattern()` parses the string, it resolves both built-in and user-registered flags, building a vector of formatter objects that participate in the same pipeline.

## Implementing a Custom Flag Formatter

Creating a working custom flag requires three steps: implementing the class, registering it with the formatter, and using the new token in your pattern string.

### Step 1: Inherit from custom_flag_formatter

Your class must inherit from `spdlog::custom_flag_formatter` and implement two methods:

1. **`clone()`**: Returns a `std::unique_ptr<custom_flag_formatter>` with the same state. This is required because spdlog clones formatters when initializing loggers.
2. **`format(const details::log_msg&, const std::tm&, memory_buf_t&)`**: Writes your custom output into the destination buffer.

```cpp
#include <spdlog/custom_flag_formatter.h>

class hello_flag : public spdlog::custom_flag_formatter {
public:
    explicit hello_flag(std::string txt) : txt_(std::move(txt)) {}

    // Required deep-copy for formatter cloning
    std::unique_ptr<spdlog::custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<hello_flag>(txt_);
    }

    void format(const spdlog::details::log_msg&, const std::tm&, 
                spdlog::memory_buf_t& dest) override {
        dest.append(txt_.data(), txt_.data() + txt_.size());
    }

private:
    std::string txt_;
};

```

### Step 2: Register with pattern_formatter

Instantiate a `pattern_formatter` and call `add_flag<YourClass>('x', ctor_args...)` where `'x'` is your flag character:

```cpp
auto fmt = std::make_shared<spdlog::pattern_formatter>();
fmt->add_flag<hello_flag>('h', "👋 Hello");

```

### Step 3: Apply the Pattern

Use your custom flag in the pattern string passed to `set_pattern()`:

```cpp
fmt->set_pattern("[%n] [%h] %v");

auto logger = spdlog::stdout_logger_mt("demo");
logger->set_formatter(fmt);
logger->info("world");
// Output: [demo] [👋 Hello] world

```

## Practical Code Examples

### Simple Custom Flag (Static Text)

The example above demonstrates a basic flag that appends a constant string. This is useful for injecting static metadata like build versions or environment identifiers into every log line.

### Time Formatting Custom Flag

You can access the `std::tm` parameter to implement custom time formats not covered by built-in flags:

```cpp
class time12_flag : public spdlog::custom_flag_formatter {
public:
    std::unique_ptr<spdlog::custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<time12_flag>();
    }

    void format(const spdlog::details::log_msg&, const std::tm& tm,
                spdlog::memory_buf_t& dest) override {
        auto formatted = spdlog::fmt_lib::format("{:d}:{:02d}{}",
            tm.tm_hour % 12 == 0 ? 12 : tm.tm_hour % 12,
            tm.tm_min,
            tm.tm_hour >= 12 ? "PM" : "AM");
        dest.append(formatted.data(), formatted.data() + formatted.size());
    }
};

```

Usage:

```cpp
auto fmt = std::make_shared<spdlog::pattern_formatter>();
fmt->add_flag<time12_flag>('t')
    .set_pattern("[%n] %t > %v");
// Result: [demo] 2:05PM > Hello world

```

### Supporting Padding and Truncation

The `custom_flag_formatter` base class exposes the `padinfo_` member (type `padding_info`). When users specify width in the pattern (e.g., `%5x` or `%8!x`), spdlog populates this structure automatically. You can honor padding by checking `padinfo_.enabled()`:

```cpp
class padded_flag : public spdlog::custom_flag_formatter {
public:
    explicit padded_flag(std::string txt) : txt_(std::move(txt)) {}

    std::unique_ptr<spdlog::custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<padded_flag>(txt_);
    }

    void format(const spdlog::details::log_msg&, const std::tm&, 
                spdlog::memory_buf_t& dest) override {
        if (padinfo_.enabled()) {
            std::string padded;
            // Apply left/right padding based on padinfo_.side_
            // padinfo_.width_ contains the target width
            // padinfo_.truncate_ indicates if truncation is enabled
            dest.append(padded.data(), padded.data() + padded.size());
        } else {
            dest.append(txt_.data(), txt_.data() + txt_.size());
        }
    }

private:
    std::string txt_;
};

```

Reference implementations in [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp) demonstrate handling `pad_side::left`, `pad_side::right`, and `pad_side::center` alignments.

### Error Handling in Custom Flags

If your custom flag can throw exceptions (e.g., accessing external resources), spdlog will propagate these as `spdlog::spdlog_ex` exceptions:

```cpp
class exploding_flag : public spdlog::custom_flag_formatter {
public:
    explicit exploding_flag(std::string trigger) : trigger_(std::move(trigger)) {}

    std::unique_ptr<spdlog::custom_flag_formatter> clone() const override {
        return spdlog::details::make_unique<exploding_flag>(trigger_);
    }

    void format(const spdlog::details::log_msg&, const std::tm&, 
                spdlog::memory_buf_t&) override {
        if (trigger_ == "boom") {
            throw spdlog::spdlog_ex("exploding_flag triggered");
        }
    }

private:
    std::string trigger_;
};

```

When registered and triggered, the exception bubbles up through `pattern_formatter::format()` to the caller, allowing you to handle initialization errors or runtime failures in your application logic.

## Summary

- **Inherit from `custom_flag_formatter`** in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h) to create new pattern tokens.
- **Implement `clone()`** to enable deep-copying of your formatter when loggers are initialized or duplicated.
- **Register with `add_flag<T>()`** to bind a character (like `'x'`) to your class constructor arguments.
- **Access padding information** through the inherited `padinfo_` member to support width specifiers like `%5x`.
- **Handle exceptions** appropriately, as spdlog propagates `spdlog::spdlog_ex` thrown within custom flags.

## Frequently Asked Questions

### Do I need to implement the clone() method for my custom flag?

Yes. The `clone()` method is mandatory because `spdlog::custom_flag_formatter` requires it for deep-copying when a formatter is cloned or shared between multiple loggers. Return a `std::unique_ptr<custom_flag_formatter>` constructed with your class's copy constructor or state.

### Can custom flags access the log message content?

Yes. The `format()` method receives a `const spdlog::details::log_msg&` parameter that contains the log level, thread ID, source location, and the actual message payload. You can inspect these fields to implement conditional formatting or include message metadata in your output.

### How do I handle padding and alignment in custom pattern flags?

Check `padinfo_.enabled()` to determine if the user specified a width in the pattern string. The `padinfo_` struct (type `spdlog::details::padding_info`) contains `width_`, `side_` (left/right/center), and `truncate_` flags. Apply these to your output string before appending to the destination buffer, or use the base class methods if delegating to standard formatting.

### Where can I find reference implementations and tests?

The spdlog repository contains comprehensive examples in [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp), including sections on **custom flags**, **custom flags-padding**, and **custom flags-exception**. These demonstrate edge cases like exception safety and complex padding scenarios that you can adapt for your own implementations.