# How to Use fmtlib for Debugging Output: A Complete Guide

> Learn how to use fmtlib for debugging output with its debug presentation mode. Automatically quote strings, escape control characters, and render non-printable bytes with !d or !debug.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-06

---

**fmtlib provides a debug presentation mode activated by the `!d` or `!debug` format specifier suffix that automatically quotes strings, escapes control characters like `\n` and `\t`, and renders non-printable bytes as hexadecimal escape sequences.**

The {fmt} library offers built-in mechanisms for producing debug-friendly output without manual string escaping. Whether you are logging file paths with backslashes or inspecting container contents with embedded newlines, fmtlib's debug presentation ensures readable, unambiguous output. This guide demonstrates how to use fmtlib for debugging output based on the actual implementation in the `fmtlib/fmt` repository.

## Understanding fmtlib's Debug Presentation Mode

Internally, fmtlib drives debug output through the **`presentation_type::debug`** flag set on formatters. When activated, this flag triggers specialized write paths that transform raw data into escaped, quoted representations.

The implementation relies on three core components:

| Component | Source Location | Purpose |
|-----------|----------------|---------|
| `maybe_set_debug_format` | `format.h:907‑912` | Forwards debug mode requests to formatters that support `set_debug_format` |
| Debug detection logic | `format.h:2071‑2084` | Checks `specs.type()` for the debug flag and routes to appropriate output paths |
| `write_debug_string` | `ranges.h:410‑424` | Provides generic debug string representation for ranges and containers |

When the debug flag is active, the formatter wraps output in double quotes, escapes backslashes and quotes, and converts control characters to their escaped equivalents (e.g., `\n`, `\t`).

## Activating Debug Output with Format Specifiers

### Using the !d Suffix

Append **`!d`** or **`!debug`** to any format string to enable debug presentation. In [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), the parser recognizes this suffix and invokes `maybe_set_debug_format`, which sets the internal debug flag on the formatter.

```cpp
#include <fmt/core.h>

int main() {
    // Normal output prints the literal newline
    fmt::print("Normal: {}\n", "Hello\nWorld");
    
    // Debug output escapes the newline and adds quotes
    fmt::print("Debug : {!d}\n", "Hello\nWorld");
}

```

Output:

```

Normal: Hello
World
Debug : "Hello\nWorld"

```

### Explicit fmt::debug Wrapper

For clearer intent, use the **`fmt::debug`** helper function. This wrapper explicitly calls `set_debug_format(true)` on the underlying formatter, achieving the same result as the `!d` suffix.

```cpp
#include <fmt/core.h>

int main() {
    fmt::print("Path: {}\n", fmt::debug("C:\\Windows\\System32"));
}

```

This approach improves code readability when passing arguments to logging functions.

## Formatting Containers and Ranges

Debug mode extends to container types via [`include/fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ranges.h). When formatting ranges with `!d`, each element receives debug formatting, and the collection itself is presented unambiguously.

```cpp
#include <vector>
#include <fmt/ranges.h>

int main() {
    std::vector<char> v{'f', 'o', 'o', '\n'};
    
    // Standard formatting concatenates characters
    fmt::print("Normal: {}\n", v);
    
    // Debug formatting escapes the newline
    fmt::print("Debug : {!d}\n", v);
}

```

Output:

```

Normal: foo
Debug : "foo\n"

```

The `write_debug_string` function in `ranges.h:410‑424` handles the escaping logic for range elements, ensuring consistent output across vector, list, and string-view containers.

## Advanced Debugging Techniques

### Debug Output to Memory Buffers

Debug formatting works seamlessly with `fmt::memory_buffer`, allowing you to capture escaped output for later inspection or network transmission without writing directly to streams.

```cpp
#include <fmt/core.h>
#include <fmt/format.h>

int main() {
    fmt::memory_buffer buf;
    fmt::format_to(buf, "Debug buffer: {!d}", "Line1\nLine2");
    std::string result(buf.begin(), buf.end());
    // result now contains: Debug buffer: "Line1\nLine2"
}

```

### Combining Debug with Width and Alignment

You can combine debug presentation with standard format specifiers. Width and alignment apply to the fully escaped, quoted string.

```cpp
#include <fmt/core.h>

int main() {
    // Right-align the debug-formatted string to 20 characters
    fmt::print("{:>20!d}\n", "ABC\nDEF");
}

```

Output:

```

       "ABC\nDEF"

```

The width calculation occurs after escaping, so the quoted string `"ABC\nDEF"` is padded to the specified width.

## Implementation Details in the fmtlib Source Code

Understanding the source architecture helps explain why debug output works consistently across all fmtlib targets. In [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), lines 907‑912 define `maybe_set_debug_format`, a helper template that checks whether a formatter implements `set_debug_format` and forwards the request accordingly.

Lines 2071‑2084 contain the runtime logic that inspects `specs.type()` for `presentation_type::debug`. When detected, the formatter branches to `write_escaped_string` for scalar strings or delegates to range-specific handlers.

For container types, [`include/fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ranges.h) provides `write_debug_string` (lines 410‑424), which iterates through range elements and applies per-element escaping before surrounding the output with quotes. This design ensures that debug mode works with any output destination supported by fmtlib, including `std::string`, `memory_buffer`, `FILE*`, and streams.

## Summary

- **Debug presentation** is activated by the `!d` or `!debug` format specifier suffix, or the `fmt::debug()` wrapper function.
- **Automatic escaping** handles newlines, tabs, backslashes, quotes, and non-printable bytes (`\xhh` hex format).
- **Range support** via [`include/fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ranges.h) applies debug formatting to each container element.
- **Source locations**: `maybe_set_debug_format` resides in `format.h:907‑912`, debug logic in `format.h:2071‑2084`, and range handling in `ranges.h:410‑424`.
- **Compatibility** extends to all fmtlib output targets including memory buffers and file streams.

## Frequently Asked Questions

### How do I print a string with visible escape sequences using fmtlib?

Use the **`!d`** suffix in your format string: `fmt::print("{!d}", my_string)`. This activates the debug presentation mode, which wraps the output in quotes and converts control characters like newlines to `\n` and tabs to `\t`.

### Can I use debug formatting with containers like std::vector?

Yes. Include [`fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/fmt/ranges.h) and apply the debug specifier to the container: `fmt::print("{!d}", my_vector)`. The library formats each element with debug escaping and handles the surrounding braces automatically.

### What is the difference between `!d` and `fmt::debug()`?

Both produce identical output. The **`!d`** suffix is a format-string feature parsed by the library, while **`fmt::debug()`** is a C++ wrapper function that explicitly sets the debug flag on the formatter. Use `fmt::debug()` when you want compile-time type safety and clearer code intent.

### Does debug mode affect performance?

Debug formatting adds minimal overhead compared to standard formatting, primarily the cost of character inspection for escaping. The implementation in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) uses efficient branching at lines 2071‑2084 to select the debug path only when explicitly requested, ensuring zero cost when the feature is unused.