diff --git a/core/foundation/inc/ROOT/RLogger.hxx b/core/foundation/inc/ROOT/RLogger.hxx index dd6e020688619..8620bdd4c9108 100644 --- a/core/foundation/inc/ROOT/RLogger.hxx +++ b/core/foundation/inc/ROOT/RLogger.hxx @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -99,7 +100,8 @@ class RLogChannel : public RLogDiagCount { std::string fName; /// Verbosity of this channel. By default, use the global verbosity. - ELogLevel fVerbosity = ELogLevel::kUnset; + mutable ELogLevel fVerbosity = ELogLevel::kUnset; + mutable bool fVerbosityInitialized{false}; public: /// Construct an anonymous channel. @@ -114,6 +116,7 @@ public: ELogLevel SetVerbosity(ELogLevel verbosity) { std::swap(fVerbosity, verbosity); + fVerbosityInitialized = true; return verbosity; } ELogLevel GetVerbosity() const { return fVerbosity; } @@ -142,6 +145,8 @@ public: static RLogManager &Get(); + ELogLevel GetConfiguredVerbosity(const std::string &name) const; + /// Add a RLogHandler in the front - to be called before all others. void PushFront(std::unique_ptr handler) { fHandlers.emplace_front(std::move(handler)); } @@ -294,6 +299,7 @@ public: }; namespace Internal { +void ParseRootLogStr(const std::string& env, std::map& out); inline RLogChannel &GetChannelOrManager() { @@ -308,6 +314,12 @@ inline RLogChannel &GetChannelOrManager(RLogChannel &channel) inline ELogLevel RLogChannel::GetEffectiveVerbosity(const RLogManager &mgr) const { + if (!fVerbosityInitialized) { + fVerbosityInitialized = true; + ELogLevel cfg = mgr.GetConfiguredVerbosity(fName); + if (cfg != ELogLevel::kUnset) + fVerbosity = cfg; + } if (fVerbosity == ELogLevel::kUnset) return mgr.GetVerbosity(); return fVerbosity; diff --git a/core/foundation/src/RLogger.cxx b/core/foundation/src/RLogger.cxx index 9c0c39ba2123c..3c53548c4f581 100644 --- a/core/foundation/src/RLogger.cxx +++ b/core/foundation/src/RLogger.cxx @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include // pin vtable ROOT::RLogHandler::~RLogHandler() {} @@ -54,14 +57,84 @@ inline bool RLogHandlerDefault::Emit(const ROOT::RLogEntry &entry) entry.fMessage.c_str()); return true; } + +static ROOT::ELogLevel ParseVerbosityStr(const std::string &str) { + if (str == "Fatal") return ROOT::ELogLevel::kFatal; + if (str == "Error") return ROOT::ELogLevel::kError; + if (str == "Warning") return ROOT::ELogLevel::kWarning; + if (str == "Info") return ROOT::ELogLevel::kInfo; + if (str.compare(0, 5, "Debug") == 0) { + if (str.length() > 6 && str[5] == '(' && str.back() == ')') { + int level = std::stoi(str.substr(6, str.length() - 7)); + return static_cast(static_cast(ROOT::ELogLevel::kDebug) + level); + } + return ROOT::ELogLevel::kDebug; + } + return ROOT::ELogLevel::kUnset; +} + +namespace ROOT { +namespace Internal { + +void ParseRootLogStr(const std::string& env, std::map& sChannelVerbosities) { + std::string s = env; + size_t start = 0; + size_t end = s.find(','); + while (start != std::string::npos) { + std::string part = s.substr(start, end - start); + size_t eq = part.find('='); + if (eq != std::string::npos) { + sChannelVerbosities[part.substr(0, eq)] = ParseVerbosityStr(part.substr(eq + 1)); + } else { + sChannelVerbosities[""] = ParseVerbosityStr(part); + } + if (end == std::string::npos) break; + start = end + 1; + end = s.find(',', start); + } +} + +} // namespace Internal +} // namespace ROOT + +static std::map& GetChannelVerbosities() { + static std::map sChannelVerbosities; + static bool parsed = false; + if (!parsed) { + parsed = true; + const char *env = std::getenv("ROOT_LOG"); + if (env) { + ROOT::Internal::ParseRootLogStr(env, sChannelVerbosities); + } + } + return sChannelVerbosities; +} + } // unnamed namespace ROOT::RLogManager &ROOT::RLogManager::Get() { static RLogManager instance(std::make_unique()); + static bool configured = false; + if (!configured) { + configured = true; + auto& cfg = GetChannelVerbosities(); + if (cfg.count("")) { + instance.SetVerbosity(cfg[""]); + } + } return instance; } +ROOT::ELogLevel ROOT::RLogManager::GetConfiguredVerbosity(const std::string &name) const +{ + auto& cfg = GetChannelVerbosities(); + auto it = cfg.find(name); + if (it != cfg.end()) + return it->second; + return ROOT::ELogLevel::kUnset; +} + std::unique_ptr ROOT::RLogManager::Remove(RLogHandler *handler) { auto iter = std::find_if(fHandlers.begin(), fHandlers.end(), [&](const std::unique_ptr &handlerPtr) { diff --git a/core/foundation/test/testLogger.cxx b/core/foundation/test/testLogger.cxx index 0c9be1260d364..b5f91e65a0471 100644 --- a/core/foundation/test/testLogger.cxx +++ b/core/foundation/test/testLogger.cxx @@ -6,6 +6,7 @@ #include #include +#include struct TestLogger { class Handler : public ROOT::RLogHandler { @@ -305,3 +306,14 @@ TEST(Logger, ROOTErrorHandlerDiagString) SetErrorHandler(prevErrorHandler); } + +TEST(Logger, EnvVarParsing) +{ + std::map cfg; + ROOT::Internal::ParseRootLogStr("ROOT.InterpreterPerf=Debug(3),ROOT.RBrowser=Error,Info", cfg); + EXPECT_EQ(cfg.size(), 3); + EXPECT_EQ(cfg[""], ROOT::ELogLevel::kInfo); + EXPECT_EQ(cfg["ROOT.RBrowser"], ROOT::ELogLevel::kError); + EXPECT_EQ(cfg["ROOT.InterpreterPerf"], static_cast(static_cast(ROOT::ELogLevel::kDebug) + 3)); +} +