# How to Configure Multiple Sinks in spdlog: A Complete Guide

> Learn to configure multiple sinks in spdlog with this complete guide. Easily set up stdout and file logging simultaneously for comprehensive application insights.

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

---

**To configure multiple sinks in spdlog, instantiate concrete sink objects such as `stdout_color_sink_mt` and `basic_file_sink_mt`, aggregate them using either a `std::vector<sink_ptr>` passed to the `spdlog::logger` constructor or a `dist_sink_mt` wrapper, and register the resulting logger with `spdlog::register_logger`.**

The spdlog library provides a high-performance, header-only logging solution for C++ applications. Its flexible sink architecture allows a single logger to dispatch messages to multiple destinations—such as the console, files, or external systems—simultaneously with independent formatting and severity filtering.

## Understanding the Multi-Sink Architecture

### The Core Logger Implementation

In [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h), the `spdlog::logger` class maintains a `std::vector<std::shared_ptr<spdlog::sinks::sink>>` member that stores all attached sinks. When you invoke a logging method like `logger->info()`, the implementation iterates over this vector and forwards the formatted message to each sink's `sink_it_()` method.

### The Distribution Sink Pattern

For scenarios requiring dynamic sink management, spdlog provides `spdlog::sinks::dist_sink` defined in [`include/spdlog/sinks/dist_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/dist_sink.h). This composite sink owns a collection of child sinks and forwards each log request to all children, preserving their individual `set_pattern()` and `set_level()` configurations.

## Creating Concrete Sink Instances

Each sink derives from the abstract base class in [`include/spdlog/sinks/sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/sink.h) and implements the `sink_it_()` and `flush_()` virtual methods.

Console sink example from [`include/spdlog/sinks/stdout_color_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/stdout_color_sinks.h):

```cpp
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::info);
console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");

```

File sink example from [`include/spdlog/sinks/basic_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/basic_file_sink.h):

```cpp
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/app.log", true);
file_sink->set_level(spdlog::level::debug);
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");

```

## Aggregating Sinks Using dist_sink

The `dist_sink` approach simplifies logger construction and enables runtime modification. Instead of passing a vector directly to the logger constructor, you wrap sinks in a distribution sink:

```cpp
auto dist_sink = std::make_shared<spdlog::sinks::dist_sink_mt>();
dist_sink->add_sink(console_sink);
dist_sink->add_sink(file_sink);

auto logger = std::make_shared<spdlog::logger>("multi_sink", dist_sink);
spdlog::register_logger(logger);

```

Alternatively, pass a vector directly to avoid the wrapper overhead:

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

```

## Complete Implementation Example

The following example demonstrates configuring both console and file output with distinct formatting and log levels, as implemented in the spdlog source:

```cpp
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/dist_sink.h>

void setup_multi_sink_logger() {
    // Create console sink with color support
    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console_sink->set_level(spdlog::level::info);
    console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
    
    // Create file sink with append mode
    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/app.log", true);
    file_sink->set_level(spdlog::level::debug);
    file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
    
    // Combine using dist_sink for runtime flexibility
    auto dist_sink = std::make_shared<spdlog::sinks::dist_sink_mt>();
    dist_sink->add_sink(console_sink);
    dist_sink->add_sink(file_sink);
    
    // Initialize logger with global debug level
    auto logger = std::make_shared<spdlog::logger>("multi_sink", dist_sink);
    logger->set_level(spdlog::level::debug);
    spdlog::register_logger(logger);
    
    // Usage: info goes to both, debug only to file
    logger->info("Application startup complete");
    logger->debug("Debug details visible only in file");
}

```

## Advanced Configuration Patterns

### Per-Sink Log Level Filtering

When you configure multiple sinks in spdlog, each sink maintains its own severity threshold via `set_level()`. Messages below a sink's threshold are silently discarded for that destination, allowing fine-grained control—such as verbose debug logs in files while restricting console output to warnings and errors.

### Runtime Sink Management

Using `dist_sink` enables dynamic modification of the sink collection after logger creation. Call `dist_sink->add_sink(new_sink)` or `dist_sink->remove_sink(existing_sink)` to alter destinations without reconstructing the logger object, as the underlying vector in [`include/spdlog/sinks/dist_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/dist_sink.h) supports thread-safe modifications when using the `_mt` variants.

## Summary

- **Vector-based storage**: The `spdlog::logger` class in [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) stores sinks in a `std::vector<std::shared_ptr<sink>>`, dispatching each log message to every attached sink.
- **Concrete implementations**: Sinks like `stdout_color_sink_mt` and `basic_file_sink_mt` derive from the base class in [`include/spdlog/sinks/sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/sink.h) and implement `sink_it_()` for specific destinations.
- **Aggregation strategies**: Use `spdlog::sinks::dist_sink` from [`include/spdlog/sinks/dist_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/dist_sink.h) for convenient runtime management, or pass a sink vector directly to the logger constructor for minimal overhead.
- **Independent configuration**: Each sink supports individual pattern formatting and log level filtering, allowing console and file outputs to display different information densities.

## Frequently Asked Questions

### Can I add or remove sinks after creating the logger?

Yes. If you use `spdlog::sinks::dist_sink` as the logger's single sink, you can call `add_sink()` or `remove_sink()` on the distribution sink at any time to modify the destination collection without recreating the logger object. This is not possible when passing a raw vector to the logger constructor, as that vector is copied during initialization.

### How do I set different log levels for different sinks?

Each concrete sink exposes a `set_level()` method. When configuring multiple sinks, call this method on each sink individually before adding them to the logger. For example, set the console sink to `spdlog::level::warn` and the file sink to `spdlog::level::debug` to suppress debug messages in the terminal while preserving them in the log file.

### What is the difference between dist_sink and passing a vector directly?

The `dist_sink` approach wraps your sinks in a composite object that implements the sink interface itself, allowing runtime modifications and treating the collection as a single entity. Passing a `std::vector<sink_ptr>` directly to the `spdlog::logger` constructor stores the sinks internally without the extra indirection layer, offering marginally better performance but static configuration after construction.

### Which header files do I need to include for multi-sink logging?

Include [`spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/spdlog/spdlog.h) for the core API, plus specific sink headers such as [`spdlog/sinks/stdout_color_sinks.h`](https://github.com/gabime/spdlog/blob/main/spdlog/sinks/stdout_color_sinks.h) for console output and [`spdlog/sinks/basic_file_sink.h`](https://github.com/gabime/spdlog/blob/main/spdlog/sinks/basic_file_sink.h) for file logging. If using the distribution pattern, include [`spdlog/sinks/dist_sink.h`](https://github.com/gabime/spdlog/blob/main/spdlog/sinks/dist_sink.h). The base sink interface resides in [`spdlog/sinks/sink.h`](https://github.com/gabime/spdlog/blob/main/spdlog/sinks/sink.h).