# How to Configure spdlog Async Thread Pool Size and Queue

> Learn how to configure spdlog async thread pool size and queue with init_thread_pool before creating async loggers. Optimize your logging performance easily.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: how-to-guide
- Published: 2026-07-12

---

**Configure spdlog's async thread pool by calling `spdlog::init_thread_pool(queue_size, thread_count)` before instantiating any async loggers to set the global queue capacity and number of worker threads.**

The spdlog library routes asynchronous log messages through a global lock-free multi-producer-multi-consumer queue processed by a shared thread pool. Because all async loggers created via `spdlog::create_async` share this single pool, you must configure the **spdlog async thread pool size and queue** capacity before creating your first logger.

## Understanding the Global Thread Pool Architecture

spdlog implements asynchronous logging using a singleton `details::thread_pool` instance stored in the global registry. When you call `spdlog::create_async`, the logger obtains a reference to this pool via `registry::instance().get_tp()` and submits log messages to its lock-free queue.

### Default Configuration Values

If you do not explicitly initialize the pool, spdlog lazily constructs it with default parameters defined in [`include/spdlog/async.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async.h):

- **Queue size**: `8192` items (`details::default_async_q_size`)
- **Thread count**: `1` worker thread

This default configuration works for light loads but requires adjustment for high-throughput applications to prevent queue overflow or thread contention.

### Why Configuration Timing Matters

The pool must be configured **before** any async logger is instantiated. Once created, async loggers cache a reference to the current thread pool. While you can replace the pool by calling `init_thread_pool` again, existing loggers continue using the old pool reference, leading to inconsistent behavior.

## Configuring the Async Thread Pool

Use `spdlog::init_thread_pool` to explicitly define the queue capacity and worker thread count. This function is declared in [`include/spdlog/async.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async.h) (lines 75-93) and implemented in [`src/async.cpp`](https://github.com/gabime/spdlog/blob/main/src/async.cpp).

```cpp
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_sinks.h>

int main()
{
    // Configure queue for 65536 messages with 2 worker threads
    spdlog::init_thread_pool(65536, 2);
    
    auto logger = spdlog::create_async<spdlog::sinks::stdout_sink_mt>("async_logger");
    logger->info("Async logging configured");
}

```

The function signature accepts:
- `q_size`: Maximum number of pending log messages (size_t)
- `thread_count`: Number of background threads processing the queue (size_t)
- Optional `on_thread_start` and `on_thread_stop` callbacks for thread lifecycle management

### Thread Callbacks

You can provide optional callbacks that execute in each worker thread during start and stop operations:

```cpp
spdlog::init_thread_pool(
    32768,  // queue size
    4,      // threads
    []() { std::cout << "Worker thread started\n"; },  // on_thread_start
    []() { std::cout << "Worker thread stopped\n"; }   // on_thread_stop
);

```

These callbacks are useful for setting thread priorities, CPU affinity, or initialization of thread-local resources.

## Creating Async Loggers

After initializing the pool, create async loggers using template functions that automatically reference the global thread pool.

### Blocking vs Non-Blocking Creation

- **`spdlog::create_async`**: Uses the **block-until-space** policy. If the queue is full, the calling thread blocks until space becomes available.
- **`spdlog::create_async_nb`**: Uses the **overrun-oldest** policy. When the queue is full, the oldest message is dropped to make room for the new log entry.

```cpp
// Blocking logger (default behavior)
auto blocking_logger = spdlog::create_async<spdlog::sinks::stdout_sink_mt>("blocking");

// Non-blocking logger (drops oldest entries when full)
auto nb_logger = spdlog::create_async_nb<spdlog::sinks::stdout_sink_mt>("non_blocking");

```

Choose `create_async_nb` for real-time systems where application responsiveness is more critical than complete log retention.

## Querying Pool Status

Access the current thread pool instance using `spdlog::thread_pool()` to diagnose queue utilization:

```cpp
auto tp = spdlog::thread_pool();
std::cout << "Queue capacity: " << tp->queue_size() << '\n';

```

This returns a `std::shared_ptr<spdlog::details::thread_pool>` allowing inspection of the pool configuration defined in [`include/spdlog/details/thread_pool.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool.h).

## Summary

- **Single global pool**: All async loggers share one `details::thread_pool` instance accessed via the registry.
- **Configure early**: Call `spdlog::init_thread_pool(queue_size, thread_count)` before any `create_async` call.
- **Default limits**: Without explicit configuration, the pool starts with 8192 queue slots and 1 thread.
- **Overflow policies**: Use `create_async` for blocking behavior or `create_async_nb` to drop oldest messages when the queue fills.
- **Implementation files**: Configuration logic resides in [`include/spdlog/async.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async.h) and [`src/async.cpp`](https://github.com/gabime/spdlog/blob/main/src/async.cpp), with pool mechanics defined in [`include/spdlog/details/thread_pool.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/thread_pool.h).

## Frequently Asked Questions

### What is the default spdlog async queue size and thread count?

By default, spdlog initializes the async thread pool with a queue size of **8192** entries and **1** worker thread. This is defined by the `details::default_async_q_size` constant in [`include/spdlog/async.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async.h). For production workloads, you should explicitly configure larger values using `spdlog::init_thread_pool`.

### Can I change the thread pool size after creating async loggers?

While `spdlog::init_thread_pool` can replace the global pool at any time, existing async loggers retain references to the original pool instance. Therefore, changing the pool size only affects loggers created **after** the configuration change. Always initialize the pool before creating your loggers to ensure consistent behavior.

### What happens when the spdlog async queue becomes full?

The behavior depends on the logger type. **Blocking loggers** (created with `spdlog::create_async`) will block the calling thread until space is available in the queue. **Non-blocking loggers** (created with `spdlog::create_async_nb`) implement the overrun-oldest policy, discarding the oldest log message to accommodate new entries when the queue reaches capacity.

### How many threads should I configure for spdlog async logging?

The optimal thread count depends on your sink throughput and CPU cores. For most applications, **1-2 threads** suffice since the queue is lock-free and threads primarily serialize to the sink. Multi-threaded sinks (like `stdout_sink_mt`) may benefit from 2-4 threads to handle I/O bottlenecks, but avoid over-subscribing CPU cores to prevent context-switching overhead.