How to Configure spdlog Sinks: A Complete Guide to Console, File, and Async Logging

spdlog sinks are configured by creating std::shared_ptr instances of specific sink types (such as stdout_color_sink_mt or basic_file_sink_mt), setting their individual patterns and log levels, and passing them to an spdlog::logger constructor or using factory helpers like spdlog::stdout_color_mt().

In the gabime/spdlog repository, sinks are the fundamental output drivers that handle where log messages are written. Understanding how to configure spdlog sinks allows you to route logs to multiple destinations simultaneously, each with independent formatting and verbosity settings. This guide covers the exact implementation details found in the spdlog source code, including the specific headers and factory functions you need to build flexible logging pipelines.

Understanding spdlog Sinks and Loggers

A sink in spdlog is any class derived from spdlog::sinks::sink (defined in include/spdlog/sinks/sink.h) that implements the actual write logic. The spdlog::logger class (in include/spdlog/logger.h) holds a collection of sink pointers via std::vector<spdlog::sink_ptr> and forwards formatted log records to each attached sink.

Key architectural points:

  • Sinks are shared resources managed via std::shared_ptr<spdlog::sinks::sink>
  • Multiple loggers can share the same sink instance
  • Each sink maintains its own pattern formatter and log level filter
  • Factory functions in include/spdlog/spdlog.h provide convenient one-liner setup for common configurations

Step-by-Step Sink Configuration

Choose a Sink Type

Available sink implementations are located under include/spdlog/sinks/:

Create and Configure Sink Instances

Instantiate sinks using std::make_shared with the thread-safe _mt (multi-threaded) variants:

auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/app.log", true);

Configure individual sink behavior using set_pattern() and set_level():

console_sink->set_pattern("%^[%T] %n: %v%$");  // Colored output
console_sink->set_level(spdlog::level::info);

file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
file_sink->set_level(spdlog::level::debug);

Assemble the Logger

Create a logger with multiple sinks by passing a vector of spdlog::sink_ptr:

std::vector<spdlog::sink_ptr> sinks {console_sink, file_sink};
auto logger = std::make_shared<spdlog::logger>("multi_sink", 
                                               sinks.begin(), 
                                               sinks.end());
spdlog::register_logger(logger);

Alternatively, use convenience factories for single-sink loggers:

auto logger = spdlog::stdout_color_mt("console_logger");
auto file_logger = spdlog::basic_logger_mt("file_logger", "logs/output.log");

Practical Implementation Examples

Console and File Sinks with Different Log Levels

This example from include/spdlog/sinks/stdout_color_sinks.h and include/spdlog/sinks/basic_file_sink.h demonstrates routing debug messages to file while restricting console output to info and above:

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>

int main() {
    // Create sinks
    auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    auto file = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/app.log", true);

    // Configure individually
    console->set_pattern("%^[%T] %n: %v%$");
    console->set_level(spdlog::level::info);
    
    file->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
    file->set_level(spdlog::level::debug);

    // Assemble multi-sink logger
    std::vector<spdlog::sink_ptr> sinks{console, file};
    auto logger = std::make_shared<spdlog::logger>("app", sinks.begin(), sinks.end());
    spdlog::register_logger(logger);

    // Usage: debug only appears in file
    logger->info("Application started");
    logger->debug("Debug details hidden from console");
}

Rotating File Sink Configuration

The rotating_file_sink (in include/spdlog/sinks/rotating_file_sink.h) rotates files when they reach a specified size:

#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>

int main() {
    // Rotate at 5 MB, keep 3 archived files
    auto rotating = spdlog::rotating_logger_mt("rotating_logger",
                                               "logs/rotating.log",
                                               5 * 1024 * 1024,  // 5 MB
                                               3);
    rotating->info("This creates rotating.log, rotating.1.log, etc.");
}

Async Logger with Multiple Sinks

For high-performance logging, combine sinks with the async thread pool (defined in include/spdlog/async.h):

#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/daily_file_sink.h>

int main() {
    // Initialize thread pool: queue size 8192, 1 worker thread
    spdlog::init_thread_pool(8192, 1);

    // Create sinks
    auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    auto daily = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/daily.log", 0, 0);

    // Build async logger with overflow policy
    std::vector<spdlog::sink_ptr> sinks{console, daily};
    auto async_logger = std::make_shared<spdlog::async_logger>("async",
                                                               sinks.begin(), sinks.end(),
                                                               spdlog::thread_pool(),
                                                               spdlog::async_overflow_policy::block);
    spdlog::register_logger(async_logger);
    
    async_logger->info("Non-blocking async logging initialized");
}

Summary

  • Sink Creation: Instantiate concrete sink classes from include/spdlog/sinks/ using std::make_shared with the _mt suffix for thread safety.
  • Individual Configuration: Each sink supports independent set_pattern() and set_level() calls to control formatting and filtering.
  • Logger Assembly: Pass sink pointers to spdlog::logger constructors or use factory helpers in include/spdlog/spdlog.h for single-sink setups.
  • Async Support: Use spdlog::init_thread_pool() and spdlog::async_logger (from include/spdlog/async.h) when combining multiple sinks with asynchronous logging.
  • Thread Safety: All _mt sink variants are thread-safe, allowing shared use across multiple loggers and threads.

Frequently Asked Questions

How do I register a custom logger with multiple sinks?

After creating your std::shared_ptr<spdlog::logger> with the desired sinks, call spdlog::register_logger(logger) to make it accessible via spdlog::get("logger_name") throughout your application. This is implemented in include/spdlog/spdlog.h and manages the global logger registry.

Can I share the same sink between multiple loggers?

Yes. Since sinks are held by std::shared_ptr<spdlog::sinks::sink>, you can pass the same sink instance to multiple spdlog::logger constructors. This is useful when you want multiple log categories (e.g., "network" and "database") writing to the same file or console output with identical formatting.

What is the difference between basic_file_sink and rotating_file_sink?

basic_file_sink_mt (in include/spdlog/sinks/basic_file_sink.h) provides simple file appending without size limits. rotating_file_sink_mt (in include/spdlog/sinks/rotating_file_sink.h) automatically archives the current log file when it exceeds a specified size, maintaining a fixed number of backups (e.g., app.log, app.1.log, app.2.log).

How do I configure spdlog sinks to use different log levels for different outputs?

Create separate sink instances and call set_level() on each before attaching them to the logger. For example, set console_sink->set_level(spdlog::level::warn) and file_sink->set_level(spdlog::level::debug). The logger will forward messages to both sinks, but each sink filters independently according to its configured level threshold.

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 →