Memory Usage of the Async Logging Queue in spdlog: Implementation and Optimization
spdlog's asynchronous logging queue stores full copies of formatted log messages in a lock-free MPMC blocking queue, consuming approximately queue_capacity × (average_message_size + overhead) bytes—roughly 8 MiB with default settings (8192 items) and 1 KB messages.
The gabime/spdlog library implements high-performance asynchronous logging through a dedicated thread pool and lock-free queue architecture. Understanding the memory usage of the async logging queue is essential for capacity planning in high-throughput applications. This analysis examines the internal data structures, calculation methods, and configuration options that control memory consumption in the spdlog source code.
Architecture of the Async Logging Queue
Global Thread Pool Initialization
spdlog creates a global thread pool lazily when the first async logger is instantiated. According to the source code in include/spdlog/async.h (lines 27-48), the async_factory_impl checks the registry and constructs a details::thread_pool with details::default_async_q_size (8192 items) and one worker thread by default.
Queue Storage Structure
The thread pool maintains an instance q_ of type details::mpmc_blocking_queue<async_msg>, defined in include/spdlog/details/thread_pool.h (lines 72-76). This lock-free multi-producer-multi-consumer (MPMC) queue serves as the central message buffer between producer threads and the logging worker thread.
Message Payload Storage
Each queue entry is an async_msg object (defined in include/spdlog/details/thread_pool.h, lines 25-68) containing:
- A
log_msg_bufferinstance (which internally holds amemory_buf_t) - An
async_logger_ptr(8 bytes on 64-bit systems) - An
async_msg_typeenum
The log_msg_buffer class (found in include/spdlog/details/log_msg_buffer.h, lines 11-18) creates a full copy of the log message payload. Unlike log_msg which only holds string views referencing stack data, log_msg_buffer allocates internal storage to ensure the message survives the asynchronous hand-off.
Calculating Memory Consumption
The total memory footprint follows this formula:
memory_used ≈ queue_capacity × (average_formatted_message_size + sizeof(async_msg))
With the default capacity of 8192 items:
- A workload generating 200-byte messages consumes approximately 200 × 8192 = ~1.6 MiB plus overhead
- A workload with 1 KB messages temporarily allocates ~8 MiB (8192 × 1 KB) plus overhead
The dominant factor is the formatted message buffer. The async_msg structure adds approximately 24 bytes of overhead per entry on 64-bit systems.
Configuring Queue Memory Limits
Adjust Queue Size
Call spdlog::init_thread_pool(q_size, thread_count) before creating any async loggers to override the default 8192-item capacity. Reducing q_size lowers the maximum memory footprint but increases the risk of blocking or message loss under heavy load.
// Configure 4096 queue items with 2 worker threads
spdlog::init_thread_pool(4096, 2);
auto logger = spdlog::create_async<spdlog::sinks::stdout_sink_mt>("async_logger");
Configure Overflow Policies
The async_overflow_policy determines behavior when the queue fills:
block(default): Producer threads block until space is availableoverrun_oldest: Discard the oldest pending messages to make room
// Non-blocking logger that drops oldest messages when queue is full
auto nb_logger = spdlog::create_async_nb<spdlog::sinks::basic_file_sink_mt>(
"nb_logger", "log.txt");
Optimize Message Size
Since spdlog does not truncate messages automatically, keeping formatted log lines concise reduces per-entry memory consumption proportionally.
Runtime Monitoring
Inspect queue state through the global thread pool instance:
size_t queued = spdlog::thread_pool()->queue_size(); // Current items in queue
size_t overrun = spdlog::thread_pool()->overrun_counter(); // Dropped due to overrun_oldest
size_t discard = spdlog::thread_pool()->discard_counter(); // Ignored due to other reasons
These metrics help identify memory pressure and tuning opportunities in production environments.
Summary
- spdlog's async queue stores full message copies in
async_msgobjects within anmpmc_blocking_queue - Default configuration allocates 8192 queue slots, potentially consuming ~8 MiB with 1 KB messages
- Memory usage scales linearly:
capacity × (message_size + ~24 bytes overhead) - Control footprint via
spdlog::init_thread_pool()to adjust capacity - Choose between blocking (backpressure) and overrun_oldest (message loss) overflow policies
- Monitor runtime metrics using
thread_pool()->queue_size()and counter methods
Frequently Asked Questions
How much memory does spdlog's async queue use by default?
With the default queue size of 8192 items, memory consumption depends on your average log message size. For 1 KB formatted messages, expect approximately 8 MiB of heap allocation, plus roughly 24 bytes of overhead per entry for the async_msg structure.
Can I reduce the memory footprint of async logging?
Yes. Call spdlog::init_thread_pool(q_size, thread_count) with a smaller q_size value before creating async loggers. Alternatively, use shorter log messages, or switch to synchronous logging for low-volume loggers to eliminate queue allocation entirely.
What happens when the async queue fills up?
By default, producer threads block until the worker thread consumes entries. If you create the logger using create_async_nb or specify async_overflow_policy::overrun_oldest, the oldest pending messages are silently discarded to make room for new entries.
Does spdlog reuse memory buffers in the async queue?
No. Each async_msg entry allocates its own memory_buf_t storage to hold the formatted message copy. The queue pre-allocates slots for the async_msg objects themselves, but the message payload buffers are dynamically sized based on each log entry's content.
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 →