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
blockwhen 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_oldestwhen 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_newwhen 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_policyenum ininclude/spdlog/async_logger.hdefines three queue-full behaviors:block,overrun_oldest, anddiscard_new. - Each logger maintains its own policy in the
overflow_policy_member, passed to the thread pool duringpost_log()andpost_flush()operations. - Policy enforcement occurs in
include/spdlog/details/thread_pool-inl.hat lines 79-87. - Configure policies via direct constructor arguments or factory aliases (
async_factoryandasync_factory_nonblock) defined ininclude/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →