# How to Use Custom Pattern Flags in spdlog: A Complete Guide

> Learn to use custom pattern flags in spdlog formatting by inheriting from spdlog::custom_flag_formatter. This guide shows you how to implement format and clone methods and register your new flag.

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

---

**Create a class inheriting from `spdlog::custom_flag_formatter`, implement the virtual `format()` and `clone()` methods, and register the flag using `pattern_formatter::add_flag()` to use custom pattern flags in spdlog formatting.**

spdlog formats log lines through the `pattern_formatter` class, which expands pattern strings like `"%l %v"` using internal flag formatters. While the library ships with built-in flags for timestamps, log levels, and source locations, you can extend the system with custom pattern flags to inject domain-specific context—such as request IDs, thread names, or custom timestamp formats—directly into your log output. This guide demonstrates how to implement and register these extensions using the actual source code from the `gabime/spdlog` repository.

## Understanding the spdlog Pattern Formatting Architecture

The formatting pipeline in spdlog relies on three key components defined in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h). The **`flag_formatter`** abstract base class defines the interface for all pattern tokens, requiring a `format()` method that receives the log message, a `std::tm` time structure, and a destination buffer. The **`custom_flag_formatter`** class extends this interface by adding a pure-virtual `clone()` method, which enables deep-copying when formatters are duplicated. Finally, the **`pattern_formatter::add_flag<T>()`** template method registers your custom class in an internal `custom_handlers_` map, associating a single character (like `'x'`) with your formatter implementation.

When you call `set_pattern()` on a formatter instance, spdlog parses the pattern string and builds a vector of formatter objects. Custom flags participate in this pipeline identically to built-in flags, supporting the same padding, truncation, and time-zone facilities provided by the library.

## Creating a Custom Pattern Flag

Implementing custom pattern flags in spdlog requires three distinct steps: defining the formatter class, registering it with the pattern formatter, and applying the formatter to a logger.

### Step 1: Inherit from custom_flag_formatter

Create a class that derives from `spdlog::custom_flag_formatter` and implement both the `format()` method and the `clone()` method. The `format()` method writes your custom output into the provided `memory_buf_t` buffer, while `clone()` ensures spdlog can copy your formatter when creating new logger instances.

```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)) {}

    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 the Flag with add_flag()

Instantiate a `pattern_formatter` and use the **`add_flag<T>()`** template method to bind your custom flag class to a specific pattern character. This method stores a `std::unique_ptr<T>` in the formatter's internal registry.

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

```

### Step 3: Set the Pattern and Apply to Logger

Use `set_pattern()` to include your custom flag (referenced by `%` followed by your registered character) in the format string, then assign the formatter to a logger.

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

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

```

This produces the output:

```text
[demo] [👋 Hello] world

```

## Advanced Custom Pattern Flag Examples

The following examples demonstrate practical implementations found in the spdlog test suite at [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp), showing how to handle time formatting, padding, and error conditions.

### Custom Time Formatting (12-Hour Clock)

This flag formats the current time in 12-hour notation with AM/PM markers, accessing the `std::tm` structure passed to the `format()` method.

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

```

Assuming the local time is 14:05, this outputs:

```text
[demo] 2:05PM > Hello world

```

### Handling Padding and Alignment

Custom flags automatically respect padding specifiers like `%8x` or `%=10x` through the protected **`padinfo_`** member inherited from the base class. The `padding_info` structure contains the width, alignment side, and truncation settings.

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

```

When used with a pattern like `[%5p]`, this produces right-padded output such as `[ data]`.

### Error Handling and Exceptions

Custom flags may throw exceptions during formatting. When a flag's `format()` method throws `spdlog::spdlog_ex` (or any exception), the `pattern_formatter::format` method propagates this to the caller. The test suite verifies this behavior in the **custom flags‑exception** section of [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp).

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

```

## Key Implementation Files

According to the `gabime/spdlog` source code, the following files define the custom flag API:

- **[`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h)**: Declares `pattern_formatter`, `custom_flag_formatter`, and the `add_flag()` template method.
- **[`include/spdlog/pattern_formatter-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter-inl.h)**: Contains inline implementations used when `SPDLOG_HEADER_ONLY` is defined.
- **[`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp)**: Provides comprehensive unit tests for custom flags, including padding, truncation, and exception scenarios.

## Summary

- **Inherit from `spdlog::custom_flag_formatter`** to create new pattern flags, implementing both `format()` for output generation and `clone()` for deep-copying.
- **Register flags** using `pattern_formatter::add_flag<T>('x', args...)`, where `'x'` becomes the pattern character accessed via `%x`.
- **Support padding** by checking the `padinfo_` member inherited from the base class, which spdlog populates when patterns like `%5x` or `%-10x` are used.
- **Reference the source** in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h) and [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp) for the complete API specification and test patterns.

## Frequently Asked Questions

### What is the difference between `flag_formatter` and `custom_flag_formatter` in spdlog?

The `flag_formatter` class in [`include/spdlog/pattern_formatter.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter.h) is the abstract base that defines the `format()` interface used by all pattern tokens. The `custom_flag_formatter` class extends this base by adding a pure-virtual `clone()` method, which spdlog requires to deep-copy formatter objects when duplicating logger configurations. You must inherit from `custom_flag_formatter` (not `flag_formatter`) when creating user-defined flags so that the `pattern_formatter` can properly clone your custom handlers.

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

Your custom flag class inherits a protected member called `padinfo_` of type `spdlog::details::padding_info` from the `custom_flag_formatter` base. When users specify width in the pattern (e.g., `%8x` for right-align or `%-8x` for left-align), spdlog automatically parses these specifiers and populates `padinfo_` before calling your `format()` method. You can check `padinfo_.enabled()` to determine if padding is required, then use `padinfo_.width_` and `padinfo_.side_` to format your output accordingly.

### Can custom pattern flags in spdlog throw exceptions?

Yes. If your custom flag's `format()` method throws an exception (such as `spdlog::spdlog_ex`), the `pattern_formatter::format` method will propagate that exception to the caller. This behavior is verified in [`tests/test_pattern_formatter.cpp`](https://github.com/gabime/spdlog/blob/main/tests/test_pattern_formatter.cpp) under the custom flags exception tests. You should handle recoverable errors internally if you want logging to continue, or throw exceptions to signal critical failures that should halt processing.

### How do I register multiple custom flags on the same pattern formatter?

Call `add_flag<T>()` multiple times on the same `pattern_formatter` instance, passing a unique character for each flag. Each invocation stores a `std::unique_ptr` to your formatter in the internal `custom_handlers_` map. For example:

```cpp
auto fmt = std::make_shared<spdlog::pattern_formatter>();
fmt->add_flag<request_id_flag>('r', get_request_id());
fmt->add_flag<session_flag>('s', session_token);
fmt->set_pattern("[%r] [%s] %v");

```

This allows you to combine multiple custom pattern flags in a single log format string.