# How to Enable and Manage Backtrace Logging in spdlog: A Complete Guide

> Learn how spdlog enables backtrace logging with a thread-safe buffer to capture recent messages for error analysis. Master managing your logs effectively.

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

---

**spdlog enables backtrace logging through a thread-safe circular buffer that stores the last N debug and trace messages, allowing you to dump recent history when errors occur.**

The spdlog library (GitHub: `gabime/spdlog`) provides a lightweight backtrace feature that helps diagnose issues by capturing debug context leading up to warnings or errors. This article explains how to enable and manage backtrace logging using the actual implementation details from the spdlog source code.

## How spdlog Backtrace Logging Works

The backtrace system consists of three coordinated components that work together to capture, store, and emit recent log history.

### Core Architecture

| Component | Source File | Responsibility |
|-----------|-------------|----------------|
| **`details::backtracer`** | [`include/spdlog/details/backtracer.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/backtracer.h) / [`backtracer-inl.h`](https://github.com/gabime/spdlog/blob/main/backtracer-inl.h) | Maintains a thread-safe circular queue of `log_msg_buffer` objects; handles storage via `push_back` and retrieval via `foreach_pop` |
| **`logger`** | [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) | Owns a `details::backtracer tracer_` member; forwards messages during `log_it_` and exposes public control methods |
| **`registry`** | [`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h) | Manages global backtrace settings across all registered loggers |

When backtrace is enabled, the logger stores formatted messages in a fixed-size circular buffer. Once the buffer reaches capacity, new entries overwrite the oldest ones, ensuring you always retain the most recent activity.

## Enabling Backtrace Logging in spdlog

You can enable backtrace logging either for individual loggers or globally across your application.

### Per-Logger Configuration

To enable backtrace on a specific logger, call `enable_backtrace()` with the desired buffer size:

```cpp
auto logger = spdlog::basic_logger_mt("my_logger", "app.log");
logger->enable_backtrace(10);  // Store last 10 debug/trace messages

```

This invokes `details::backtracer::enable(size_t)` which initializes the circular queue:

```cpp
std::lock_guard<std::mutex> lock{mutex_};
enabled_.store(true, std::memory_order_relaxed);
messages_ = circular_q<log_msg_buffer>{size};

```

### Global Configuration

To enable backtrace on all existing and future loggers, use the global namespace functions defined in [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h):

```cpp
spdlog::enable_backtrace(8);   // Applies to all registered loggers

```

These global functions forward to `spdlog::details::registry`, which iterates through all registered loggers and invokes `enable_backtrace()` on each instance.

## Recording Messages in the Backtrace Buffer

During each log call, `logger::log_it_()` checks `tracer_.enabled()`. If true, the message is passed to `tracer_.push_back(msg)`:

```cpp
std::lock_guard<std::mutex> lock{mutex_};
messages_.push_back(log_msg_buffer{msg});

```

The implementation only stores messages that pass through the logger's formatting pipeline. Because the buffer uses a circular queue structure, once capacity is reached, the oldest `log_msg_buffer` is automatically discarded to make room for new entries. This design ensures constant memory usage regardless of runtime duration.

## Dumping the Backtrace

When an error condition occurs, trigger the backtrace dump using `dump_backtrace()`:

```cpp
logger->dump_backtrace();  // Emits buffered messages to sinks

```

The `logger::dump_backtrace_()` method (defined in [`logger.h`](https://github.com/gabime/spdlog/blob/main/logger.h)) handles the emission sequence:

```cpp
void logger::dump_backtrace_() {
    if (!tracer_.empty()) {
        // Header
        log_msg header{source_loc{}, name_, level::info,
                       "****************** Backtrace Start ******************"};
        sink_it_(header);
        // Emit stored messages in FIFO order
        tracer_.foreach_pop([this](const details::log_msg& msg) { sink_it_(msg); });
        // Footer
        log_msg footer{source_loc{}, name_, level::info,
                       "****************** Backtrace End ********************"};
        sink_it_(footer);
    }
}

```

The `foreach_pop` method empties the queue while iterating, passing each stored message to `sink_it_()` for normal sink processing. This ensures backtrace entries respect your configured formatting patterns and sink destinations.

## Thread Safety Considerations

All backtrace operations in `details::backtracer` acquire `mutex_` through `std::lock_guard`, guaranteeing safe concurrent access from multiple threads. The `enabled()` check in `logger::log_it_` uses `std::memory_order_relaxed` to minimize overhead when backtrace is disabled, ensuring the feature adds negligible cost to hot paths when not in use.

## Practical Code Examples

### Basic Per-Logger Backtrace

This example captures debug history for a specific file logger:

```cpp
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>

int main() {
    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("app.log", true);
    spdlog::logger logger("my_logger", {file_sink});
    logger.set_level(spdlog::level::debug);
    logger.enable_backtrace(5);               // Keep last 5 debug/trace messages

    logger.info("Application start");
    for (int i = 0; i < 10; ++i)
        logger.debug("debug {}", i);          // Only the last 5 are stored

    // Something goes wrong – dump the recent debug history
    logger.dump_backtrace();
}

```

### Global Backtrace Configuration

Use this pattern to capture backtraces across your entire application:

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

int main() {
    spdlog::set_level(spdlog::level::debug);
    spdlog::enable_backtrace(8);   // Every logger now records 8 messages

    auto log = spdlog::default_logger();
    log->info("startup");
    for (int i = 0; i < 20; ++i)
        log->debug("step {}", i);

    // Later, perhaps from a signal handler:
    spdlog::dump_backtrace();      // Prints backtrace for each registered logger
}

```

### Asynchronous Logger Backtrace

Backtrace works seamlessly with spdlog's async loggers:

```cpp
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>

int main() {
    spdlog::init_thread_pool(8192, 1);
    auto async_logger = std::make_shared<spdlog::async_logger>(
        "async", spdlog::sinks::stdout_color_sink_mt::instance(),
        spdlog::thread_pool(), spdlog::async_overflow_policy::block);
    async_logger->enable_backtrace(6);
    async_logger->set_level(spdlog::level::debug);

    async_logger->info("begin async work");
    for (int i = 0; i < 12; ++i)
        async_logger->debug("async debug {}", i);

    async_logger->dump_backtrace();   // Emitted via the async sink
}

```

## Key Implementation Files

Understanding these source files helps when customizing or debugging backtrace behavior:

- **[`include/spdlog/details/backtracer.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/backtracer.h)** – Declares the `backtracer` class with the circular buffer and mutex
- **[`include/spdlog/details/backtracer-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/backtracer-inl.h)** – Contains inline implementations of `enable()`, `disable()`, `push_back()`, and `foreach_pop()`
- **[`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h)** – Defines the `tracer_` member and `dump_backtrace_()` method
- **[`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h)** – Exposes global API functions that forward to the registry
- **[`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h)** – Propagates backtrace settings to all registered loggers

## Summary

- **spdlog backtrace logging** uses a fixed-size circular buffer to store recent debug and trace messages without unbounded memory growth
- Enable per-logger with `logger.enable_backtrace(N)` or globally via `spdlog::enable_backtrace(N)`
- The `details::backtracer` class in [`backtracer.h`](https://github.com/gabime/spdlog/blob/main/backtracer.h) provides thread-safe storage using `mutex_` and `circular_q<log_msg_buffer>`
- Dump stored history anytime using `dump_backtrace()`, which emits messages through standard sinks with header/footer delimiters
- The feature adds minimal overhead when disabled due to relaxed memory ordering checks in the hot path
- Works with both synchronous and asynchronous loggers (`async_logger`)

## Frequently Asked Questions

### What log levels does spdlog backtrace capture?

spdlog backtrace specifically captures **debug** and **trace** level messages. When `logger::log_it_()` processes these levels and backtrace is enabled, it calls `tracer_.push_back()` to store the message. Higher levels (info, warning, error) trigger the log output but are not stored in the backtrace buffer unless they pass through during active backtrace dumping.

### Is spdlog backtrace logging thread-safe?

Yes, all backtrace operations are thread-safe. The `details::backtracer` class protects its internal `circular_q<log_msg_buffer>` with `std::mutex`. Every operation—including `enable()`, `push_back()`, and `foreach_pop()`—acquires `std::lock_guard<std::mutex> lock{mutex_}` before accessing shared state, making it safe to log from multiple threads concurrently while backtrace is active.

### How do I disable backtrace logging after enabling it?

Call `disable_backtrace()` on the logger instance or `spdlog::disable_backtrace()` globally. This sets the internal `enabled_` atomic flag to false via `backtracer::disable()`, preventing further messages from being stored in the circular buffer. Existing buffered messages remain until cleared by `dump_backtrace()` or `clear()`.

### Can I adjust the backtrace buffer size at runtime?

Yes, you can change the buffer size by calling `enable_backtrace()` again with a new size. According to the implementation in [`backtracer-inl.h`](https://github.com/gabime/spdlog/blob/main/backtracer-inl.h), calling `enable(size_t)` reinitializes the `messages_` queue with the new capacity, effectively resizing the buffer. However, this clears any existing stored messages since it creates a new `circular_q<log_msg_buffer>` instance.