How to Configure the Syslog Sink in spdlog: A Complete Guide

spdlog provides dedicated syslog sinks (syslog_sink_mt and syslog_sink_st) that forward log messages to the system logger via the standard syslog(3) API, configurable through factory helpers that control thread safety, identifiers, and formatting options.

The gabime/spdlog library offers robust support for system logging through its specialized syslog sink implementation. Configuring the syslog sink in spdlog requires selecting the appropriate thread-safety variant, setting the syslog facility code, and determining whether to apply spdlog's pattern formatting or forward raw payloads.

Understanding the spdlog Syslog Sink Architecture

The syslog functionality is implemented in include/spdlog/sinks/syslog_sink.h. This header defines the syslog_sink template class that inherits from base_sink<Mutex>, providing a standard interface for routing log entries to the Unix syslog service.

Thread Safety Variants

The implementation provides two mutex-specific type aliases for different concurrency requirements:

  • syslog_sink_mt – Uses std::mutex for thread-safe access across multiple threads
  • syslog_sink_st – Uses a null-mutex for single-threaded contexts where synchronization overhead is unnecessary

Both variants manage the syslog connection lifecycle automatically, calling ::openlog in the constructor and ::closelog in the destructor.

How to Configure the Syslog Sink in spdlog

The most common approach uses the factory helpers syslog_logger_mt and syslog_logger_st. These functions instantiate a logger pre-configured with a syslog sink and accept five parameters:

  1. Logger name – The spdlog identifier for retrieving the logger later
  2. Syslog ident – The string that appears as the program name in system logs (defaults to the logger name if empty)
  3. Option flags – Bitwise OR of syslog options like LOG_PID (include PID) or LOG_CONS (write to console on failure)
  4. Facility – The syslog facility code (e.g., LOG_USER, LOG_DAEMON, LOG_LOCAL0 through LOG_LOCAL7)
  5. Enable formatting – Boolean flag determining whether spdlog applies its pattern formatter or sends raw payloads
#include <spdlog/spdlog.h>
#include <spdlog/sinks/syslog_sink.h>

int main() {
    // Single-threaded logger with raw message output
    auto syslog = spdlog::syslog_logger_st(
        "my_app",                // logger name
        "my_app_ident",          // syslog ident (appears in /var/log/syslog)
        LOG_PID,                 // syslog option flags
        LOG_LOCAL0,              // facility
        false);                  // disable spdlog formatting

    spdlog::register_logger(syslog);
    syslog->info("Application started");

    // Thread-safe logger with full spdlog formatting
    auto syslog_mt = spdlog::syslog_logger_mt(
        "my_app_mt",
        "my_app_mt",
        LOG_PID | LOG_CONS,
        LOG_USER,
        true);                   // enable formatting (timestamp, level, etc.)

    spdlog::register_logger(syslog_mt);
    syslog_mt->warn("A warning with formatted output");
}

Message Formatting and Priority Mapping

The enable_formatting parameter controls how messages appear in system logs. When set to true, the sink applies the attached formatter (typically adding timestamps, log levels, and source locations) before transmission. When false, only the raw msg.payload is forwarded via the sink_it_ method.

Priority mapping follows a fixed array syslog_levels_ that translates spdlog severity levels to syslog priorities. The implementation calls syslog_prio_from_level to perform this translation before sending to ::syslog.

Customizing Priority Mapping

For applications requiring non-standard level mappings (e.g., mapping spdlog::level::info to LOG_NOTICE), subclass syslog_sink and override the protected syslog_prio_from_level method:

template <typename Mutex>
class my_syslog_sink : public spdlog::sinks::syslog_sink<Mutex> {
public:
    using base = spdlog::sinks::syslog_sink<Mutex>;
    using base::base; // inherit constructors

protected:
    int syslog_prio_from_level(const spdlog::details::log_msg &msg) const override {
        if (msg.level == spdlog::level::info)
            return LOG_NOTICE;               // custom mapping
        return base::syslog_prio_from_level(msg);
    }
};

Instantiate your custom sink using my_syslog_sink_mt or my_syslog_sink_st type aliases, then create a logger manually via spdlog::logger.

Lifetime Management and Best Practices

Because syslog_sink calls ::openlog in its constructor and ::closelog in its destructor, the logger instance must remain alive for the duration of syslog usage. Premature destruction closes the system log connection, potentially dropping subsequent messages. Register the logger with spdlog::register_logger to ensure global accessibility, or store it in a static scope for application lifetime.

The example/example.cpp file in the repository demonstrates practical syslog usage at line 304, showing integration with the broader sink ecosystem.

Summary

  • spdlog provides syslog_sink_mt and syslog_sink_st in include/spdlog/sinks/syslog_sink.h for system logging
  • Factory helpers syslog_logger_mt and syslog_logger_st streamline configuration of the syslog ident, facility, and formatting options
  • The enable_formatting boolean controls whether raw payloads or formatted messages reach the system log
  • Priority mapping is customizable by overriding syslog_prio_from_level in a derived class
  • Syslog connections persist for the sink's lifetime, requiring careful scope management to avoid premature closure

Frequently Asked Questions

What is the difference between syslog_sink_mt and syslog_sink_st?

syslog_sink_mt uses std::mutex to protect internal state during concurrent logging, making it safe for multi-threaded applications. syslog_sink_st uses a null-mutex and provides better performance in single-threaded contexts where synchronization is unnecessary. Both implement identical message formatting and syslog API interaction.

How do I change the syslog facility after creating the sink?

The facility is set during construction via ::openlog and cannot be changed dynamically. To use a different facility, you must create a new sink instance with the desired facility code (such as LOG_DAEMON or LOG_LOCAL0) and replace the existing logger or add the new sink to a dist_sink or multi_sink logger.

Why are my log messages not showing up in /var/log/syslog?

Ensure the syslog identifier matches your system configuration and that the facility code is not filtered by syslog daemon rules. Additionally, verify that enable_formatting is set appropriately—if false, only the raw message payload is sent without spdlog's timestamp or level prefixes. Check system-specific log locations (e.g., /var/log/messages or journalctl) depending on your Linux distribution.

Can I use the syslog sink on Windows?

The syslog sink relies on the Unix syslog(3) API and is not available on Windows systems. For cross-platform applications, use conditional compilation to include syslog_sink.h only on Unix-like systems, or create a custom sink that abstracts platform-specific logging mechanisms.

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 →