# How to Create a Custom Error Handler in spdlog: Global and Per-Logger Configuration

> Learn to create a custom error handler in spdlog using global or per-logger configuration. Intercept spdlog errors for better failure management and error reporting.

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

---

**You can intercept spdlog internal errors by installing a custom callback via `spdlog::set_error_handler()` for global coverage or `logger::set_error_handler()` for specific logger instances, allowing you to redirect failures to monitoring systems, throw exceptions, or perform cleanup instead of writing to stderr.**

The **gabime/spdlog** library provides a robust mechanism to capture internal failures—such as file permission errors or sink write failures—through user-defined callbacks. When a logging operation fails, spdlog invokes your registered handler with a descriptive message, giving you full control over error reporting rather than relying on the default `stderr` output.

## Understanding spdlog Error Handling Scopes

spdlog implements two distinct levels of **error handling** to accommodate different architectural needs. The **global error handler** applies to all loggers by default, while **per-logger error handlers** override the global setting for specific instances.

The default behavior, as implemented in the source, writes error messages to `stderr`. By installing a **custom error handler**, you can integrate with application-specific logging frameworks, trigger alerts, or implement graceful degradation strategies when the logging subsystem itself encounters problems.

## Implementing a Global Custom Error Handler

The global API is declared in [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h) (line 91) and implemented in [`include/spdlog/spdlog-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog-inl.h) (line 54). This handler propagates through the registry mechanism defined in [`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h) and [`registry-inl.h`](https://github.com/gabime/spdlog/blob/main/registry-inl.h) (lines 165-168), ensuring all newly created loggers inherit the setting unless explicitly overridden.

### Registering the Handler

To install a global handler, define a function matching the signature `void(const std::string&)` and pass it to `spdlog::set_error_handler()`:

```cpp
#include <spdlog/spdlog.h>
#include <iostream>

// Define a custom error handler
void my_error_handler(const std::string& msg)
{
    std::cerr << "[CRITICAL] spdlog error: " << msg << std::endl;
    // Optionally: send to monitoring, write to alternative log, or std::terminate()
}

int main()
{
    // Install globally before creating any loggers
    spdlog::set_error_handler(my_error_handler);
    
    auto console = spdlog::stdout_color_mt("console");
    // Any internal errors in 'console' will now call my_error_handler
}

```

### Global Handler Propagation

According to the **spdlog source code**, the registry stores the global handler and automatically assigns it to new loggers during creation. This ensures consistent error handling across your application without requiring per-instance configuration.

## Configuring Per-Logger Error Handlers

For scenarios requiring specialized error handling—such as different failure strategies for file versus network loggers—use the per-logger API. The member declaration resides in [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) (line 310) with implementation in [`include/spdlog/logger-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger-inl.h) (line 112).

### Overriding the Global Handler

Once you obtain a logger instance, call `set_error_handler()` with a callable object:

```cpp
auto file_logger = spdlog::basic_logger_mt("file", "logs.txt");

// Install a lambda handler specific to this logger
file_logger->set_error_handler([](const std::string& msg)
{
    std::cerr << "[file logger] Specific error: " << msg << std::endl;
    // Implement file-specific recovery logic here
});

```

This **per-logger error handler** takes precedence over the global setting, allowing granular control when different logging destinations require distinct failure management strategies.

## Complete Working Example

The following example demonstrates both global and per-logger **custom error handlers** working together:

```cpp
// custom_handler_demo.cpp
#include <spdlog/spdlog.h>
#include <iostream>

void global_error_handler(const std::string& msg)
{
    std::cerr << "[GLOBAL ERROR] " << msg << std::endl;
}

int main()
{
    // 1. Set global handler (from spdlog.h)
    spdlog::set_error_handler(global_error_handler);
    
    // 2. Create logger inheriting global handler
    auto console = spdlog::stdout_color_mt("console");
    
    // 3. Create file logger with specific handler
    auto file_logger = spdlog::basic_logger_mt("file", "logs.txt");
    file_logger->set_error_handler([](const std::string& msg)
    {
        std::cerr << "[FILE LOGGER ERROR] " << msg << std::endl;
        // Could attempt file rotation or alert operations team
    });
    
    // 4. Normal usage - errors will route to respective handlers
    console->info("Console message");
    file_logger->error("File message");
    
    return 0;
}

```

## Key Implementation Details

When implementing a **custom error handler in spdlog**, consider these technical specifics from the source code:

- **Function Signature**: Handlers must accept `const std::string&` and return `void`
- **Thread Safety**: The registry implementation in [`registry-inl.h`](https://github.com/gabime/spdlog/blob/main/registry-inl.h) uses mutex protection when setting and retrieving the global handler
- **Inheritance**: Loggers created after `spdlog::set_error_handler()` automatically receive the global handler; existing loggers retain their current settings unless explicitly modified
- **Default Behavior**: If no handler is set, spdlog uses an internal default that prints to `stderr`

## Summary

- **Global error handlers** apply application-wide via `spdlog::set_error_handler()`, declared in [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h)
- **Per-logger error handlers** override global settings via `logger::set_error_handler()`, declared in [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h)
- The **registry** ([`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h)) propagates global handlers to new logger instances
- Handlers must match the signature `void(const std::string&)` and can perform any operation including throwing exceptions or terminating the application
- Install handlers before logger creation to ensure consistent application-wide error handling

## Frequently Asked Questions

### What is the exact signature required for a custom error handler function?

Your custom handler must match the signature `void handler_name(const std::string& msg)`. Both the global `spdlog::set_error_handler()` and the per-logger `set_error_handler()` expect this exact signature, as defined in [`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h) and [`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/logger.h) respectively.

### Can I throw exceptions from within the custom error handler?

Yes, you can throw exceptions from your **custom error handler**, though you should consider whether your application can safely handle exceptions originating from the logging subsystem. The spdlog source code does not catch exceptions from user-provided handlers, so any uncaught exception will propagate up the call stack.

### How do I restore the default stderr error handler?

To restore the default behavior, pass a null handler or a lambda that mimics the default behavior to `spdlog::set_error_handler()` or `logger::set_error_handler()`. The default implementation simply writes the message to `stderr`, so you can replicate this with `[](const std::string& msg) { std::cerr << msg << std::endl; }`.

### What types of errors trigger the custom handler?

The **custom error handler** is invoked for internal spdlog failures such as being unable to open a log file, write failures to sinks, mutex acquisition failures, or formatting errors. It does not trigger on normal log messages; it only fires when the logging infrastructure itself encounters an error condition that would prevent successful message delivery.