How to Configure the spdlog Thread Pool for Asynchronous Logging
Initialize the global thread pool explicitly using spdlog::init_thread_pool(queue_size, thread_count) before creating any async loggers to customize queue capacity and worker thread count.
spdlog implements asynchronous logging through a shared global thread pool that services a lock-free message queue from one or more dedicated worker threads. By default, the library lazily initializes this pool with a queue size of 8192 items and a single worker thread when the first async logger is constructed. To configure the spdlog thread pool for asynchronous logging with custom parameters, you must explicitly initialize it before instantiating any async loggers.
Understanding spdlog's Global Thread Pool Architecture
The asynchronous backend centers on the spdlog::details::thread_pool class defined in include/spdlog/details/thread_pool.h. This pool manages an mpmc_blocking_queue (multi-producer, multi-consumer) and a fixed set of worker threads that consume log messages and dispatch them to their configured sinks.
Default Lazy Initialization
If you do not call the initialization function manually, spdlog creates the pool automatically upon the first invocation of create_async(). As implemented in include/spdlog/async.h (lines 75-93), this lazy initialization uses details::default_async_q_size (8192) and a single thread. The pool instance is stored in the global registry via details::registry::instance().set_tp() as defined in include/spdlog/details/registry-inl.h (lines 113-119).
Explicit Thread Pool Configuration
For production workloads requiring higher throughput or different latency characteristics, you must configure the spdlog thread pool explicitly. The init_thread_pool() function accepts queue size, thread count, and optional thread start/stop callbacks. This approach instantiates a details::thread_pool directly and registers it globally before any logger creation occurs.
How to Initialize the spdlog Thread Pool
Call spdlog::init_thread_pool() before constructing any asynchronous loggers. The function is overloaded in include/spdlog/async.h to support three levels of configuration.
Basic Configuration
Use the two-parameter overload to set just the queue size and thread count:
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() {
// Configure 16KB queue with 4 worker threads
spdlog::init_thread_pool(16384, 4);
// Now create async loggers - they will use the configured pool
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>(
"async_file", "logs/app.log");
}
Advanced Configuration (with Callbacks)
For scenarios requiring thread affinity setup or custom initialization, use the full signature with callbacks:
spdlog::init_thread_pool(
/*queue_size*/ 32768,
/*thread_count*/ 8,
/*on_thread_start*/ []() {
// Optional: Set thread name or CPU affinity
// pthread_setname_np(pthread_self(), "spdlog_worker");
},
/*on_thread_stop*/ []() {
// Optional: Cleanup resources specific to this thread
}
);
The callbacks are invoked by each worker thread in include/spdlog/details/thread_pool-inl.h within the worker loop (lines 70-81).
Configuring Overflow Behavior
When the message queue fills up, spdlog can either block the producer, discard the oldest entry, or discard the new entry. This behavior is controlled by the async_overflow_policy enum defined in include/spdlog/async_logger.h (lines 21-27):
block(default): Producer threads wait until space is availableoverrun_oldest: Remove the oldest message from the queuediscard_log_msg: Silently drop the new log message
Use create_async_nb() to create a logger with non-blocking discard policy:
// Non-blocking: discards new messages when queue is full
auto nb_logger = spdlog::create_async_nb<spdlog::sinks::stdout_sink_mt>(
"nonblocking_console");
Accessing the Thread Pool at Runtime
Retrieve the currently configured pool using spdlog::thread_pool(), which returns a std::shared_ptr<details::thread_pool> stored in the registry:
auto pool = spdlog::thread_pool();
std::cout << "Queue capacity: " << pool->queue_size() << '\n';
std::cout << "Active threads: " << pool->threads_count() << '\n';
This accessor is defined in include/spdlog/async.h and simply delegates to details::registry::instance().get_tp() as implemented in include/spdlog/details/registry.h (lines 48-55).
Summary
- Global singleton: All async loggers share a single
spdlog::details::thread_poolinstance managed by the global registry - Explicit initialization: Call
spdlog::init_thread_pool(queue_size, thread_count)before creating loggers to override the defaults (8192 items, 1 thread) - Thread callbacks: Use the four-parameter overload to execute custom code when worker threads start or stop
- Overflow policies: Configure via
create_async()(blocking) orcreate_async_nb()(non-blocking discard) to handle full queue scenarios - Runtime inspection: Access the active pool via
spdlog::thread_pool()to query queue size and thread count
Frequently Asked Questions
When should I configure the thread pool explicitly?
You should call spdlog::init_thread_pool() explicitly when your application requires a queue size different from 8192 items, needs more than one worker thread for high-throughput logging, or must set thread affinity and names before any async logger is constructed. Once the first async logger is created, the pool is locked and cannot be reconfigured.
What happens if the queue fills up?
The behavior depends on the overflow policy specified during logger creation. By default, the producer thread blocks until space becomes available. If you use create_async_nb() or specify async_overflow_policy::discard_log_msg, the new message is dropped. With overrun_oldest, the oldest queued message is removed to make room for the new one.
Can I create multiple thread pools?
No. spdlog maintains a single global thread pool stored in the registry (details::registry::instance().set_tp()). All asynchronous loggers share this instance via weak pointers. If you need isolation between different logging components, consider using synchronous loggers or separate processes rather than multiple thread pools.
How do I check the current queue size?
Obtain the thread pool pointer via auto pool = spdlog::thread_pool() and call pool->queue_size() to get the maximum capacity, or monitor the actual usage through your own metrics. The pool does not expose a method to query the current number of queued items directly, though you can check if it is overrun via the overflow policy handlers.
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 →