From 566b2d14043a439ed83cd02774af44f225ecad72 Mon Sep 17 00:00:00 2001 From: SamareshSingh <97642706+ssam18@users.noreply.github.com> Date: Mon, 9 Feb 2026 13:58:20 -0600 Subject: [PATCH] Fix #3525: Make level name matching case-insensitive (#3535) Modified from_str() to perform case-insensitive comparison for all level names. This allows environment variables and SPDLOG_LEVEL_NAMES to use uppercase or mixed case level names (e.g., DEBUG, INFO, Warning) while maintaining full backward compatibility with existing lowercase names. --- include/spdlog/common-inl.h | 22 +++++++++++++++++++--- tests/test_cfg.cpp | 30 ++++++++++++++++++++++++++++++ tests/test_misc.cpp | 11 +++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/include/spdlog/common-inl.h b/include/spdlog/common-inl.h index f35901c5..77064756 100644 --- a/include/spdlog/common-inl.h +++ b/include/spdlog/common-inl.h @@ -29,15 +29,31 @@ SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOE } SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT { - auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name); + auto it = std::find_if(std::begin(level_string_views), std::end(level_string_views), + [&name](const string_view_t &level_name) { + return level_name.size() == name.size() && + std::equal(name.begin(), name.end(), level_name.begin(), + [](char a, char b) { + return std::tolower(static_cast(a)) == + std::tolower(static_cast(b)); + }); + }); if (it != std::end(level_string_views)) return static_cast(std::distance(std::begin(level_string_views), it)); // check also for "warn" and "err" before giving up.. - if (name == "warn") { + auto iequals = [](const std::string &a, const std::string &b) { + return a.size() == b.size() && + std::equal(a.begin(), a.end(), b.begin(), [](char ac, char bc) { + return std::tolower(static_cast(ac)) == + std::tolower(static_cast(bc)); + }); + }; + + if (iequals(name, "warn")) { return level::warn; } - if (name == "err") { + if (iequals(name, "err")) { return level::err; } return level::off; diff --git a/tests/test_cfg.cpp b/tests/test_cfg.cpp index 7dec94b4..cff35d33 100644 --- a/tests/test_cfg.cpp +++ b/tests/test_cfg.cpp @@ -176,3 +176,33 @@ TEST_CASE("restore-to-default", "[cfg]") { load_argv_levels(2, argv); REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); } + +TEST_CASE("uppercase-level-names", "[cfg]") { + spdlog::drop("l1"); + spdlog::drop("l2"); + +#ifdef CATCH_PLATFORM_WINDOWS + _putenv_s("SPDLOG_LEVEL", "l1=DEBUG,INFO"); +#else + setenv("SPDLOG_LEVEL", "l1=DEBUG,INFO", 1); +#endif + load_env_levels(); + auto l1 = spdlog::create("l1"); + auto l2 = spdlog::create("l2"); + + REQUIRE(l1->level() == spdlog::level::debug); + REQUIRE(l2->level() == spdlog::level::info); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); + + // Test with argv + spdlog::drop("l3"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l3=WARN,ERROR"}; + load_argv_levels(2, argv); + auto l3 = spdlog::create("l3"); + + REQUIRE(l3->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::err); + + // Reset to info + spdlog::set_level(spdlog::level::info); +} diff --git a/tests/test_misc.cpp b/tests/test_misc.cpp index ff32fed2..e855e1b2 100644 --- a/tests/test_misc.cpp +++ b/tests/test_misc.cpp @@ -73,6 +73,17 @@ TEST_CASE("to_level_enum", "[convert_to_level_enum]") { REQUIRE(spdlog::level::from_str("critical") == spdlog::level::critical); REQUIRE(spdlog::level::from_str("off") == spdlog::level::off); REQUIRE(spdlog::level::from_str("null") == spdlog::level::off); + REQUIRE(spdlog::level::from_str("TRACE") == spdlog::level::trace); + REQUIRE(spdlog::level::from_str("DEBUG") == spdlog::level::debug); + REQUIRE(spdlog::level::from_str("INFO") == spdlog::level::info); + REQUIRE(spdlog::level::from_str("WARNING") == spdlog::level::warn); + REQUIRE(spdlog::level::from_str("WARN") == spdlog::level::warn); + REQUIRE(spdlog::level::from_str("ERROR") == spdlog::level::err); + REQUIRE(spdlog::level::from_str("ERR") == spdlog::level::err); + REQUIRE(spdlog::level::from_str("CRITICAL") == spdlog::level::critical); + REQUIRE(spdlog::level::from_str("OFF") == spdlog::level::off); + REQUIRE(spdlog::level::from_str("TrAcE") == spdlog::level::trace); + REQUIRE(spdlog::level::from_str("DeBuG") == spdlog::level::debug); } TEST_CASE("periodic flush", "[periodic_flush]") {