How to Use spdlog async_overflow_policy: Block, Overrun_oldest, and Discard_new Explained

The async_overflow_policy enum in spdlog controls how asynchronous loggers behave when their internal queue fills up, offering three strategies: block (wait for space), overrun_oldest (drop oldest messages), and discard_new (drop incoming messages).

When building high-performance applications with gabime/spdlog, asynchronous logging prevents your threads from waiting for I/O operations. However, when log production exceeds consumption, the async_overflow_policy determines whether your application blocks, drops old data, or discards new messages.

Understanding the async_overflow_policy Enum

The async_overflow_policy enum is defined in include/spdlog/async_logger.h at lines 22-28. This enum controls the behavior of the thread pool when the message queue reaches capacity.

Each asynchronous logger stores its selected policy in the private member async_overflow_policy overflow_policy_ (line 68 of the same file). When posting log messages, the logger passes this policy to the thread pool:

pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
pool_ptr->post_flush(shared_from_this(), overflow_policy_);

The Three Policy Options

spdlog provides three distinct strategies for handling queue overflow:

  • block: The calling thread pauses execution until space becomes available in the queue. This guarantees that all messages are eventually logged but may increase latency in the producer thread. Use this for critical logs where message loss is unacceptable.

  • overrun_oldest: When the queue fills, the oldest message is removed to accommodate the new one. This ensures the most recent information is always preserved, making it ideal for high-throughput scenarios where stale data is less valuable than current entries.

  • discard_new: New messages are silently discarded when the queue is full, preserving the existing backlog. Choose this when you must maintain the historical log sequence and can tolerate losing the latest messages.

Implementation Details in spdlog Source

The actual enforcement of these policies occurs in include/spdlog/details/thread_pool-inl.h at lines 79-87. Here, the thread pool checks queue capacity before inserting new messages and applies the appropriate action based on the policy passed by the logger.

The thread pool implementation (spdlog::details::thread_pool) is shared among all asynchronous loggers, but each logger instance maintains its own async_overflow_policy setting. This allows different loggers in the same application to employ different overflow strategies while utilizing the same underlying thread pool.

Configuring async_overflow_policy in Your Code

You can configure the overflow policy through two primary methods: direct constructor invocation or factory functions.

Direct Logger Construction

When instantiating spdlog::async_logger directly, pass the desired policy as the fourth argument to the constructor:

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

int main() {
    // Create thread pool with queue size 8192
    auto pool = std::make_shared<spdlog::details::thread_pool>(8192, 1);
    auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("app.log", true);
    
    // Block policy (default)
    auto logger_block = std::make_shared<spdlog::async_logger>(
        "block_logger", std::vector<spdlog::sink_ptr>{sink}, pool,
        spdlog::async_overflow_policy::block);
    
    // Overrun_oldest policy
    auto logger_overrun = std::make_shared<spdlog::async_logger>(
        "overrun_logger", std::vector<spdlog::sink_ptr>{sink}, pool,
        spdlog::async_overflow_policy::overrun_oldest);
    
    // Discard_new policy
    auto logger_discard = std::make_shared<spdlog::async_logger>(
        "discard_logger", std::vector<spdlog::sink_ptr>{sink}, pool,
        spdlog::async_overflow_policy::discard_new);
}

Using Factory Aliases

The include/spdlog/async.h header (lines 34-59) provides convenient factory aliases that embed the policy as a template parameter:

// Using async_factory (defaults to block policy)
auto logger_factory = spdlog::async_factory<spdlog::async_overflow_policy::block>::create(
    "factory_logger", sink);

// Using async_factory_nonblock (overrun_oldest)
auto logger_nonblock = spdlog::async_factory_nonblock::create(
    "nonblock_logger", sink);

The async_factory template defaults to async_overflow_policy::block, while async_factory_nonblock is explicitly typedef'd to use async_overflow_policy::overrun_oldest.

Performance Implications and Selection Guide

Choosing the appropriate async_overflow_policy depends on your application's latency requirements and data criticality:

  • Use block when you require guaranteed message delivery and can tolerate occasional latency spikes in the logging thread. This is essential for audit trails or financial transaction logging.

  • Use overrun_oldest when you need high throughput and recent data is more important than historical completeness. This works well for telemetry or monitoring systems where the latest metric values matter most.

  • Use discard_new when you must preserve the initial state of an operation and cannot afford to lose early diagnostic information, even if it means missing subsequent log entries.

Summary

  • The async_overflow_policy enum in include/spdlog/async_logger.h defines three queue-full behaviors: block, overrun_oldest, and discard_new.
  • Each logger maintains its own policy in the overflow_policy_ member, passed to the thread pool during post_log() and post_flush() operations.
  • Policy enforcement occurs in include/spdlog/details/thread_pool-inl.h at lines 79-87.
  • Configure policies via direct constructor arguments or factory aliases (async_factory and async_factory_nonblock) defined in include/spdlog/async.h.

Frequently Asked Questions

What happens when the spdlog async queue is full?

When the queue fills up, spdlog applies the async_overflow_policy specified during logger creation. If set to block, the thread waits until space is available. If set to overrun_oldest, the oldest message is removed to make room. If set to discard_new, the new message is dropped silently.

Can different loggers use different async_overflow_policy settings?

Yes. While asynchronous loggers share a common thread_pool instance, each spdlog::async_logger maintains its own overflow_policy_ member variable. This allows you to create multiple loggers with different policies—for example, a block policy for critical error logs and overrun_oldest for verbose debug logs—using the same thread pool.

Where is the async_overflow_policy enforced in spdlog source code?

The policy is enforced in include/spdlog/details/thread_pool-inl.h at lines 79-87. This implementation checks the queue state before insertion and applies the blocking or discarding logic according to the policy passed from the logger via post_log().

What is the default async_overflow_policy in spdlog?

The default policy is block. When using the async_factory template without specifying a policy, or when constructing an async_logger without the fourth argument, the system defaults to async_overflow_policy::block, ensuring no messages are lost unless explicitly configured otherwise.

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 →