How to Replace the Default Logger in spdlog: A Complete Guide
Call spdlog::set_default_logger(new_logger) to atomically swap the global default instance used by spdlog::info() and logging macros, ensuring no other threads are logging during the operation.
The spdlog library (gabime/spdlog) provides a global default logger that powers the convenient spdlog::info() and SPDLOG_INFO macros. When you need to redirect all application-wide logging to custom sinks or change formatting globally, you must replace this default instance through the public registry API.
How the Default Logger Replacement Works
When you invoke spdlog::set_default_logger(), the internal details::registry performs three distinct operations atomically to ensure consistent global state.
Registry Registration
The registry maintains a map of named loggers in loggers_. According to the implementation in include/spdlog/details/registry-inl.h, calling set_default_logger registers your logger in this global map so it remains accessible via registry::get(). This prevents the logger from being garbage collected while it serves as the default.
Fast-Path Pointer Update
The registry updates the default_logger_ member variable declared in include/spdlog/details/registry.h. This pointer is what default_logger_raw() returns, providing zero-overhead access for the global convenience API without map lookups.
Automatic Cleanup of the Previous Default
If the previous default logger matches the name of the new logger, the registry resets the old shared_ptr instance (as seen in include/spdlog/details/registry-inl.h). If no other shared_ptr references exist to that logger, it is destroyed and its sinks are flushed and closed.
Thread Safety Considerations
The set_default_logger API is not thread-safe with respect to concurrent logging. Do not call this function while other threads are actively using spdlog::info(), spdlog::debug(), or any macro variants. This limitation is explicitly documented in the header comments in include/spdlog/spdlog.h. Synchronize the swap with your application's initialization phase or pause all logging threads before replacement.
Step-by-Step Implementation
Creating a Multi-Sink Logger
Build a custom logger with any combination of sinks before installing it as the default. The following example creates a logger that writes to both console and rotating file sinks:
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
// 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 max, 3 rotated 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 formatting and level
my_logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
my_logger->set_level(spdlog::level::debug);
Installing the New Default
Pass the configured logger to the registry to make it the global default:
// 2. Replace the spdlog default logger
spdlog::set_default_logger(my_logger);
// 3. All subsequent global calls use the new configuration
spdlog::info("This message appears in both console and rotating file");
SPDLOG_DEBUG("Debug macro also routes to the new default");
Replacing with a Null Logger (Silencing Output)
To temporarily disable all global logging without removing code, install 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 spdlog::info() calls now return immediately with no output
Key Source Files Reference
Understanding these implementation files helps when debugging or extending behavior:
include/spdlog/spdlog.h— Declares the public façade functionsdefault_logger(),default_logger_raw(), andset_default_logger().include/spdlog/spdlog-inl.h— Implements the inline wrappers that forward calls to the registry singleton.include/spdlog/details/registry.h— Defines theregistryclass interface, including thedefault_logger_pointer and registration methods.include/spdlog/details/registry-inl.h— Contains the template instantiation logic for storing, swapping, and resetting the default logger in theloggers_map.include/spdlog/logger.h— Shows the constructor interface used when building replacement loggers with custom sinks.
Summary
- Use
spdlog::set_default_logger()to replace the global default instance used by convenience functions and macros. - Registry mechanics automatically handle registration and fast-path pointer updates in
include/spdlog/details/registry-inl.h. - Thread safety requires synchronization — call the replacement only when no other threads are logging, as noted in
include/spdlog/spdlog.h. - Previous loggers are cleaned up when their names match and reference counts drop to zero.
- Any logger type works, including multi-sink loggers, async loggers, or null sinks for silencing.
Frequently Asked Questions
Is spdlog::set_default_logger thread-safe?
No, it is not thread-safe with respect to concurrent calls to logging macros or spdlog::info(). According to the source in include/spdlog/spdlog.h, you must ensure no other threads are using spdlog's global functions during the swap, typically by performing the replacement during single-threaded initialization or under a global application lock.
What happens to the previous default logger after replacement?
If the old logger's name matches the new one, the registry resets its internal shared_ptr (as implemented in include/spdlog/details/registry-inl.h). The logger is destroyed only if no other shared_ptr references exist elsewhere in your application.
Can I use an async logger as the default?
Yes. Since spdlog::async_logger inherits from spdlog::logger, you can create it using spdlog::basic_logger_mt<spdlog::async_factory>() and pass the resulting pointer to set_default_logger(). The async logger will then handle all global logging calls with its associated thread pool.
How do I temporarily replace the default logger and restore it later?
Capture the current default before swapping by storing the return value of spdlog::default_logger() in a std::shared_ptr<spdlog::logger>. After your specialized logging section completes, restore the original by calling spdlog::set_default_logger(stored_logger).
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →