spdlog MDC Mapped Diagnostic Context Usage: Complete Implementation Guide
spdlog provides a thread-local Mapped Diagnostic Context (MDC) facility that stores key-value pairs via set_mdc() and injects them into log messages using the %MDC{key} pattern placeholder, eliminating the need to pass context manually to every log call.
The gabime/spdlog library includes a lightweight Mapped Diagnostic Context implementation that enables per-thread diagnostic storage. Understanding spdlog MDC Mapped Diagnostic Context usage allows you to attach request IDs, user identifiers, or transaction metadata to logging threads and automatically include this data in formatted output.
Core MDC API Functions
The MDC API resides in include/spdlog/mdc.h and operates on a per-thread basis using thread-local storage. Each thread maintains its own independent map, requiring no synchronization between threads.
Setting Context Values
Use spdlog::set_mdc(key, value) to store a string value associated with a specific key for the current thread. This function accepts two std::string parameters and updates the thread-local map.
#include <spdlog/mdc.h>
// Attach a request ID to the current thread
spdlog::set_mdc("req_id", "42a7f9c3");
Retrieving and Removing Values
Retrieve values using spdlog::get_mdc(key), which returns an optional<std::string>. If the key exists, the optional contains the value; otherwise, it is empty.
auto req_id = spdlog::get_mdc("req_id");
if (req_id) {
// Process the retrieved value
}
Remove specific keys with spdlog::remove_mdc(key) to delete individual entries from the current thread's context.
Clearing the Context
Call spdlog::clear_mdc() to remove all key-value pairs from the current thread's MDC map. This is particularly useful when recycling threads from a pool or at the end of request processing to prevent data leakage between sessions.
Configuring Log Patterns with %MDC
The pattern formatter in include/spdlog/pattern_formatter.h implements the %MDC{key} placeholder. When processing log messages, the formatter queries the current thread's MDC map for the specified key and substitutes the placeholder with the stored value or an empty string if the key is absent.
Configure your logger to use MDC placeholders in the pattern string:
auto logger = spdlog::stdout_color_mt("example");
logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%MDC{req_id}] %v");
Thread Safety and Implementation Details
The MDC implementation stores data in thread-local storage (thread_local variables), ensuring that each thread has an isolated context map. This design eliminates lock contention and makes spdlog MDC Mapped Diagnostic Context usage safe for high-concurrency applications without additional synchronization overhead.
According to the source code in include/spdlog/mdc.h, the internal implementation resides in spdlog::detail::mdc but exposes convenient wrapper functions in the main spdlog namespace.
Practical Implementation Examples
Basic Request Tracking
This example demonstrates setting a request ID, logging messages with automatic context injection, and cleaning up afterward:
#include <spdlog/spdlog.h>
#include <spdlog/mdc.h>
int main()
{
auto logger = spdlog::stdout_color_mt("example");
logger->set_pattern("[%H:%M:%S] [%l] [%MDC{req_id}] %v");
// Log without context
logger->info("Started processing");
// Set request context
spdlog::set_mdc("req_id", "42a7f9c3");
logger->info("Handling request");
// Update context mid-flow
spdlog::set_mdc("req_id", "e9d2a1b0");
logger->info("Finished request");
// Remove specific key
spdlog::remove_mdc("req_id");
logger->info("After removal");
// Clear all context
spdlog::clear_mdc();
}
Multi-Threaded Server Implementation
In server applications, each client connection typically runs on its own thread. The MDC automatically isolates context between threads:
#include <spdlog/spdlog.h>
#include <spdlog/mdc.h>
#include <thread>
void handle_client(int client_id)
{
// Set thread-specific context
spdlog::set_mdc("client_id", std::to_string(client_id));
auto logger = spdlog::get("server");
logger->info("Client connected");
// Processing occurs here...
logger->info("Client disconnected");
// Clean up thread context before thread exits
spdlog::clear_mdc();
}
int main()
{
auto logger = spdlog::stdout_color_mt("server");
logger->set_pattern("[%H:%M:%S] [client=%MDC{client_id}] %v");
std::thread t1(handle_client, 1);
std::thread t2(handle_client, 2);
t1.join();
t2.join();
}
Key Source Files
The MDC functionality spans several header files in the gabime/spdlog repository:
include/spdlog/mdc.h— Contains the public API (set_mdc,get_mdc,remove_mdc,clear_mdc) and the thread-local storage implementation.include/spdlog/pattern_formatter.h— Implements the%MDC{key}placeholder resolution that queries the MDC map during message formatting.include/spdlog/logger.h— Defines the logger interface where patterns are configured viaset_pattern().
Summary
- spdlog MDC Mapped Diagnostic Context usage enables thread-local storage of diagnostic key-value pairs accessible through pattern placeholders.
- Use
spdlog::set_mdc(),spdlog::get_mdc(),spdlog::remove_mdc(), andspdlog::clear_mdc()to manage context ininclude/spdlog/mdc.h. - Configure loggers with
%MDC{key}placeholders ininclude/spdlog/pattern_formatter.hto automatically inject context into messages. - The thread-local implementation requires no synchronization and safely isolates data between threads.
Frequently Asked Questions
How does spdlog handle missing MDC keys in pattern strings?
When a log pattern contains %MDC{key} but the key does not exist in the current thread's MDC map, the pattern formatter substitutes an empty string. This behavior ensures that log formatting continues uninterrupted even if context has not been set or was removed.
Is spdlog MDC thread-safe for concurrent access?
Yes, the MDC implementation uses thread-local storage (thread_local), meaning each thread maintains its own independent map. According to the implementation in include/spdlog/mdc.h, no locks are required because threads cannot access each other's MDC data, making it safe for high-concurrency scenarios.
Can I use multiple MDC keys in the same log pattern?
Yes, you can include multiple %MDC{key} placeholders with different keys in a single pattern string. For example: [%MDC{req_id}] [%MDC{user_id}] %v. Each placeholder resolves independently against the current thread's MDC map, allowing you to display multiple context values simultaneously.
When should I call spdlog::clear_mdc()?
Call spdlog::clear_mdc() at the end of a request lifecycle, when recycling threads from a pool, or before terminating threads. This prevents context leakage where values from one session or request inadvertently appear in subsequent operations on the same thread, ensuring clean separation between logging contexts.
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 →