# How to Implement Custom Formatter Flags for spdlog Pattern Formatter

> Learn to implement custom spdlog formatter flags by inheriting from spdlog::custom_flag_formatter. Register your flag with pattern_formatter::add_flag() for advanced logging patterns.

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

---

**Create a class inheriting from `spdlog::custom_flag_formatter`, implement the `format()` and `clone()` methods, then register it with `pattern_formatter::add_flag()` to use your custom flag in pattern strings.**

The spdlog logging library provides a flexible pattern formatting system that allows you to customize log output through formatter flags. While it includes built-in flags for timestamps, log levels, and messages, you can extend this system by implementing custom formatter flags for spdlog pattern formatter to add application-specific data or formatting logic.

## Understanding the spdlog Pattern Formatter Architecture

The spdlog formatting pipeline centers on the `pattern_formatter` class located in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h). When you call `set_pattern()`, the formatter parses the pattern string and constructs a vector of `flag_formatter` objects that transform log records into formatted output.

### The flag_formatter Base Class

At the core of the system is `spdlog::details::flag_formatter`, an abstract base class defined in [`pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/pattern_formatter.h). This class receives a log message (`details::log_msg`), a `std::tm` time structure, and a destination buffer (`memory_buf_t`), then writes its formatted output to that buffer.

### The custom_flag_formatter Extension

To create user-defined flags, you inherit from `spdlog::custom_flag_formatter`, which extends `flag_formatter` with a pure-virtual `clone()` method. This clone method is required for deep-copying when a formatter is duplicated, ensuring thread safety and proper object lifecycle management.

### Registration via add_flag

The `pattern_formatter::add_flag<T>(char flag, Args&&... args)` template method stores a `std::unique_ptr<T>` in the internal `custom_handlers_` map. The character key (`flag`) becomes the pattern trigger recognized in format strings like `%x`. When `set_pattern()` parses the pattern, it resolves both built-in and user-registered flags to build the `formatters_` vector.

## Step-by-Step Implementation Guide

### Step 1: Inherit from custom_flag_formatter

Create a class that inherits from `spdlog::custom_flag_formatter` and implement the required methods. You must override `format()` to write your custom output to the destination buffer, and `clone()` to return a new instance of your formatter.

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

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

    void format(const spdlog::details::log_msg&, const std::tm&, 
                spdlog::memory_buf_t& dest) override {
        // Write your custom output to dest
        dest.append("custom", 6);
    }
};

```

### Step 2: Register with pattern_formatter

Instantiate a `pattern_formatter` and call `add_flag()` with your class type and the desired flag character. You can pass constructor arguments as variadic template parameters.

```cpp
auto fmt = std::make_shared<spdlog::pattern_formatter>();
fmt->add_flag<my_custom_flag>('x');  // Registers %x

```

### Step 3: Use in Pattern Strings

After registration, use your flag in the pattern string passed to `set_pattern()`. The custom flag participates in the formatting pipeline alongside built-in flags.

```cpp
fmt->set_pattern("[%n] [%x] %v");
logger->set_formatter(fmt);

```

## Practical Code Examples

### Example 1: Simple Constant String Flag

This implementation creates a flag that inserts a constant string into the log output, useful for adding static metadata or separators.

```cpp
// custom_flag.hpp
#pragma once
#include <spdlog/pattern_formatter.h>

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

    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_;
};

```

```cpp
// main.cpp
#include <spdlog/spdlog.h>
#include "custom_flag.hpp"

int main() {
    auto fmt = std::make_shared<spdlog::pattern_formatter>();
    fmt->add_flag<hello_flag>('h', "👋 Hello")
        .set_pattern("[%n] [%h] %v");

    auto logger = spdlog::stdout_logger_mt("demo");
    logger->set_formatter(fmt);
    logger->info("world");
}

```

**Output:**

```

[demo] [👋 Hello] world

```

### Example 2: 12-Hour Time Format Flag

This custom flag formats the current time in 12-hour notation with AM/PM indicators, extending spdlog's time formatting capabilities.

```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());
    }
};

```

```cpp
auto fmt = std::make_shared<spdlog::pattern_formatter>();
fmt->add_flag<time12_flag>('t')
    .set_pattern("[%n] %t > %v");

```

**Result** (assuming local time is 14:05):

```

[demo] 2:05PM > Hello world

```

### Example 3: Flag with Padding and Truncation Support

The `custom_flag_formatter` base class exposes the `padinfo_` member (type `padding_info`) that automatically populates when users specify width in patterns like `%5x` or `%-10x`. Access this member to honor padding and truncation specifications.

```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(txt_.size() + padinfo_.width_, ' ');
            if (padinfo_.side_ == spdlog::details::padding_info::pad_side::right) {
                padded.replace(0, txt_.size(), txt_);
            } else {
                padded.replace(padded.size() - txt_.size(), txt_.size(), txt_);
            }
            dest.append(padded.data(), padded.data() + padded.size());
        } else {
            dest.append(txt_.data(), txt_.data() + txt_.size());
        }
    }

private:
    std::string txt_;
};

```

```cpp
auto fmt = std::make_shared<spdlog::pattern_formatter>();
fmt->add_flag<padded_flag>('p', "data")
    .set_pattern("[%n] [%5p] %v");

```

**Output:**

```

[demo] [ data] message

```

### Example 4: Exception Handling in Custom Flags

Custom flags can throw exceptions during formatting, which propagate through the `pattern_formatter::format` method. The test suite in [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp) demonstrates this behavior for error handling validation.

```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 with `fmt->add_flag<exploding_flag>('e', "boom")`, any log call triggers a `spdlog::spdlog_ex` exception that propagates to the caller.

## Summary

- **Derive 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 flags
- **Implement `clone()`** to enable deep-copying when formatters are duplicated
- **Implement `format()`** to write output to the `memory_buf_t` destination using log message data and time structures
- **Register with `add_flag<T>()`** to bind a character trigger to your formatter class
- **Access `padinfo_`** to support padding specifiers like `%5x` or `%-10x` in pattern strings
- **Reference [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp)** for comprehensive examples of custom flag behavior, padding, and exception handling

## Frequently Asked Questions

### How do I access the log message content in a custom flag formatter?

The `format()` method receives a `const spdlog::details::log_msg&` parameter containing the log message payload, level, and other metadata. Access `log_msg.payload` to read the actual message text, or check `log_msg.level` to conditionally format based on severity.

### Can I use constructor arguments when registering custom flags?

Yes, the `add_flag<T>()` method accepts variadic template arguments that forward to your formatter's constructor. For example: `fmt->add_flag<my_flag>('x', "arg1", 42)` constructs `my_flag("arg1", 42)` and stores it in the internal handlers map.

### Where does spdlog store the mapping between flag characters and formatter objects?

The `pattern_formatter` class maintains a private `custom_handlers_` member (a map of `char` to `std::unique_ptr<custom_flag_formatter>`) populated by `add_flag()` calls. When `set_pattern()` parses the pattern string, it looks up each `%` sequence in this map to instantiate the appropriate formatter, as implemented in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h).

### Do custom flags support the same padding and truncation syntax as built-in flags?

Yes, when you inherit from `custom_flag_formatter`, you automatically inherit the `padinfo_` member of type `padding_info`. This structure populates when users specify width (e.g., `%5x`), truncation (e.g., `%.3x`), or alignment (e.g., `%-5x` for left-align) in the pattern string, allowing your custom implementation to respect the same formatting specifications as built-in flags.