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

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, while the templated base_sink<Mutex> class in 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 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 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, 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.

#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 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) 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) rolls over at UTC midnight (or a custom hour), while hourly_file_sink (in include/spdlog/sinks/hourly_file_sink.h) creates a new file every 60 minutes. Both accept a tm time calculator and file name pattern.

#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) 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) 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) 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 and 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

#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.

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) 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) 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) 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) invokes a user-provided callable for every log record. This enables integration with proprietary logging backends, custom network protocols, or Qt signals.

#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) 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) 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.

#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, 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.

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.

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 →