# spdlog Sinks: A Complete Guide to Console, File, Network, and Custom Logging Targets

> Explore spdlog sinks for console, file, network, and custom logging. This guide details how spdlog's output handlers direct your log messages effectively.

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

---

**spdlog sinks are destination-specific output handlers that route formatted log messages to consoles, files, network endpoints, or custom callbacks, with every sink available in both thread-safe (`*_mt`) and single-threaded (`*_st`) variants.**

All logging output in the `gabime/spdlog` library flows through **sinks**—objects that encapsulate the logic for writing formatted records to concrete destinations. The library ships with more than twenty built-in sink types, each residing in its own header under `include/spdlog/sinks/`, allowing developers to log to stdout, rotating files, syslog, Kafka, or even in-memory ring buffers.

## The Sink Architecture

Every spdlog sink inherits from the type-erased base class defined in [`include/spdlog/sinks/sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/sink.h), while the templated `base_sink<Mutex>` class in [`include/spdlog/sinks/base_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/base_sink.h) provides the actual implementation. This template accepts a mutex type that determines thread-safety:

- **`std::mutex`** produces `*_mt` (multi-threaded) sinks that synchronize concurrent access
- **`spdlog::details::null_mutex_t`** produces `*_st` (single-threaded) sinks that skip locking for maximum performance in single-threaded contexts

To implement a custom sink, you derive from `base_sink<Mutex>` and override two pure virtual functions: `sink_it_(const spdlog::details::log_msg&)` to handle the write operation, and `flush_()` to ensure buffered data reaches the destination.

## Console Sinks

Console sinks write log output to `stdout` or `stderr`, with optional color support for ANSI terminals or Windows consoles.

### stdout_sink

The **basic console sink** (`stdout_sink` / `stderr_sink`) defined in [`include/spdlog/sinks/stdout_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/stdout_sinks.h) performs unadorned text output without color codes. Use `stdout_sink_mt` or `stdout_sink_st` depending on your thread-safety requirements.

### stdout_color_sink

For **ANSI-colored console output** on Linux and macOS, include [`include/spdlog/sinks/stdout_color_sinks.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/stdout_color_sinks.h) and instantiate `stdout_color_sink_mt`. This sink automatically wraps log levels in color escape codes (e.g., red for errors) before writing to the terminal.

### wincolor_sink

Windows applications should use **wincolor_sink** from [`include/spdlog/sinks/wincolor_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/wincolor_sink.h), which utilizes the WinAPI `SetConsoleTextAttribute` function to apply colors in Command Prompt or PowerShell windows without emitting ANSI escape sequences that Windows does not interpret natively.

```cpp
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"

int main() {
    auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
    
    spdlog::logger logger("console", {console});
    logger.info("Colored console output");
}

```

## File Sinks

File sinks persist logs to the filesystem, with strategies ranging from simple append-only files to time-based and size-based rotation policies.

### basic_file_sink

The **simple file sink** in [`include/spdlog/sinks/basic_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/basic_file_sink.h) opens a file once and appends indefinitely. The constructor accepts a filename and a boolean `truncate` flag; when `true`, the sink clears existing file contents on creation.

### rotating_file_sink

To prevent unbounded disk usage, **rotating_file_sink** (in [`include/spdlog/sinks/rotating_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/rotating_file_sink.h)) enforces a maximum file size and maintains a fixed number of backups. When the active file reaches the size limit, the sink rotates files (e.g., `app.log.1`, `app.log.2`) and opens a new primary log file.

### daily_file_sink and hourly_file_sink

Time-based rotation creates new log files at fixed intervals. **daily_file_sink** (in [`include/spdlog/sinks/daily_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/daily_file_sink.h)) rolls over at UTC midnight (or a custom hour), while **hourly_file_sink** (in [`include/spdlog/sinks/hourly_file_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/hourly_file_sink.h)) creates a new file every 60 minutes. Both accept a `tm` time calculator and file name pattern.

```cpp
#include "spdlog/sinks/rotating_file_sink.h"

auto rotating = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
    "logs/app.log", 
    10 * 1024 * 1024,  // 10 MB max size
    5);                // Keep 5 backup files

```

## Network and System Sinks

These sinks forward logs to remote collectors, operating system facilities, or mobile platform logging frameworks.

### udp_sink and tcp_sink

The **UDP sink** ([`include/spdlog/sinks/udp_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/udp_sink.h)) transmits log messages as UDP packets to a specified host and port, suitable for high-throughput scenarios where occasional packet loss is acceptable. For reliable delivery, the **TCP sink** ([`include/spdlog/sinks/tcp_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/tcp_sink.h)) establishes a TCP connection and blocks until the data is acknowledged by the remote server.

### syslog_sink

On Linux and Unix systems, **syslog_sink** (in [`include/spdlog/sinks/syslog_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/syslog_sink.h)) writes to the system logger using `syslog()` calls, respecting facility codes and options like `LOG_PID` or `LOG_CONS`.

### systemd_sink

For systems running systemd, **systemd_sink** and **systemd_namespace_sink** (in [`include/spdlog/sinks/systemd_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/systemd_sink.h) and [`systemd_namespace_sink.h`](https://github.com/gabime/spdlog/blob/main/systemd_namespace_sink.h)) call `sd_journal_print` or `sd_journal_send` to write structured logs directly to the journal, preserving metadata fields and supporting custom namespaces.

### Platform-Specific Sinks

- **android_sink** ([`include/spdlog/sinks/android_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/android_sink.h)): Routes messages to Android's `logcat` via `__android_log_print`.
- **win_eventlog_sink** ([`include/spdlog/sinks/win_eventlog_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/win_eventlog_sink.h)): Registers events in the Windows Event Log using the WinAPI `ReportEvent` function.

```cpp
#include "spdlog/sinks/udp_sink.h"

auto udp = std::make_shared<spdlog::sinks::udp_sink_mt>("127.0.0.1", 5140);
spdlog::logger logger("network", {udp});
logger.error("Transmitted via UDP");

```

## Third-Party Integration Sinks

spdlog includes experimental sinks for modern observability stacks and databases.

- **kafka_sink** ([`include/spdlog/sinks/kafka_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/kafka_sink.h)): Publishes log records to an Apache Kafka topic using the `librdkafka` client.
- **loki_sink** ([`include/spdlog/sinks/loki_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/loki_sink.h)): Sends logs to Grafana Loki via HTTP POST requests, formatting entries as JSON streams compatible with Loki's push API.
- **mongo_sink** ([`include/spdlog/sinks/mongo_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/mongo_sink.h)): Persists log messages as BSON documents in a MongoDB collection using the MongoDB C++ driver.

## Utility and Specialized Sinks

These sinks provide debugging aids, filtering capabilities, and custom processing hooks.

### null_sink

The **null_sink** ([`include/spdlog/sinks/null_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/null_sink.h)) discards all log messages, functioning as a `/dev/null` equivalent. This is useful for conditionally disabling logging in performance-critical paths without removing logger calls from the source code.

### ostream_sink

**ostream_sink** ([`include/spdlog/sinks/ostream_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/ostream_sink.h)) writes to any `std::ostream` object, such as `std::ostringstream` for in-memory string capture or `std::ofstream` for file handles managed externally.

### ringbuffer_sink

The **ringbuffer_sink** ([`include/spdlog/sinks/ringbuffer_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/ringbuffer_sink.h)) maintains a circular buffer of the last *N* log records in memory. This pattern supports "diagnostic dumps" where an application can retrieve recent log history on demand (e.g., after a crash or error condition) without writing to disk during normal operation.

### callback_sink

**callback_sink** ([`include/spdlog/sinks/callback_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/callback_sink.h)) invokes a user-provided callable for every log record. This enables integration with proprietary logging backends, custom network protocols, or Qt signals.

```cpp
#include "spdlog/sinks/callback_sink.h"

auto cb = std::make_shared<spdlog::sinks::callback_sink_mt>(
    [](const spdlog::details::log_msg& msg) {
        std::string txt = fmt::to_string(msg.payload);
        // Forward to custom queue or analytics pipeline
    });

```

### dup_filter_sink

To reduce noise, **dup_filter_sink** ([`include/spdlog/sinks/dup_filter_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/dup_filter_sink.h)) suppresses duplicate messages that repeat within a configurable time window, passing through only the first occurrence and a summary count of skipped duplicates.

### dist_sink

The **distribution sink** ([`include/spdlog/sinks/dist_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/dist_sink.h)) implements multicasting by maintaining a vector of child sinks. When `log()` is called, the message propagates to every registered child, enabling simultaneous output to console, file, and network targets without multiple logger instances.

## Creating Multi-Sink Loggers

A single `spdlog::logger` can aggregate multiple sinks via a `std::vector<std::shared_ptr<spdlog::sinks::sink>>`. Each sink maintains its own log level and pattern, allowing fine-grained control over which messages reach specific destinations.

```cpp
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/syslog_sink.h"

auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file    = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/combined.log");
auto syslog  = std::make_shared<spdlog::sinks::syslog_sink_mt>("myapp", 0, LOG_USER, true);

spdlog::logger logger("multi", {console, file, syslog});
logger.warn("Appears in console, file, and syslog simultaneously");

```

## Summary

- **spdlog sinks** inherit from `base_sink<Mutex>` in [`include/spdlog/sinks/base_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/base_sink.h), with `*_mt` variants using `std::mutex` for thread safety and `*_st` variants using `null_mutex_t` for lock-free performance.
- **Console sinks** include `stdout_color_sink_mt` for ANSI terminals and `wincolor_sink` for Windows API-colored output.
- **File sinks** range from `basic_file_sink` to rotation-aware `rotating_file_sink`, `daily_file_sink`, and `hourly_file_sink` for log retention policies.
- **Network sinks** provide `udp_sink` for fire-and-forget transmission and `tcp_sink` for reliable delivery, while **system sinks** integrate with syslog, systemd, Android logcat, and Windows Event Log.
- **Utility sinks** like `ringbuffer_sink`, `callback_sink`, and `dup_filter_sink` support in-memory diagnostics, custom processing, and deduplication.
- **Multi-sink loggers** combine multiple destinations by passing a vector of sink pointers to the `spdlog::logger` constructor.

## Frequently Asked Questions

### What is the difference between *_mt and *_st sink suffixes?

The `*_mt` suffix indicates a **multi-threaded** sink that uses `std::mutex` to synchronize access, making it safe to use from multiple threads concurrently. The `*_st` suffix indicates a **single-threaded** sink that uses `spdlog::details::null_mutex_t`, which compiles to no-ops and provides maximum performance when you guarantee the sink is only accessed from one thread. Both variants share the same underlying implementation via the `base_sink<Mutex>` template defined in [`include/spdlog/sinks/base_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/base_sink.h).

### How do I implement a custom spdlog sink?

Derive your class from `base_sink<Mutex>` (typically with `std::mutex` for thread safety) and override two protected virtual methods: `sink_it_(const spdlog::details::log_msg& msg)` to perform the actual output operation, and `flush_()` to ensure any buffered data is written. The `log_msg` structure contains the formatted payload, log level, timestamp, and source location metadata.

### Which file sink should I use for production applications?

Use **rotating_file_sink** when you need to limit total disk consumption by file size and backup count, which is ideal for long-running services. Use **daily_file_sink** when you require time-based organization (e.g., one log file per day) for easier manual inspection or log aggregation pipelines. Use **basic_file_sink** only for short-lived processes or when external log rotation tools (like `logrotate`) manage the files.

### Can I change log patterns per sink in a multi-sink logger?

Yes. Each sink maintains its own formatter, allowing distinct output patterns for different destinations. For example, you might configure a console sink with a colored, human-readable pattern like `[%H:%M:%S] [%^%l%$] %v`, while a file sink uses a machine-parseable JSON format. Call `sink->set_pattern()` on each sink individually before attaching them to the logger.