# Integrating spdlog with syslog: A Complete Guide to the Syslog Sink

> Easily integrate spdlog with syslog using the dedicated syslog sink. This guide covers forwarding log records to the native POSIX syslog service for robust logging.

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

---

**`spdlog` ships with a dedicated syslog sink that forwards log records to the native POSIX `syslog(3)` service, implemented in [`include/spdlog/sinks/syslog_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/syslog_sink.h).**

Integrating **spdlog** with the system syslog daemon allows applications to leverage centralized logging infrastructure on Linux and Unix systems. The `gabime/spdlog` repository provides a template-based `syslog_sink` class that handles all POSIX syslog API calls, including `openlog`, `syslog`, and `closelog`, while translating spdlog severity levels to standard syslog priorities.

## Understanding the syslog_sink Architecture

The core of **spdlog syslog integration** resides in the `syslog_sink` template class defined in [`include/spdlog/sinks/syslog_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/syslog_sink.h). This sink extends `base_sink<Mutex>` and manages the lifecycle of the native syslog connection.

### Template Structure and Thread Safety

The sink uses a mutex template parameter to control concurrency:

- **`syslog_sink_mt`** → `syslog_sink<std::mutex>` for multithreaded applications
- **`syslog_sink_st`** → `syslog_sink<details::null_mutex>` for single-threaded contexts with zero overhead

The constructor accepts three critical parameters: an **ident** string (the program name appearing in syslog entries), **option** flags (such as `LOG_PID` to include the process ID), and a **facility** code (like `LOG_USER` or `LOG_LOCAL0`).

### POSIX syslog Integration

When instantiated, the sink immediately calls `::openlog` to establish the connection to the syslog daemon. Each log message passes through the following flow:

1. The base sink formats the message using the configured pattern
2. The sink maps `spdlog::level` values (info, warn, error) to corresponding `LOG_*` priorities
3. The formatted string is forwarded to `::syslog`
4. Upon destruction, `::closelog` releases system resources

## Configuring syslog_logger Factory Helpers

Rather than manually constructing sinks, use the convenience factory functions exposed in the public API. These helpers instantiate a logger that owns a `syslog_sink` and automatically register it in spdlog's global registry.

**Multithreaded factory:**

```cpp
auto logger = spdlog::syslog_logger_mt("syslog", "my-app", LOG_PID, LOG_USER);

```

**Single-threaded factory:**

```cpp
auto logger = spdlog::syslog_logger_st("syslog_st", "my-app", LOG_PID);

```

After registration, retrieve the logger anywhere in your application using `spdlog::get("syslog")`.

## Practical Implementation Examples

### Basic Single-Sink Setup

Create a dedicated syslog logger with the default thread-safe configuration:

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

int main()
{
    // ident = program name, LOG_PID adds the PID, LOG_USER is the facility
    auto syslog_logger = spdlog::syslog_logger_mt("syslog", "my-app", LOG_PID, LOG_USER);
    syslog_logger->info("Application started");
    syslog_logger->warn("Low-level warning");
    syslog_logger->error("An error occurred: {}", std::strerror(errno));
}

```

### Multi-Sink Configuration (Console + Syslog)

Combine stdout output with system logging by constructing multiple sinks and passing them to a single logger:

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

int main()
{
    auto console = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
    console->set_level(spdlog::level::info);
    console->set_pattern("[%l] %v");

    auto syslog = std::make_shared<spdlog::sinks::syslog_sink_mt>("my-app", LOG_PID);
    syslog->set_level(spdlog::level::warn);   // only warnings+ go to syslog

    spdlog::logger logger("dual", {console, syslog});
    logger.set_level(spdlog::level::debug);
    logger.debug("Debug message – stays on console only");
    logger.warn("Warning – appears on console and syslog");
}

```

### Single-Threaded Optimization

For performance-critical single-threaded applications, eliminate mutex overhead by using the `_st` variant:

```cpp
auto syslog_logger = spdlog::syslog_logger_st("syslog_st", "my-app", LOG_PID);
syslog_logger->info("Running in a single-threaded context");

```

## Advanced Configuration Options

### Formatting Control with enable_formatting

By default, `syslog_sink` forwards formatted text to the syslog daemon. Set `enable_formatting` to `false` to send raw message templates (including `%` placeholders), allowing the external syslog service to handle interpolation:

```cpp
auto syslog = std::make_shared<spdlog::sinks::syslog_sink_mt>("my-app", LOG_PID);
syslog->enable_formatting = false;  // Sends raw format string to syslog

```

This is useful when the syslog daemon performs structured parsing or custom formatting of log entries.

## Summary

- **Primary Header**: All syslog functionality resides in [`include/spdlog/sinks/syslog_sink.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/sinks/syslog_sink.h) within the `gabime/spdlog` repository.
- **Thread Safety**: Choose between `syslog_sink_mt` (thread-safe) and `syslog_sink_st` (lock-free) based on your concurrency requirements.
- **Factory Methods**: Use `spdlog::syslog_logger_mt()` or `spdlog::syslog_logger_st()` to create and register loggers automatically.
- **POSIX Mapping**: The sink handles translation between `spdlog::level` enums and standard `LOG_*` priorities, managing `openlog`, `syslog`, and `closelog` calls internally.
- **Flexible Output**: Combine the syslog sink with other sinks (stdout, file) to create multi-target logging strategies with independent level filtering.

## Frequently Asked Questions

### What is the difference between syslog_sink_mt and syslog_sink_st?

`syslog_sink_mt` uses `std::mutex` to protect shared resources, making it safe for concurrent access from multiple threads. `syslog_sink_st` uses `details::null_mutex`, which compiles to no-ops and provides higher performance in single-threaded applications where synchronization is unnecessary.

### How do I prevent duplicate logs when using multiple sinks?

Configure severity level filtering on individual sinks rather than the logger. Set the syslog sink to a higher threshold (e.g., `spdlog::level::warn`) while keeping the console sink at `debug` or `info`. This ensures only warnings and errors reach the system log while verbose output remains local.

### Can I use custom syslog facilities like LOG_LOCAL0?

Yes. The third parameter to the `syslog_sink` constructor or factory function accepts any valid POSIX facility constant, including `LOG_LOCAL0` through `LOG_LOCAL7`, `LOG_DAEMON`, `LOG_AUTH`, or `LOG_SYSLOG`. These facilities route messages to specific system log files based on your syslog daemon configuration.

### Does spdlog's syslog sink work on Windows?

No. The `syslog_sink` implementation relies exclusively on POSIX `syslog(3)` APIs (`openlog`, `syslog`, `closelog`), which are not available on Windows. For Windows Event Log integration, use the `msvc_sink` or implement a custom sink using the Windows Event Logging API.