spdlog Thread Pool Message Types: Understanding async_msg_type in gabime/spdlog

The spdlog library processes three distinct message types—log, flush, and terminate—through its internal thread pool to enable asynchronous logging, explicit buffer flushing, and graceful worker thread shutdown.

The spdlog library (gabime/spdlog) implements high-performance asynchronous logging via an internal thread pool that decouples log production from I/O operations. Each work item in this system is encapsulated in an async_msg structure tagged with a specific message type defined in include/spdlog/details/thread_pool.h. Understanding these message types is essential for debugging async behavior and extending the library’s functionality.

The Three Message Types Defined

In include/spdlog/details/thread_pool.h (lines 23–28), the async_msg_type enum class defines three distinct operational modes:

  • async_msg_type::log – Represents a standard logging request containing formatted log data
  • async_msg_type::flush – Signals the worker to synchronize all pending log entries for a specific logger
  • async_msg_type::terminate – A control message that triggers graceful shutdown of the worker thread

Log Messages (async_msg_type::log)

The log type is the most frequent message processed by the thread pool. When an async logger generates output, it posts an async_msg with msg_type set to async_msg_type::log. According to the implementation in include/spdlog/details/thread_pool-inl.h, the process_next_msg_() method handles this by invoking the logger's backend sink:

case async_msg_type::log: {
    incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
    return true;  // Keep thread alive
}

Flush Messages (async_msg_type::flush)

When logger->flush() is called on an async logger, the library enqueues a flush message. This triggers backend_flush_() on the target logger's worker, ensuring all buffered content reaches the destination sink before returning control. The implementation returns true to keep the worker thread running:

case async_msg_type::flush: {
    incoming_async_msg.worker_ptr->backend_flush_();
    return true;
}

Terminate Messages (async_msg_type::terminate)

During thread pool destruction, the system posts terminate messages to wake sleeping workers and signal them to exit their processing loops. Unlike log and flush operations, this handler returns false to break the main loop in process_next_msg_():

case async_msg_type::terminate: {
    return false;  // Exit processing loop
}

Message Processing Logic in process_next_msg_()

The dispatch logic resides in include/spdlog/details/thread_pool-inl.h within the process_next_msg_() method (lines 96–116). This method blocks on a queue until a message arrives, then uses a switch statement to route the work to the appropriate handler:

switch (incoming_async_msg.msg_type) {
    case async_msg_type::log: {
        incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
        return true;
    }
    case async_msg_type::flush: {
        incoming_async_msg.worker_ptr->backend_flush_();
        return true;
    }
    case async_msg_type::terminate: {
        return false;
    }
    default: assert(false);
}

Practical Usage Examples

Posting Log Messages

When using an async logger, log messages automatically enter the thread pool as log type messages:

#include <spdlog/spdlog.h>
#include <spdlog/async.h>

int main() {
    auto tp = std::make_shared<spdlog::details::thread_pool>(1024, 2);
    auto logger = std::make_shared<spdlog::async_logger>("my_logger", 
        spdlog::stdout_color_mt("console"), tp, spdlog::async_overflow_policy::block);
    
    // This posts async_msg_type::log to the thread pool
    logger->info("Processing user request");
}

Requesting Explicit Flushes

To ensure critical log entries are written immediately, trigger a flush operation:

logger->critical("System failure imminent");
logger->flush();  // Posts async_msg_type::flush and blocks until complete

Thread Pool Lifecycle Management

The terminate message is handled automatically when the thread pool destructor runs:

{
    spdlog::details::thread_pool tp{1024, 2};
    // Workers active...
}  // Destructor enqueues terminate messages for all workers

Summary

  • Three message types: The spdlog thread pool recognizes log, flush, and terminate messages defined in include/spdlog/details/thread_pool.h.
  • Log operations: Standard logging requests invoke backend_sink_it_() to write formatted data through the sink chain.
  • Flush operations: Explicit synchronization calls trigger backend_flush_() to drain pending buffers.
  • Graceful shutdown: Terminate messages break the worker loop in process_next_msg_(), allowing threads to exit cleanly during pool destruction.

Frequently Asked Questions

What happens if I call flush() on an async logger?

When you invoke flush() on an async logger, the library posts a message with type async_msg_type::flush to the thread pool. The worker executes backend_flush_() to ensure all previously queued log messages are written to their destinations before the flush operation completes.

How does spdlog ensure thread pool workers shut down cleanly?

During thread pool destruction, the destructor enqueues async_msg_type::terminate messages for each worker thread. The process_next_msg_() method returns false when processing a terminate message, breaking the main loop and allowing the thread function to return naturally.

Can I create custom message types for the spdlog thread pool?

The async_msg_type enum is not designed for extension via user-defined types. The switch statement in include/spdlog/details/thread_pool-inl.h explicitly handles only log, flush, and terminate cases, with a default assert(false) for unknown values. Extending functionality typically requires modifying the spdlog source or implementing a custom sink rather than adding message types.

Where is the async_msg structure defined?

The async_msg structure and the async_msg_type enum are declared in include/spdlog/details/thread_pool.h (lines 23–28). This structure encapsulates the message type, log content, and worker pointer necessary for the thread pool to route operations to the correct async logger instance.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →