# How spdlog Integrates with the fmt Library for Formatting Arguments

> Discover how spdlog integrates with the fmt library for efficient, zero-copy argument formatting. Learn about its internal bundling and use of fmt::vformat_to for streamlined log messages.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: internals
- Published: 2026-08-06

---

**spdlog delegates all string formatting to the fmt library by bundling it inside `spdlog/fmt/` and using `fmt::vformat_to` for zero-copy log message construction.**

The gabime/spdlog repository builds its entire logging API on top of the **fmt** library (formerly cppformat). This integration allows spdlog to provide type-safe, high-performance formatting without reimplementing string formatting logic. Whether you're logging simple values or complex custom patterns, fmt handles the heavy lifting under the hood.

## Bundled fmt Header Structure

spdlog includes a **complete bundled copy of fmt** in the `include/spdlog/fmt/` directory. This vendored approach ensures consistent behavior across platforms and eliminates external dependencies.

The primary re-export point is [`spdlog/fmt/fmt.h`](https://github.com/gabime/spdlog/blob/main/spdlog/fmt/fmt.h). This header simply includes the corresponding fmt header and brings its symbols into the spdlog namespace:

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

// Use fmt directly through spdlog's bundled version
auto msg = spdlog::fmt::format("Hex: {:#x}", 255);  // equivalent to fmt::format

```

Users can write `spdlog::fmt::format(...)` or rely on the default `fmt::format` calls used internally throughout the library. This design keeps the public API clean while maintaining full access to fmt's capabilities.

## Core Log Message Formatting: logger.h

When you call any logging function, spdlog captures variadic arguments using **fmt's format-argument list** and forwards them for direct buffer writing.

In [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) (around line 336), the `logger` class implements the formatting pipeline:

```cpp
// Example usage
auto logger = spdlog::stdout_color_mt("console");
int value = 42;
logger->info("The answer is {}", value);  // fmt::vformat_to invoked internally

```

The internal flow works as follows:

- **Argument capture** – Variadic arguments are wrapped with `fmt::make_format_args`
- **Zero-copy formatting** – `fmt::vformat_to` writes directly into `spdlog::memory_buf_t`
- **Avoided allocations** – No temporary `std::string` objects are created during formatting

This approach maximizes performance by eliminating intermediate string copies, which matters significantly for high-throughput logging scenarios.

## Pattern Formatting with fmt

The **pattern_formatter**—responsible for timestamps, thread IDs, log levels, and other metadata—also relies on fmt internally. Found in [`include/spdlog/pattern_formatter-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter-inl.h), this component:

1. Parses the user-provided pattern string (e.g., `"[%Y-%m-%d %H:%M:%S.%e] [%l] %v"`)
2. Builds an internal fmt format string representing the pattern structure
3. Calls `fmt::format_to` to embed pattern-expanded elements into the final log line

```cpp
// Custom pattern using fmt internally
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
spdlog::info("Formatted with custom pattern");

```

The pattern system leverages fmt's formatting specifiers for date/time components while maintaining spdlog's specialized syntax for log-specific fields like `%l` (level) and `%v` (message).

## Compile-Time Format String Safety

spdlog inherits fmt's **compile-time format string checking** through the `FMT_STRING` macro. This feature catches mismatched format specifiers at compile time rather than runtime.

The implementation resides in [`include/spdlog/fmt/bundled/compile.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/fmt/bundled/compile.h) (around line 35). When enabled, code like this produces a compile error:

```cpp
// Compile-time error: format specifier mismatch
logger->info(FMT_STRING("Value: {}"), "string", 42);  // too many arguments
logger->info(FMT_STRING("Value: {0:d}"), "not a number");  // type mismatch

```

This safety guarantee extends automatically to all spdlog users without requiring explicit opt-in for basic cases.

## Key Integration Files

| File | Role |
|------|------|
| [`include/spdlog/fmt/fmt.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/fmt/fmt.h) | Re-exports bundled fmt API for public use |
| [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) | Implements logger class with `fmt::vformat_to` usage |
| [`include/spdlog/pattern_formatter-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/pattern_formatter-inl.h) | Pattern parsing and fmt-based final formatting |
| [`include/spdlog/fmt/bundled/compile.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/fmt/bundled/compile.h) | Compile-time format string checking via `FMT_STRING` |
| [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h) | Public entry point that pulls in fmt headers |

## Summary

- spdlog **bundles fmt directly** in `spdlog/fmt/` to eliminate external dependencies
- All log formatting flows through **fmt::vformat_to** for zero-copy buffer writing
- The **pattern_formatter** uses fmt internally for timestamp and metadata expansion
- **Compile-time safety** comes via fmt's `FMT_STRING` macro with automatic type checking
- Users can access fmt directly through `spdlog::fmt::format()` when needed

## Frequently Asked Questions

### Can I use my system-installed fmt instead of spdlog's bundled version?

Yes. spdlog supports both bundled and external fmt through CMake options. Set `SPDLOG_FMT_EXTERNAL=ON` during configuration to link against a system-installed fmt library rather than the vendored copy.

### Does spdlog support fmt's compile-time format strings by default?

Basic compile-time checking works automatically with `FMT_STRING`. For full compile-time format string optimization (where the format is parsed at compile time), you need to include the appropriate headers and use `FMT_COMPILE` macros from [`spdlog/fmt/compile.h`](https://github.com/gabime/spdlog/blob/main/spdlog/fmt/compile.h).

### What fmt version does spdlog bundle?

The bundled fmt version varies by spdlog release. Check [`include/spdlog/fmt/bundled/core.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/fmt/bundled/core.h) for the `FMT_VERSION` macro to determine the exact version included in your spdlog installation.

### Is there any performance overhead from spdlog's fmt integration?

No—spdlog's fmt integration adds no overhead compared to using fmt directly. The `fmt::vformat_to` path used in [`logger.h`](https://github.com/gabime/spdlog/blob/main/logger.h) is the same high-performance approach recommended for direct fmt usage, with pre-allocated buffers avoiding all dynamic allocations during normal operation.