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

> Learn how to replace spdlog's default logger. This guide shows you to atomically swap the global default logger for seamless updates without logging interruptions.

- Repository: [Gabi Melman/spdlog](https://github.com/gabime/spdlog)
- Tags: how-to-guide
- Published: 2026-07-19

---

**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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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:

```cpp
#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:

```cpp
// 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:

```cpp
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`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h)** — Declares the public façade functions `default_logger()`, `default_logger_raw()`, and `set_default_logger()`.
- **[`include/spdlog/spdlog-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog-inl.h)** — Implements the inline wrappers that forward calls to the registry singleton.
- **[`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h)** — Defines the `registry` class interface, including the `default_logger_` pointer and registration methods.
- **[`include/spdlog/details/registry-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry-inl.h)** — Contains the template instantiation logic for storing, swapping, and resetting the default logger in the `loggers_` map.
- **[`include/spdlog/logger.h`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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`](https://github.com/gabime/spdlog/blob/main/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)`.