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

Call spdlog::set_default_logger(std::shared_ptr<logger>) to replace the global default logger used by spdlog::info() and SPDLOG_INFO macros.

The spdlog library provides a global default logger that powers convenience functions like spdlog::info() and the SPDLOG_INFO macro. When you need to redirect all global logging output to custom sinks or silence it entirely, you must change the default logger in spdlog through its registry API. This guide explains the exact mechanism using the actual implementation from the gabime/spdlog repository.

Understanding the spdlog Default Logger Registry

The default logger is managed internally by the details::registry singleton. According to the source code in include/spdlog/details/registry.h, the registry maintains a default_logger_ pointer that provides fast access for the default API. When you invoke global logging functions, they ultimately call default_logger_raw() to retrieve this pointer and dispatch messages.

The registry stores all loggers in a loggers_ map, and the default logger is simply a specific entry that receives special treatment. The public API for swapping it is declared in include/spdlog/spdlog.h and implemented in include/spdlog/spdlog-inl.h, which forwards calls to the registry's set_default_logger() method.

How to Change the Default Logger in spdlog

To change the default logger, construct a std::shared_ptr<spdlog::logger> with your desired sinks and configuration, then pass it to spdlog::set_default_logger(). As implemented in include/spdlog/details/registry-inl.h, this function performs three operations: it registers the new logger in the global loggers_ map, updates the default_logger_ pointer used by default_logger_raw(), and resets the previous default instance if it matches the replaced name.

Here is a complete example that creates a multi-sink logger and installs it as the new default:

// 1. Build a custom logger (colored console + rotating file)
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
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 pattern and level
my_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
my_logger->set_level(spdlog::level::debug);

// 2. Replace the spdlog default logger
spdlog::set_default_logger(my_logger);

// 3. Global calls now use the new configuration
spdlog::info("This goes to both console and rotating file");
SPDLOG_DEBUG("Debug message via macro");

Silencing Global Output with a Null Logger

To completely suppress output from global macros without modifying existing code, create a logger with a null_sink_mt and set it as the default. This technique effectively discards all messages sent through spdlog::info() or similar functions.

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);

Thread Safety Considerations

Important: The set_default_logger API is not thread-safe with respect to concurrent logging. Do not call spdlog::set_default_logger while other threads are actively using spdlog's global functions (spdlog::info(), SPDLOG_INFO, etc.). This restriction is explicitly documented in the header comments of include/spdlog/spdlog.h. For thread-safe reconfiguration, ensure all logging threads are synchronized or idle before swapping the default logger.

Key Source Files and Implementation

Understanding the internal structure helps debug issues when you change the default logger in spdlog:

Summary

  • The default logger in spdlog is stored in the internal details::registry and accessed via default_logger_raw().
  • Use spdlog::set_default_logger(std::shared_ptr<logger>) to replace it at runtime.
  • The registry updates the fast-path pointer and registers the new logger automatically.
  • Thread safety warning: Never call set_default_logger during concurrent logging operations.
  • You can silence global logging by setting a null sink logger as the default.

Frequently Asked Questions

What happens to the old default logger when I call set_default_logger?

When you change the default logger in spdlog, the registry checks if the previous default shares the same name as the new one. If so, it resets the old instance in the loggers_ map. The old logger's shared_ptr reference count decrements normally, and it is destroyed when no other references exist.

Can I use set_default_logger with async loggers?

Yes. Since spdlog::set_default_logger accepts any std::shared_ptr<spdlog::logger>, you can pass an async_logger instance. The registry treats async and sync loggers identically regarding default logger assignment.

Is spdlog::set_default_logger thread-safe?

No. According to the source code comments in include/spdlog/spdlog.h, you must not call set_default_logger while other threads are using global logging functions. Synchronize your threads or perform the swap during application initialization before spawning worker threads.

How do I retrieve the current default logger?

Call spdlog::default_logger() to get a std::shared_ptr<logger> copy, or spdlog::default_logger_raw() for a raw pointer when you need high-performance access and can guarantee the logger remains valid.

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 →