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

> Learn to send logs to Windows Event Log using spdlog's win_eventlog_sink. This guide shows how to register your source and write logs directly via the native ReportEvent API.

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

---

**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`](https://github.com/gabime/spdlog/blob/main/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):

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

```cpp
#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`](https://github.com/gabime/spdlog/blob/main/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:

```cpp
#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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/tests/test_eventlog.cpp), which validates correct event type mapping and message content retrieval.