Skip to content

Commit

Permalink
Added Mapped Diagnostic Context (MDC) support
Browse files Browse the repository at this point in the history
  • Loading branch information
massimiliano96 committed Mar 6, 2024
1 parent 5370076 commit ae059fa
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
26 changes: 26 additions & 0 deletions include/spdlog/pattern_formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <unordered_map>
#include <map>
#include <vector>

#include "./common.h"
Expand Down Expand Up @@ -57,6 +58,31 @@ class SPDLOG_API custom_flag_formatter : public details::flag_formatter {
void set_padding_info(const details::padding_info &padding) { flag_formatter::padinfo_ = padding; }
};

class SPDLOG_API MDC {
public:
static void put(const std::string &key, const std::string &value) {
get_context()[key] = value;
}

static std::string get(const std::string &key) {
auto &context = get_context();
auto it = context.find(key);
if (it != context.end()) {
return it->second;
}
return "";
}

static void remove(const std::string &key) { get_context().erase(key); }

static void clear() { get_context().clear(); }

static std::map<std::string, std::string> &get_context() {
static thread_local std::map<std::string, std::string> context;
return context;
}
};

class SPDLOG_API pattern_formatter final : public formatter {
public:
using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
Expand Down
28 changes: 28 additions & 0 deletions src/pattern_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,30 @@ class full_formatter final : public flag_formatter {
memory_buf_t cached_datetime_;
};

template <typename ScopedPadder>
class mdc_formatter : public flag_formatter {
public:
explicit mdc_formatter(padding_info padinfo)
: flag_formatter(padinfo) {}

void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override {
auto mdc_map = MDC::get_context();
if (!mdc_map.empty()) {
auto last_element = *(--mdc_map.end());
for (const auto &pair : mdc_map) {
std::string mdc_content_;
if (pair != last_element) {
mdc_content_ = pair.first + ':' + pair.second + ' ';
} else {
mdc_content_ = pair.first + ':' + pair.second;
}
ScopedPadder p(mdc_content_.size(), padinfo_, dest);
fmt_helper::append_string_view(mdc_content_, dest);
}
}
}
};

} // namespace details

pattern_formatter::pattern_formatter(std::string pattern,
Expand Down Expand Up @@ -1096,6 +1120,10 @@ void pattern_formatter::handle_flag_(char flag, details::padding_info padding) {
formatters_.push_back(std::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));
break;

case ('&'):
formatters_.push_back(details::make_unique<details::mdc_formatter<Padder>>(padding));
break;

default: // Unknown flag appears as is
auto unknown_flag = std::make_unique<details::aggregate_formatter>();

Expand Down
128 changes: 128 additions & 0 deletions tests/test_pattern_formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,131 @@ TEST_CASE("override need_localtime", "[pattern_formatter]") {
REQUIRE(to_string_view(formatted) == oss.str());
}
}

TEST_CASE("MDC formatter test-1", "[pattern_formatter]") {
spdlog::MDC::put("mdc_key_1", "mdc_value_1");
spdlog::MDC::put("mdc_key_2", "mdc_value_2");

auto formatter = std::make_shared<spdlog::pattern_formatter>();
formatter->set_pattern("[%n] [%l] [%&] %v");

memory_buf_t formatted;
spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
"some message");
formatter->format(msg, formatted);

auto expected = spdlog::fmt_lib::format(
"[logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message{}",
spdlog::details::os::default_eol);
REQUIRE(to_string_view(formatted) == expected);

SECTION("Tear down") { spdlog::MDC::clear(); }
}

TEST_CASE("MDC formatter value update", "[pattern_formatter]") {
spdlog::MDC::put("mdc_key_1", "mdc_value_1");
spdlog::MDC::put("mdc_key_2", "mdc_value_2");

auto formatter = std::make_shared<spdlog::pattern_formatter>();
formatter->set_pattern("[%n] [%l] [%&] %v");

memory_buf_t formatted_1;
spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
"some message");
formatter->format(msg, formatted_1);

auto expected = spdlog::fmt_lib::format(
"[logger-name] [info] [mdc_key_1:mdc_value_1 mdc_key_2:mdc_value_2] some message{}",
spdlog::details::os::default_eol);

REQUIRE(to_string_view(formatted_1) == expected);

spdlog::MDC::put("mdc_key_1", "new_mdc_value_1");
memory_buf_t formatted_2;
formatter->format(msg, formatted_2);
expected = spdlog::fmt_lib::format(
"[logger-name] [info] [mdc_key_1:new_mdc_value_1 mdc_key_2:mdc_value_2] some message{}",
spdlog::details::os::default_eol);

REQUIRE(to_string_view(formatted_2) == expected);

SECTION("Tear down") { spdlog::MDC::clear(); }
}

TEST_CASE("MDC different threads", "[pattern_formatter]") {
auto formatter = std::make_shared<spdlog::pattern_formatter>();
formatter->set_pattern("[%n] [%l] [%&] %v");
spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
"some message");

memory_buf_t formatted_2;

auto lambda_1 = [formatter, msg]() {
spdlog::MDC::put("mdc_key", "thread_1_id");
memory_buf_t formatted;
formatter->format(msg, formatted);

auto expected =
spdlog::fmt_lib::format("[logger-name] [info] [mdc_key:thread_1_id] some message{}",
spdlog::details::os::default_eol);

REQUIRE(to_string_view(formatted) == expected);
};

auto lambda_2 = [formatter, msg]() {
spdlog::MDC::put("mdc_key", "thread_2_id");
memory_buf_t formatted;
formatter->format(msg, formatted);

auto expected =
spdlog::fmt_lib::format("[logger-name] [info] [mdc_key:thread_2_id] some message{}",
spdlog::details::os::default_eol);

REQUIRE(to_string_view(formatted) == expected);
};

std::thread thread_1(lambda_1);
std::thread thread_2(lambda_2);

thread_1.join();
thread_2.join();

SECTION("Tear down") { spdlog::MDC::clear(); }
}

TEST_CASE("MDC remove key", "[pattern_formatter]") {
spdlog::MDC::put("mdc_key_1", "mdc_value_1");
spdlog::MDC::put("mdc_key_2", "mdc_value_2");
spdlog::MDC::remove("mdc_key_1");

auto formatter = std::make_shared<spdlog::pattern_formatter>();
formatter->set_pattern("[%n] [%l] [%&] %v");

memory_buf_t formatted;
spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
"some message");
formatter->format(msg, formatted);

auto expected =
spdlog::fmt_lib::format("[logger-name] [info] [mdc_key_2:mdc_value_2] some message{}",
spdlog::details::os::default_eol);
REQUIRE(to_string_view(formatted) == expected);

SECTION("Tear down") { spdlog::MDC::clear(); }
}

TEST_CASE("MDC empty", "[pattern_formatter]") {
auto formatter = std::make_shared<spdlog::pattern_formatter>();
formatter->set_pattern("[%n] [%l] [%&] %v");

memory_buf_t formatted;
spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info,
"some message");
formatter->format(msg, formatted);

auto expected = spdlog::fmt_lib::format("[logger-name] [info] [] some message{}",
spdlog::details::os::default_eol);
REQUIRE(to_string_view(formatted) == expected);

SECTION("Tear down") { spdlog::MDC::clear(); }
}

0 comments on commit ae059fa

Please sign in to comment.