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

> Learn how to replace the default logger in spdlog using set_default_logger. Customize spdlog globally for your C++ projects with this comprehensive guide.

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

---

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

```cpp
#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`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h):

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

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

- **[`include/spdlog/spdlog.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/spdlog.h)** – Declares the public 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 to the registry.
- **[`include/spdlog/details/registry.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry.h)** – Defines the registry interface, including the `default_logger()` and `set_default_logger()` methods.
- **[`include/spdlog/details/registry-inl.h`](https://github.com/gabime/spdlog/blob/main/include/spdlog/details/registry-inl.h)** – Contains the actual 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 how a logger is constructed and how sinks are attached when building a replacement.

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