spdlog Error Handling: How to Implement Custom Error Handlers in C++

spdlog catches internal logging exceptions and forwards error messages to a customizable handler function that users can configure globally via spdlog::set_error_handler() or per-logger via logger::set_error_handler() to replace the default stderr output.

The spdlog C++ logging library protects application stability by isolating logging failures through a centralized spdlog error handling system. When sinks fail or formatting throws, the library catches exceptions and routes them to a user-configurable callback instead of crashing. This mechanism is managed through the spdlog::details::registry class and individual logger instances, giving you granular control over failure recovery strategies.

How spdlog Error Handling Works

spdlog wraps every logging operation in a try-catch block inside the logger::log() method. When an exception occurs—such as a disk-full error during file sink output or a formatting exception—the library constructs an error message string and invokes the configured handler.

The system supports two handler scopes:

  • Global handler: Set via spdlog::set_error_handler() in include/spdlog/spdlog.h (lines 90-92), stored in the registry, and automatically applied to all new loggers.
  • Per-logger handler: Set via logger::set_error_handler() declared in include/spdlog/logger.h (lines 310-311), allowing specific loggers to override the global behavior.

Both handlers must match the signature void handler(const std::string &msg);. The default implementation simply writes the message to stderr, allowing the application to continue running.

Registry Architecture and Source Locations

The error handling infrastructure resides in the registry pattern implementation. When you call spdlog::set_error_handler(), the inline implementation in include/spdlog/spdlog-inl.h (lines 54-56) forwards the call to spdlog::details::registry.

Key source file responsibilities:

Setting a Global Error Handler

To intercept all spdlog internal errors application-wide, register a callback function before creating loggers. This is ideal for production services that need to redirect errors to monitoring systems or crash dumps.

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <cstdlib>

int main() {
    // Set global handler in include/spdlog/spdlog.h
    spdlog::set_error_handler([](const std::string &msg) {
        // Custom error processing
        auto err_sink = spdlog::stderr_color_mt("internal_errors");
        err_sink->critical("Logging system failure: {}", msg);
        
        // Optionally abort on critical logging failures
        std::abort();
    });

    // All subsequent loggers inherit this handler
    auto app_logger = spdlog::stdout_color_mt("app");
    app_logger->info("Application started");
}

The registry automatically applies this handler to every logger created after the call, as implemented in include/spdlog/details/registry-inl.h.

Setting a Per-Logger Error Handler

For library code or specific subsystems that require distinct error policies, override the handler on individual logger instances. This stores the callable directly in the logger object's internal state.

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

// Define custom exception type
struct logging_error : std::runtime_error {
    using std::runtime_error::runtime_error;
};

// Override handler specific to this logger (include/spdlog/logger.h)
file_logger->set_error_handler([](const std::string &msg) {
    throw logging_error("File sink failure: " + msg);
});

try {
    file_logger->info("Writing to potentially full disk");
} catch (const logging_error &e) {
    // Handle logging failure in application-specific way
    std::cerr << "Recoverable logging error: " << e.what() << std::endl;
}

Handler Precedence and Interaction

When both handlers are configured, per-logger handlers take precedence over the global handler. If a logger has its own error handler set via logger::set_error_handler(), the registry's global handler is bypassed for that instance. This allows you to maintain a default stderr policy for most loggers while implementing custom recovery logic for critical paths.

Production Error Handling Strategies

Choose your handler implementation based on deployment context.

Abort on failure for critical infrastructure where logging must succeed:

spdlog::set_error_handler([](const std::string &) {
    std::abort();
});

Alternative sink redirection for microservices that need clean log separation:

Redirect to syslog or dedicated error files to avoid interleaving with stdout logs, particularly important in multi-process environments.

Exception propagation for library development:

Use per-logger handlers to throw custom exceptions that library consumers can catch, converting silent logging failures into actionable errors that surface in user code.

Summary

  • spdlog error handling uses a callback mechanism with the signature void handler(const std::string &msg) to process internal logging failures.
  • Configure global handlers via spdlog::set_error_handler() in include/spdlog/spdlog.h for application-wide policies.
  • Configure per-logger handlers via logger::set_error_handler() in include/spdlog/logger.h for specific error recovery strategies.
  • The spdlog::details::registry class manages handler storage and automatically propagates global settings to new loggers.
  • Per-logger handlers override global handlers, allowing granular control over error response behaviors.

Frequently Asked Questions

What is the default spdlog error handler?

The default handler writes error messages to stderr using standard output streams and allows the application to continue executing. This prevents logging failures from crashing the application while still making errors visible during development. You can replace this default at any time by calling spdlog::set_error_handler() with your own callable.

Can I throw exceptions from within a custom error handler?

Yes, you can throw exceptions from custom handlers, but be aware that the exception will propagate from the logging call site. If you throw from a global handler, any logging operation that triggers an internal error will throw, potentially affecting all logging throughout your application. For targeted exception handling, use per-logger handlers instead.

How do I reset the error handler to the default behavior?

Pass an empty callable or re-implement the default behavior to spdlog::set_error_handler(). However, spdlog does not provide a direct "reset to default" function, so you must capture the original default behavior yourself before overriding it, or simply set a new handler that writes to stderr and continues execution.

Does spdlog error handling affect application performance?

The error handling mechanism only incurs overhead when an actual error occurs, as it operates within catch blocks. The normal logging hot path does not check error handler state, so there is no runtime performance penalty for having custom handlers configured during successful logging operations.

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 →