How to Send Logs to Windows Event Log with spdlog: A Complete Implementation Guide

Use spdlog's win_eventlog_sink to write log records directly to the Windows Event Log via native ReportEvent API calls, after registering your source name in the system registry.

The spdlog library provides first-class support for Windows Event Log integration through a dedicated sink implementation. This guide covers the architecture of the win_eventlog_sink, registry prerequisites, and practical code examples using the actual implementation from the gabime/spdlog repository.

Understanding the win_eventlog_sink Architecture

The Windows Event Log sink is implemented in include/spdlog/sinks/win_eventlog_sink.h. It extends spdlog::sinks::base_sink<Mutex> and overrides the sink_it_ method to handle the core logging routine.

Core Implementation Details

The sink registers your configured source name with the Windows API using RegisterEventSourceA (or RegisterEventSourceW for wide characters) during construction. For each log entry, it formats the message through the logger's formatter (base_sink::formatter_), appends a null terminator, and invokes ReportEventA/ReportEventW to write the event. Resource cleanup occurs automatically in the destructor via DeregisterEventSource.

The implementation handles string conversion internally: when SPDLOG_WCHAR_TO_UTF8_SUPPORT is defined, messages convert to UTF-16; otherwise, the raw UTF-8 char* passes directly to ReportEventA.

Event Type and Category Mapping

Two internal helper functions map spdlog concepts to Windows Event Log fields:

  • eventlog::get_event_type translates spdlog::level values to Windows event types: trace and debug map to EVENTLOG_SUCCESS, info to EVENTLOG_INFORMATION_TYPE, warn to EVENTLOG_WARNING_TYPE, and error/critical to EVENTLOG_ERROR_TYPE.
  • eventlog::get_event_category returns the numeric log level as the event category.

User SID Integration

Before reporting events, the sink obtains the current user's Security Identifier via internal::sid_t::get_current_user_sid. This populates the Event Log record's user field. If SID acquisition fails, the sink tolerates the error and omits the user field rather than failing the log operation.

Registry Requirements and Setup

Before instantiating the sink, you must create registry entries for your custom log source. The system requires these keys under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\<source_name>.

Create a .reg file with the following content (replace MyApp with your chosen source name):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\MyApp]
"TypesSupported"=dword:00000007
"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
  00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,\
  32,00,5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,\
  00,6c,00,00,00

Key points:

  • TypesSupported (0x7) enables success, information, warning, and error event types.
  • EventMessageFile points to mscoree.dll (the default message file containing generic templates). For production applications, point this to your custom message DLL.
  • Registry creation under HKLM requires administrator privileges—perform this step during application installation.

Creating and Configuring the Event Log Logger

Instantiate the sink using std::make_shared with your registered source name and an optional event ID (default is 1000). The library provides two type aliases: win_eventlog_sink_mt for thread-safe logging and win_eventlog_sink_st for single-threaded applications.

#include <spdlog/spdlog.h>
#include <spdlog/sinks/win_eventlog_sink.h>

int main()
{
    // Create thread-safe Event Log sink
    auto win_evt_sink = std::make_shared<spdlog::sinks::win_eventlog_sink_mt>(
        "MyApp",    // Must match registry source name
        2000);      // Custom event ID

    // Configure logger
    spdlog::logger logger("eventlog_logger", win_evt_sink);
    logger.set_level(spdlog::level::trace);
    logger.set_pattern("%v");  // Log raw message only

    // Emit logs
    logger.info("Application started successfully");
    logger.error("Failed to connect to database");
}

Complete Working Example with Registry Verification

The unit test in tests/test_eventlog.cpp demonstrates end-to-end usage including verification of recorded events. Below is a production-ready pattern combining registry setup with logging:

#include <spdlog/spdlog.h>
#include <spdlog/sinks/win_eventlog_sink.h>
#include <windows.h>
#include <iostream>

int main()
{
    // Step 1: Verify registry source exists (run as admin to create)
    const char* source_name = "MyApplication";
    HANDLE hEventLog = RegisterEventSourceA(NULL, source_name);
    
    if (hEventLog == NULL) {
        std::cerr << "Registry source not found. Run installer first.\n";
        return 1;
    }
    DeregisterEventSource(hEventLog);

    // Step 2: Create spdlog sink (must match registry source name)
    auto evt_sink = std::make_shared<spdlog::sinks::win_eventlog_sink_mt>(
        source_name, 1000);
    
    auto logger = std::make_shared<spdlog::logger>("evt_logger", evt_sink);
    logger->set_pattern("[%H:%M:%S] %v");

    // Step 3: Log at various levels
    logger->trace("Trace debug data");      // Maps to EVENTLOG_SUCCESS
    logger->debug("Debug information");     // Maps to EVENTLOG_SUCCESS
    logger->info("Informational message");  // Maps to EVENTLOG_INFORMATION_TYPE
    logger->warn("Warning condition");      // Maps to EVENTLOG_WARNING_TYPE
    logger->error("Error occurred");        // Maps to EVENTLOG_ERROR_TYPE
    logger->critical("Critical failure");   // Maps to EVENTLOG_ERROR_TYPE

    std::cout << "Events written successfully. Check Event Viewer.\n";
    return 0;
}

Mapping reference:

  • Trace/Debug → EVENTLOG_SUCCESS
  • Info → EVENTLOG_INFORMATION_TYPE
  • Warning → EVENTLOG_WARNING_TYPE
  • Error/Critical → EVENTLOG_ERROR_TYPE

Summary

  • win_eventlog_sink in include/spdlog/sinks/win_eventlog_sink.h implements Windows Event Log integration by wrapping the native ReportEvent API.
  • Registry prerequisite: Create HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\<source_name> with TypesSupported and EventMessageFile values before logging.
  • Thread safety: Use win_eventlog_sink_mt for multi-threaded applications; win_eventlog_sink_st for single-threaded scenarios.
  • Level mapping: spdlog levels automatically translate to appropriate Windows event types (Success, Information, Warning, Error).
  • User attribution: The sink automatically attaches the current user's SID to each event record when available.

Frequently Asked Questions

What registry keys are required before using win_eventlog_sink?

You must create a key under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\<source_name> containing a TypesSupported DWORD value (typically 0x7 for all standard types) and an EventMessageFile string pointing to a message DLL. Without these keys, RegisterEventSource fails and the sink cannot write events.

How does spdlog map log levels to Windows Event Log types?

The internal eventlog::get_event_type function maps spdlog::level::trace and level::debug to EVENTLOG_SUCCESS, level::info to EVENTLOG_INFORMATION_TYPE, level::warn to EVENTLOG_WARNING_TYPE, and both level::error and level::critical to EVENTLOG_ERROR_TYPE.

Can I use win_eventlog_sink in a multi-threaded application?

Yes, by using spdlog::sinks::win_eventlog_sink_mt (the mutex-protected variant). For single-threaded programs, use win_eventlog_sink_st to avoid mutex overhead. The underlying Windows ReportEvent API is thread-safe, but the spdlog sink requires the mutex wrapper to protect its internal formatter and state.

Where can I find the official implementation and tests for the Event Log sink?

The implementation resides in include/spdlog/sinks/win_eventlog_sink.h and demonstrates the base_sink inheritance pattern, SID acquisition via internal::sid_t, and cleanup logic. Comprehensive usage examples and verification tests are located in tests/test_eventlog.cpp, which validates correct event type mapping and message content retrieval.

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 →