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

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.

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

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

Single-threaded factory:

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:

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

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

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:

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

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 →