Setting Up Custom Error Handlers in spdlog: Global and Per-Logger Configuration

You can intercept spdlog internal errors by installing a custom callback using spdlog::set_error_handler() for global scope or logger::set_error_handler() for specific loggers.

Setting up custom error handlers in spdlog allows you to capture and process internal failures—such as file permission errors or sink write failures—according to your application's requirements. The gabime/spdlog library provides a two-tiered architecture that supports both application-wide and logger-specific error handling strategies. By default, spdlog writes error messages to stderr, but you can replace this behavior with custom logic for monitoring, alerting, or graceful degradation.

Understanding spdlog Error Handling Architecture

spdlog implements error handling through function pointer callbacks that accept a const std::string& parameter containing the error message. The architecture operates at two distinct scopes:

  • Global handler: Set via spdlog::set_error_handler(), applies to all loggers unless explicitly overridden
  • Per-logger handler: Set via logger::set_error_handler(), takes precedence over the global handler for that specific instance

When a logging operation encounters an error—such as failing to open a log file or write to a sink—spdlog invokes the registered handler with an informative message. If no custom handler is registered, the library falls back to writing the error to standard error.

Implementing a Global Error Handler

The global error handler provides a centralized mechanism for processing all spdlog errors across your application.

Registering the Handler

To install a global handler, define a function with the signature void(const std::string&) and pass it to spdlog::set_error_handler(). According to the spdlog source code, this function is declared in [include/spdlog/spdlog.h](https://github.com/gabime/spdlog/blob/v1.x/include/spdlog/spdlog.h#L91) at line 91 and implemented in [include/spdlog/spdlog-inl.h](https://github.com/gabime/spdlog/blob/v1.x/include/spdlog/spdlog-inl.h#L54) at line 54. The registry propagates this handler to newly created loggers through [include/spdlog/details/registry.h](https://github.com/gabime/spdlog/blob/v1.x/include/spdlog/details/registry.h#L79) and registry-inl.h (lines 165-168).

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

void my_global_handler(const std::string& msg)
{
    // Send to monitoring system, alternate log, or stderr with formatting
    std::cerr << "[CRITICAL] spdlog error: " << msg << std::endl;
}

int main()
{
    // Install before creating any loggers
    spdlog::set_error_handler(my_global_handler);
    
    auto logger = spdlog::stdout_color_mt("console");
    // Any errors in this logger will now invoke my_global_handler
}

Configuring Per-Logger Error Handlers

When different loggers require distinct error handling strategies—such as separating file logger failures from console logger issues—you can override the global handler at the logger level.

Overriding the Global Default

The logger class provides set_error_handler() to register a logger-specific callback. This member is declared in [include/spdlog/logger.h](https://github.com/gabime/spdlog/blob/v1.x/include/spdlog/logger.h#L310) at line 310 and implemented in [include/spdlog/logger-inl.h](https://github.com/gabime/spdlog/blob/v1.x/include/spdlog/logger-inl.h#L112) at line 112.

auto file_logger = spdlog::basic_logger_mt("file", "/var/log/app.txt");

// Install a lambda handler specific to this logger
file_logger->set_error_handler([](const std::string& msg)
{
    // Handle file-specific errors differently
    std::cerr << "[File Logger Failure] " << msg << std::endl;
    // Could also: increment metrics, send alerts, or trigger rotation
});

Practical Implementation Example

This complete example demonstrates both global and per-logger error handlers working together:

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

// 1. Define a global custom error handler
void my_error_handler(const std::string& msg)
{
    std::cerr << "[spdlog error] " << msg << std::endl;
    // Optional: std::terminate() or throw custom exception
}

int main()
{
    // 2. Install global handler before any logger creation
    spdlog::set_error_handler(my_error_handler);
    
    // 3. Create logger using the global handler
    auto console = spdlog::stdout_color_mt("console");
    console->info("Hello, world!");
    
    // 4. Override per-logger for specific error handling
    auto file_logger = spdlog::basic_logger_mt("file", "logs.txt");
    file_logger->set_error_handler([](const std::string& msg)
    {
        std::cerr << "[file logger specific] " << msg << std::endl;
    });
    
    // 5. Errors trigger the appropriate handler
    file_logger->error("This invokes the per-logger handler");
    
    return 0;
}

The implementation follows these steps:

  1. Define your handler with the exact signature void(const std::string&)
  2. Register globally via spdlog::set_error_handler() to establish default behavior
  3. Create loggers normally—they automatically inherit the global handler
  4. Override specific loggers using logger::set_error_handler() when specialized handling is required
  5. Handle errors as they occur through your custom logic

Summary

  • Global error handling is configured through spdlog::set_error_handler() in spdlog.h and applies to all loggers by default
  • Per-logger error handling overrides the global setting via logger::set_error_handler() declared in logger.h
  • Handler signature must be void(const std::string&) to receive error messages
  • Registry mechanism in registry.h and registry-inl.h propagates global handlers to new logger instances
  • Default behavior writes errors to stderr if no custom handler is registered

Frequently Asked Questions

What signature must a custom error handler have in spdlog?

A custom error handler must conform to the signature void(const std::string& msg). The function receives a string containing the error description. You can use a free function, static member function, or lambda expression as long as it matches this signature.

Does a per-logger error handler override the global handler?

Yes. When you call logger::set_error_handler(), that specific logger will use your custom callback instead of the global handler installed via spdlog::set_error_handler(). Other loggers continue to use the global handler unless they also have their own override.

When does spdlog invoke the error handler?

spdlog calls the error handler when internal logging operations fail, such as when a file sink cannot open the target file due to permissions, when disk space is exhausted, or when a network sink fails to connect. The handler is invoked synchronously during the logging call that triggered the error.

Can I throw exceptions from within a custom error handler?

Yes, you can throw exceptions from your error handler, but this will propagate out of the logging call site. Since spdlog is often used in error paths or destructor contexts, throwing exceptions from handlers may cause std::terminate() to be called if the exception escapes during stack unwinding. Consider logging to alternative channels or using std::abort()/std::terminate() explicitly if you need to stop the application.

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 →