How to Replace the Default Logger in spdlog: A Complete Guide

Call spdlog::set_default_logger(my_logger) to swap the internal default logger used by global macros like spdlog::info() and SPDLOG_INFO().

The spdlog library provides a convenient default logger that powers global logging functions, but production applications often need to redirect output to multiple sinks or custom formats. Replacing the default logger in spdlog requires interacting with the internal details::registry class through the public API, allowing you to install a fully configured spdlog::logger instance that handles all subsequent global log calls.

Understanding the Internal Registry

The default logger is held inside the internal registry (details::registry), which maintains a fast-path pointer named default_logger_ used by default_logger_raw(). According to the implementation in include/spdlog/details/registry-inl.h, when you call spdlog::set_default_logger(), the registry performs three critical operations:

  1. Registers the new logger in the global loggers_ map so it can be retrieved later via registry::get().
  2. Updates the fast-path pointer default_logger_ used by the default API.
  3. Optionally removes the previous default if it was the one being replaced (the old instance is reset when the name matches).

The default logger itself is a regular spdlog::logger instance, so you can create it with any sink configuration before installing it.

How to Replace the Default Logger in spdlog

Step 1: Create a Custom Logger

Build a logger with your desired sinks, pattern, and level. For example, combining a colored console output with a rotating file sink:

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

// Create sinks
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
    "logs/app.log", 1048576 * 5, 3);   // 5 MiB, 3 files

// Initialize logger with sink list
spdlog::sinks_init_list sink_list = { console_sink, file_sink };
auto my_logger = std::make_shared<spdlog::logger>("my_default", 
                                                  sink_list.begin(), 
                                                  sink_list.end());

// Configure formatting
my_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
my_logger->set_level(spdlog::level::debug);

Step 2: Install the New Default Logger

Pass your configured logger to the registry via the public API declared in include/spdlog/spdlog.h:

spdlog::set_default_logger(my_logger);

// Now all global calls use the new logger
spdlog::info("This goes to both console and rotating file");
SPDLOG_DEBUG("Debug message via macro");

As implemented in include/spdlog/details/registry-inl.h, this call immediately updates the internal pointer so subsequent global macros route to your custom sinks.

Silencing Global Output with a Null Logger

To completely disable output from global logging calls without removing the code, replace the default logger with a null sink:

auto null_sink = std::make_shared<spdlog::sinks::null_sink_mt>();
auto silent_logger = std::make_shared<spdlog::logger>("silent", null_sink);
spdlog::set_default_logger(silent_logger);

All subsequent spdlog::info() and SPDLOG_INFO() calls will execute but discard messages, effectively silencing the default output stream.

Critical Thread Safety Warning

spdlog::set_default_logger is not thread-safe with respect to other threads using spdlog’s global functions. As documented in the header comments in include/spdlog/spdlog.h, you must not call set_default_logger while other threads are actively logging via spdlog::info(), SPDLOG_INFO(), or other global macros. Ensure all logging activity has stopped or synchronize access before swapping the default logger at runtime.

Key Implementation Files

Understanding these source files helps when tracing the behavior of the default logger:

Summary

  • The default logger is stored in the internal details::registry and accessed via a fast-path pointer.
  • Use spdlog::set_default_logger(my_logger) to replace it, which updates the registry's loggers_ map and default_logger_ pointer.
  • The replacement can be any spdlog::logger instance, including multi-sink configurations or null loggers.
  • The operation is not thread-safe—do not call set_default_logger concurrently with active logging threads.
  • Global macros like spdlog::info() and SPDLOG_INFO() immediately reflect the new logger after the call returns.

Frequently Asked Questions

Can I replace the default logger at runtime?

Yes. You can call spdlog::set_default_logger() at any point during application execution to redirect global logging output. However, as noted in include/spdlog/spdlog.h, you must ensure no other threads are calling global logging functions during the swap.

Is spdlog::set_default_logger thread-safe?

No. The registry does not synchronize access to set_default_logger. While spdlog's logging operations are generally thread-safe, changing the default logger itself requires external synchronization to prevent race conditions with active logging threads.

What happens to the old default logger after replacement?

The previous default logger is removed from the registry's internal loggers_ map if its name matches the default, and its shared pointer is reset. If you maintain other shared_ptr references to the old logger, it will continue to exist independently but will no longer receive global log traffic.

Can I use multiple sinks with the default logger?

Yes. Since the default logger is just a standard spdlog::logger instance, you can construct it with multiple sinks (such as console and file sinks) using spdlog::sinks_init_list before calling set_default_logger(), as shown in the multi-sink example above.

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 →