# How to Configure the spdlog Thread Pool for Asynchronous Logging

> Configure spdlog thread pool for asynchronous logging. Initialize the global thread pool with custom queue size and worker thread count for efficient log handling. Learn how to optimize your logging setup.

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

---

**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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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:

```cpp
#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:

```cpp
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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async_logger.h) (lines 21-27):

- **`block`** (default): Producer threads wait until space is available
- **`overrun_oldest`**: Remove the oldest message from the queue
- **`discard_log_msg`**: Silently drop the new log message

Use `create_async_nb()` to create a logger with non-blocking discard policy:

```cpp
// 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:

```cpp
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`](https://github.com/gabime/spdlog/blob/main/include/spdlog/async.h) and simply delegates to `details::registry::instance().get_tp()` as implemented in [`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h) (lines 48-55).

## Summary

- **Global singleton**: All async loggers share a single `spdlog::details::thread_pool` instance 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) or `create_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.