How to Register and Retrieve Loggers by Name in spdlog

spdlog maintains a thread‑safe global registry that maps logger names to std::shared_ptr<spdlog::logger> instances, providing spdlog::register_logger(), spdlog::register_or_replace(), and spdlog::get() for centralized logger management.

The gabime/spdlog library implements a singleton registry pattern that enables you to register and retrieve loggers by name throughout your application. This architecture, defined in include/spdlog/details/registry.h, stores loggers in an internal std::unordered_map protected by a mutex, ensuring safe concurrent access while allowing you to share logging instances across modules using string identifiers.

Understanding the Logger Registry Architecture

At the core of spdlog's naming system is the spdlog::details::registry class, implemented across include/spdlog/details/registry.h and registry-inl.h. The registry maintains a private std::unordered_map<std::string, std::shared_ptr<logger>> loggers_ that serves as the authoritative lookup table for all named loggers.

You interact with this singleton through public API functions declared in include/spdlog/spdlog.h. The registry instance is acquired via spdlog::details::registry::instance(), though you typically call the wrapper functions directly rather than accessing the singleton yourself.

How to Register Loggers by Name

Automatic Registration via Factory Functions

When you create a logger using spdlog::create<> (or convenience functions like spdlog::basic_logger_mt), spdlog automatically registers the new instance if the automatic_registration_ flag is enabled (default: true). This behavior is controlled by spdlog::set_automatic_registration(bool).

// Automatically registered under the name "file_log"
auto file_logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("file_log", "application.log");

Manual Registration with register_logger

For loggers constructed manually or received from external sources, use spdlog::register_logger(). This function forwards to registry::register_logger, which inserts the shared pointer into the internal map.

auto custom_logger = std::make_shared<spdlog::logger>("network_logger",
    std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
spdlog::register_logger(custom_logger);  // Throws spdlog_ex if name exists

Critical behavior: If a logger with the same name already exists, register_logger throws a spdlog_ex exception. Always use this method when you want to ensure no accidental overwrites occur.

Replacing Existing Loggers with register_or_replace

To overwrite an existing logger without handling exceptions, call spdlog::register_or_replace(). This invokes registry::register_or_replace, which updates the map entry regardless of whether the name already exists.

auto new_logger = std::make_shared<spdlog::logger>("network_logger",
    std::make_shared<spdlog::sinks::stderr_sink_mt>());
spdlog::register_or_replace(new_logger);  // Silently replaces previous "network_logger"

How to Retrieve Loggers by Name

To obtain a registered logger, use spdlog::get("logger_name"), which returns the result of registry::instance().get(name). The function looks up the name in the internal map and returns a std::shared_ptr<spdlog::logger> or nullptr if the name is not found.

// Retrieve and use
if (auto logger = spdlog::get("network_logger")) {
    logger->info("Connection established");
}

// Safe retrieval with null check
auto maybe_logger = spdlog::get("unknown_logger");
if (!maybe_logger) {
    spdlog::error("Logger not found in registry");
}

Thread Safety and Concurrent Access

The spdlog registry guarantees thread safety for all public operations through a private std::mutex logger_map_mutex_. Whether you are registering, replacing, or retrieving loggers by name, the internal mutex ensures safe concurrent access from multiple threads.

This means you can safely call spdlog::get() from worker threads while the main thread registers new loggers, without implementing additional synchronization in your application code.

Practical Code Examples

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

void setup_logging() {
    // 1. Create and auto-register a file logger
    auto file_logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>(
        "file_log", "logs.txt");
    
    // 2. Manual registration of a custom configured logger
    auto custom_logger = std::make_shared<spdlog::logger>("my_logger",
        std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
    spdlog::register_logger(custom_logger);
    
    // 3. Replace an existing logger (no exception thrown)
    auto new_logger = std::make_shared<spdlog::logger>("my_logger",
        std::make_shared<spdlog::sinks::stderr_sink_mt>());
    spdlog::register_or_replace(new_logger);
    
    // 4. Retrieve by name and log
    if (auto logger = spdlog::get("my_logger")) {
        logger->info("Hello from {}", logger->name());
    }
    
    // 5. Handle missing loggers safely
    auto unknown = spdlog::get("nonexistent");
    if (!unknown) {
        spdlog::error("Logger not registered");
    }
}

Summary

  • spdlog uses a singleton registry (spdlog::details::registry) to manage a global map of named loggers, implemented in include/spdlog/details/registry.h.
  • Register loggers using spdlog::register_logger() (throws on duplicates) or spdlog::register_or_replace() (overwrites existing).
  • Retrieve loggers using spdlog::get("name"), which returns a shared_ptr or nullptr if the name is not found.
  • Automatic registration occurs by default when using spdlog::create<>, but can be disabled via spdlog::set_automatic_registration(false).
  • All registry operations are thread-safe, protected by an internal mutex allowing concurrent registration and retrieval across threads.

Frequently Asked Questions

What happens if I try to register a logger with a name that already exists?

Calling spdlog::register_logger() throws a spdlog_ex exception if the name is already present in the registry. To avoid this, use spdlog::register_or_replace(), which silently overwrites the existing entry with the new logger instance.

How do I disable automatic registration when creating loggers?

Call spdlog::set_automatic_registration(false) before using factory functions like spdlog::create<>. When disabled, newly created loggers will not be added to the global registry, allowing you to manage logger lifecycles manually or keep them as private instances.

Is the spdlog registry thread-safe for concurrent get and register operations?

Yes. According to the source code in registry-inl.h, all public registry methods lock a private std::mutex logger_map_mutex_ before accessing the internal map. This ensures that multiple threads can safely call spdlog::get(), spdlog::register_logger(), and spdlog::register_or_replace() concurrently without data races.

What is the performance cost of retrieving loggers by name?

Retrieval performs a lookup in an std::unordered_map<std::string, std::shared_ptr<logger>> while holding a mutex lock. For high‑performance scenarios requiring frequent access, store the returned shared_ptr locally rather than repeatedly calling spdlog::get(), though the mutex overhead is minimal for typical logging patterns.

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 →