How to Use enable_backtrace and dump_backtrace in spdlog

spdlog's backtrace facility stores the last N log messages in a circular buffer and replays them through dump_backtrace() to help diagnose the state leading up to an error.

The gabime/spdlog library provides a lightweight, thread-safe mechanism for capturing recent log history without impacting runtime performance. By using enable_backtrace and dump_backtrace in spdlog, you can maintain a sliding window of formatted log messages that can be emitted on demand when critical errors occur, making debugging production issues significantly easier.

How spdlog Backtracing Works

The backtrace system operates as a circular message buffer attached to individual loggers or the global registry. When enabled, every log call pushes a copy of the formatted log_msg into a circular_q<log_msg_buffer> before the message is dispatched to sinks. This capture happens in include/spdlog/details/backtracer.h, where the details::backtracer class manages an std::atomic<bool> enabled_ flag and protects the queue with an std::mutex for thread safety.

The architecture separates global convenience functions from per-logger control:

Enabling Backtraces Globally vs Per-Logger

You can activate backtracing across your entire application or limit it to specific loggers that handle critical subsystems.

Global Registry Configuration

Use the global helpers when you want uniform backtrace behavior across all loggers. According to include/spdlog/spdlog-inl.h, these functions forward to the registry which iterates through all registered loggers:

#include <spdlog/spdlog.h>

// Store the last 32 messages in every logger's circular buffer
spdlog::enable_backtrace(32);

// Later, dump the backtrace using the default logger
spdlog::dump_backtrace();

// Disable and clear all backtrace buffers globally
spdlog::disable_backtrace();

Per-Logger Configuration

For finer control, enable backtracing on individual logger instances. This is defined in include/spdlog/logger-inl.h and isolates the buffer to that specific logger's sinks:

auto logger = spdlog::stdout_color_mt("critical_component");
logger->enable_backtrace(10);  // Keep only the last 10 messages for this component

// When an error is detected in this subsystem
logger->dump_backtrace();
logger->disable_backtrace();

Implementation Details of the Backtracer Class

The underlying mechanism resides in include/spdlog/details/backtracer.h and its implementation in backtracer-inl.h. The backtracer class template uses a fixed-size circular queue that overwrites old messages when full (FIFO order).

Key implementation aspects:

  • Message Storage: The push_back(const log_msg&) method stores a log_msg_buffer (a copy of the formatted message) to ensure the data persists even if the original log context changes.
  • Thread Safety: An std::mutex guards the circular queue during push operations and when iterating via foreach_pop.
  • Dumping: The foreach_pop method iterates over stored messages in order, invoking a callback that writes them to the logger's sinks. This is called internally by logger::dump_backtrace().

Practical Code Examples

Basic Global Backtrace Workflow

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

int main() {
    auto file_logger = spdlog::basic_logger_mt("file_logger", "app.log");
    
    // Enable global backtrace of last 10 messages
    spdlog::enable_backtrace(10);
    
    file_logger->info("Application started");
    file_logger->debug("Config loaded: {}", 42);
    file_logger->warn("Resource threshold at 80%");
    
    // Simulate error detection
    try {
        throw std::runtime_error("Critical failure");
    } catch (...) {
        // Dump the last 10 messages to see what led to the error
        spdlog::dump_backtrace();  // Writes to default logger's sinks
    }
    
    spdlog::disable_backtrace();
}

Isolated Component Backtrace

auto network_logger = spdlog::stdout_color_mt("network");
network_logger->enable_backtrace(5);

// Simulated network operations
network_logger->info("Connecting to host...");
network_logger->debug("Sent packet: {}", 1024);
network_logger->error("Connection timeout");

// Dump only this logger's history to diagnose the network issue
network_logger->dump_backtrace();  // Prints the 5 most recent lines
network_logger->disable_backtrace();

Summary

  • Global vs Local: Use spdlog::enable_backtrace() in spdlog.h to capture across all loggers, or logger->enable_backtrace() for isolated component tracking.
  • Dumping: Call dump_backtrace() on the default logger via spdlog::dump_backtrace() or on specific logger instances to replay buffered messages to their sinks.
  • Implementation: The details::backtracer class in backtracer.h uses a thread-safe circular_q<log_msg_buffer> managed by std::mutex to store formatted message copies.
  • Cleanup: Invoke disable_backtrace() to atomically disable tracing and clear the circular buffer without dumping contents.

Frequently Asked Questions

What is the performance impact of enabling backtraces in spdlog?

The overhead is minimal but non-zero. Each log call performs an atomic check on enabled_, locks a mutex, and pushes a copy of the formatted message into the circular queue. For high-frequency logging (millions of messages per second), consider using per-logger backtraces only on critical subsystems rather than the global registry.

How do I clear the backtrace buffer without dumping the messages?

Call disable_backtrace() on the logger or the global registry. In include/spdlog/details/backtracer-inl.h, the disable() method clears the circular_q and sets the atomic flag to false, effectively dropping all stored messages without writing them to sinks.

Is the spdlog backtrace buffer thread-safe?

Yes. The details::backtracer class uses an std::atomic<bool> for the enabled state and an std::mutex to protect the circular_q<log_msg_buffer> during concurrent push and pop operations. Multiple threads can log simultaneously while the backtrace is enabled.

What happens when the backtrace buffer reaches its capacity?

The circular queue overwrites the oldest messages in FIFO order. When push_back() is called on a full buffer, the implementation in backtracer-inl.h replaces the oldest entry, maintaining exactly the last N messages specified in the enable_backtrace(size_t n_messages) call.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →