diff options
Diffstat (limited to 'src/zencore')
| -rw-r--r-- | src/zencore/include/zencore/logbase.h | 44 | ||||
| -rw-r--r-- | src/zencore/include/zencore/logging.h | 6 | ||||
| -rw-r--r-- | src/zencore/include/zencore/logging/logger.h | 33 | ||||
| -rw-r--r-- | src/zencore/include/zencore/logging/registry.h | 4 | ||||
| -rw-r--r-- | src/zencore/logging.cpp | 164 | ||||
| -rw-r--r-- | src/zencore/logging/registry.cpp | 61 | ||||
| -rw-r--r-- | src/zencore/memory/memory.cpp | 20 | ||||
| -rw-r--r-- | src/zencore/testutils.cpp | 2 |
8 files changed, 246 insertions, 88 deletions
diff --git a/src/zencore/include/zencore/logbase.h b/src/zencore/include/zencore/logbase.h index ece17a85e..ad2ab218d 100644 --- a/src/zencore/include/zencore/logbase.h +++ b/src/zencore/include/zencore/logbase.h @@ -2,8 +2,10 @@ #pragma once -#include <string_view> +#include <zenbase/refcount.h> +#include <atomic> +#include <string_view> namespace zen::logging { enum LogLevel : int @@ -54,6 +56,29 @@ struct LogPoint class Logger; +/** This is the base class for all loggers + + There's only one derived type - `Logger`. The reason this + exists is just to try and hide the full Logger definition + but once that is lightweight enough it may not be necessary + to have this split + */ +class LoggerBase : public TRefCounted<Logger> +{ +public: + bool ShouldLog(LogLevel InLevel) const { return InLevel >= m_Level.load(std::memory_order_relaxed); } + + void SetLevel(LogLevel InLevel) { m_Level.store(InLevel, std::memory_order_relaxed); } + LogLevel GetLevel() const { return m_Level.load(std::memory_order_relaxed); } + + void SetFlushLevel(LogLevel InLevel) { m_FlushLevel.store(InLevel, std::memory_order_relaxed); } + LogLevel GetFlushLevel() const { return m_FlushLevel.load(std::memory_order_relaxed); } + +protected: + std::atomic<LogLevel> m_Level{Info}; + std::atomic<LogLevel> m_FlushLevel{Off}; +}; + } // namespace zen::logging namespace zen { @@ -65,24 +90,25 @@ namespace zen { struct LoggerRef { LoggerRef() = default; - LoggerRef(logging::Logger& InLogger) : m_Logger(&InLogger) {} + explicit LoggerRef(logging::Logger& InLogger); // This exists so that logging macros can pass LoggerRef or LogCategory // to ZEN_LOG without needing to know which one it is LoggerRef Logger() { return *this; } - bool ShouldLog(logging::LogLevel Level) const; inline operator bool() const { return m_Logger != nullptr; } - inline logging::Logger* operator->() const { return m_Logger; } - inline logging::Logger& operator*() const { return *m_Logger; } + inline logging::Logger* operator->() const; + inline logging::Logger& operator*() const; + + bool ShouldLog(logging::LogLevel Level) const { return m_Logger->ShouldLog(Level); } + void SetLogLevel(logging::LogLevel NewLogLevel) { m_Logger->SetLevel(NewLogLevel); } + logging::LogLevel GetLogLevel() { return m_Logger->GetLevel(); } - void SetLogLevel(logging::LogLevel NewLogLevel); - logging::LogLevel GetLogLevel(); - void Flush(); + void Flush(); private: - logging::Logger* m_Logger = nullptr; + logging::LoggerBase* m_Logger = nullptr; }; } // namespace zen diff --git a/src/zencore/include/zencore/logging.h b/src/zencore/include/zencore/logging.h index 4b593c19e..dc5e05656 100644 --- a/src/zencore/include/zencore/logging.h +++ b/src/zencore/include/zencore/logging.h @@ -74,7 +74,11 @@ extern LoggerRef TheDefaultLogger; * Typically, classes which want to log to its own channel will declare a Log() * member function which returns a LoggerRef created at construction time. */ -LoggerRef Log(); +inline LoggerRef +Log() +{ + return TheDefaultLogger; +} using logging::ConsoleLog; using logging::ErrorLog; diff --git a/src/zencore/include/zencore/logging/logger.h b/src/zencore/include/zencore/logging/logger.h index 39d1139a5..c94bc58fa 100644 --- a/src/zencore/include/zencore/logging/logger.h +++ b/src/zencore/include/zencore/logging/logger.h @@ -17,7 +17,7 @@ public: virtual void HandleError(const std::string_view& Msg) = 0; }; -class Logger : public RefCounted +class Logger final : public LoggerBase { public: Logger(std::string_view InName, SinkPtr InSink); @@ -29,14 +29,6 @@ public: void Log(const LogPoint& Point, fmt::format_args Args); - bool ShouldLog(LogLevel InLevel) const { return InLevel >= m_Level.load(std::memory_order_relaxed); } - - void SetLevel(LogLevel InLevel) { m_Level.store(InLevel, std::memory_order_relaxed); } - LogLevel GetLevel() const { return m_Level.load(std::memory_order_relaxed); } - - void SetFlushLevel(LogLevel InLevel) { m_FlushLevel.store(InLevel, std::memory_order_relaxed); } - LogLevel GetFlushLevel() const { return m_FlushLevel.load(std::memory_order_relaxed); } - std::string_view Name() const; void SetSinks(std::vector<SinkPtr> InSinks); @@ -56,8 +48,27 @@ private: struct Impl; std::unique_ptr<Impl> m_Impl; - std::atomic<LogLevel> m_Level{Info}; - std::atomic<LogLevel> m_FlushLevel{Off}; }; } // namespace zen::logging + +namespace zen { + +// These can't be defined next to the LoggerRef declaration since +// they depend on knowing about logging::Logger above. But it's fine +// since to be able to use these you have to include this header +// anyway + +inline logging::Logger* +LoggerRef::operator->() const +{ + return static_cast<logging::Logger*>(m_Logger); +} + +inline logging::Logger& +LoggerRef::operator*() const +{ + return *static_cast<logging::Logger*>(m_Logger); +} + +} // namespace zen diff --git a/src/zencore/include/zencore/logging/registry.h b/src/zencore/include/zencore/logging/registry.h index a4d3692d2..71e84d75f 100644 --- a/src/zencore/include/zencore/logging/registry.h +++ b/src/zencore/include/zencore/logging/registry.h @@ -13,6 +13,10 @@ namespace zen::logging { +// Match a logger name against a pattern that may contain '*' wildcards. +// Supports '*' anywhere in the pattern (e.g. "proxy*", "*store", "zen*store"). +bool MatchLoggerPattern(std::string_view Pattern, std::string_view Name); + class Registry { public: diff --git a/src/zencore/logging.cpp b/src/zencore/logging.cpp index 099518637..900da6c6d 100644 --- a/src/zencore/logging.cpp +++ b/src/zencore/logging.cpp @@ -25,16 +25,6 @@ namespace zen { LoggerRef TheDefaultLogger; -LoggerRef -Log() -{ - if (TheDefaultLogger) - { - return TheDefaultLogger; - } - return zen::logging::ConsoleLog(); -} - } // namespace zen namespace zen::logging { @@ -66,6 +56,11 @@ EmitLogMessage(LoggerRef& Logger, const LogPoint& Lp, fmt::format_args Args) { ZEN_MEMSCOPE(ELLMTag::Logging); + if (!Logger) + { + Logger = zen::logging::ConsoleLog(); + } + Logger->Log(Lp, Args); if (IsErrorLevel(Lp.Level)) @@ -149,6 +144,52 @@ ConfigureLogLevels(LogLevel Level, std::string_view Loggers) LogLevels[Level] = Loggers; } +// Iterate all configured per-logger level overrides, calling Fn(LoggerName, Level) for each entry. +// Caller must hold LogLevelsLock (shared or exclusive). +template<typename Fn> +static void +ForEachConfiguredLevel(Fn&& Callback) +{ + for (int i = 0; i < LogLevelCount; ++i) + { + LogLevel Level = LogLevel(i); + std::string_view Spec = LogLevels[i]; + + while (!Spec.empty()) + { + std::string_view Entry; + if (auto CommaPos = Spec.find_first_of(','); CommaPos != std::string_view::npos) + { + Entry = Spec.substr(0, CommaPos); + Spec.remove_prefix(CommaPos + 1); + } + else + { + Entry = Spec; + Spec = {}; + } + + Callback(Entry, Level); + } + } +} + +// Find the configured level override for a named logger, if any. +// Patterns may contain wildcards (e.g. "proxy*" matches "proxy" and "proxy.http"). +// Caller must hold LogLevelsLock (shared or exclusive). +static std::optional<LogLevel> +FindConfiguredLevel(std::string_view Name) +{ + std::optional<LogLevel> Result; + ForEachConfiguredLevel([&](std::string_view Pattern, LogLevel Level) { + if (MatchLoggerPattern(Pattern, Name)) + { + Result = Level; + } + }); + return Result; +} + void RefreshLogLevels(LogLevel* DefaultLevel) { @@ -158,31 +199,7 @@ RefreshLogLevels(LogLevel* DefaultLevel) { RwLock::SharedLockScope _(LogLevelsLock); - - for (int i = 0; i < LogLevelCount; ++i) - { - LogLevel CurrentLevel{i}; - - std::string_view Spec = LogLevels[i]; - - while (!Spec.empty()) - { - std::string LoggerName; - - if (auto CommaPos = Spec.find_first_of(','); CommaPos != std::string_view::npos) - { - LoggerName = Spec.substr(0, CommaPos); - Spec.remove_prefix(CommaPos + 1); - } - else - { - LoggerName = Spec; - Spec = {}; - } - - Levels.emplace_back(std::move(LoggerName), CurrentLevel); - } - } + ForEachConfiguredLevel([&](std::string_view Entry, LogLevel Level) { Levels.emplace_back(std::string(Entry), Level); }); } Registry::Instance().SetLevels(Levels, DefaultLevel); @@ -276,11 +293,20 @@ Get(std::string_view Name) { FoundLogger = Default()->Clone(std::string(Name)); Registry::Instance().Register(FoundLogger); + + // Apply any per-logger level override that was configured before this + // logger was created (e.g. via --log-debug=proxy). + RwLock::SharedLockScope LevelsLock(LogLevelsLock); + std::optional<LogLevel> Override = FindConfiguredLevel(Name); + if (Override) + { + FoundLogger->SetLevel(*Override); + } } }); } - return *FoundLogger; + return LoggerRef(*FoundLogger); } std::once_flag ConsoleInitFlag; @@ -349,7 +375,7 @@ ConsoleLog() } }); - return *ConLogger; + return LoggerRef(*ConLogger); } void @@ -367,7 +393,7 @@ InitializeLogging() { ZEN_MEMSCOPE(ELLMTag::Logging); - TheDefaultLogger = *Registry::Instance().DefaultLoggerRaw(); + TheDefaultLogger = LoggerRef(*Registry::Instance().DefaultLoggerRaw()); } void @@ -416,28 +442,14 @@ FlushLogging() namespace zen { -bool -LoggerRef::ShouldLog(logging::LogLevel Level) const +LoggerRef::LoggerRef(logging::Logger& InLogger) : m_Logger(static_cast<logging::LoggerBase*>(&InLogger)) { - return m_Logger->ShouldLog(Level); -} - -void -LoggerRef::SetLogLevel(logging::LogLevel NewLogLevel) -{ - m_Logger->SetLevel(NewLogLevel); -} - -logging::LogLevel -LoggerRef::GetLogLevel() -{ - return m_Logger->GetLevel(); } void LoggerRef::Flush() { - m_Logger->Flush(); + (*this)->Flush(); } thread_local ScopedActivityBase* t_ScopeStack = nullptr; @@ -548,6 +560,50 @@ TEST_CASE("simple.bread") } } +TEST_CASE("MatchLoggerPattern") +{ + // Exact match + CHECK(logging::MatchLoggerPattern("proxy", "proxy")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy", "prox")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy", "proxyz")); + + // Trailing wildcard + CHECK(logging::MatchLoggerPattern("proxy*", "proxy")); + CHECK(logging::MatchLoggerPattern("proxy*", "proxy.http")); + CHECK(logging::MatchLoggerPattern("proxy*", "proxy.anything")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy*", "prox")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy*", "other")); + + // Leading wildcard + CHECK(logging::MatchLoggerPattern("*store", "store")); + CHECK(logging::MatchLoggerPattern("*store", "buildstore")); + CHECK_FALSE(logging::MatchLoggerPattern("*store", "stores")); + + // Wildcard in the middle + CHECK(logging::MatchLoggerPattern("zen*store", "zenstore")); + CHECK(logging::MatchLoggerPattern("zen*store", "zen.buildstore")); + CHECK_FALSE(logging::MatchLoggerPattern("zen*store", "zen.stores")); + + // Multiple wildcards + CHECK(logging::MatchLoggerPattern("*proxy*", "proxy")); + CHECK(logging::MatchLoggerPattern("*proxy*", "proxy.http")); + CHECK(logging::MatchLoggerPattern("*proxy*", "myproxy.http")); + CHECK_FALSE(logging::MatchLoggerPattern("*proxy*", "other")); + + // Single-char wildcard + CHECK(logging::MatchLoggerPattern("prox?", "proxy")); + CHECK_FALSE(logging::MatchLoggerPattern("prox?", "prox")); + CHECK_FALSE(logging::MatchLoggerPattern("prox?", "proxyy")); + + // Star matches empty + CHECK(logging::MatchLoggerPattern("*", "anything")); + CHECK(logging::MatchLoggerPattern("*", "")); + + // Empty pattern + CHECK(logging::MatchLoggerPattern("", "")); + CHECK_FALSE(logging::MatchLoggerPattern("", "notempty")); +} + TEST_SUITE_END(); #endif diff --git a/src/zencore/logging/registry.cpp b/src/zencore/logging/registry.cpp index 3ed1fb0df..383a5d8ba 100644 --- a/src/zencore/logging/registry.cpp +++ b/src/zencore/logging/registry.cpp @@ -13,6 +13,44 @@ namespace zen::logging { +bool +MatchLoggerPattern(std::string_view Pattern, std::string_view Name) +{ + size_t Pi = 0; + size_t Ni = 0; + size_t StarPi = std::string_view::npos; + size_t StarNi = 0; + + while (Ni < Name.size()) + { + if (Pi < Pattern.size() && (Pattern[Pi] == Name[Ni] || Pattern[Pi] == '?')) + { + ++Pi; + ++Ni; + } + else if (Pi < Pattern.size() && Pattern[Pi] == '*') + { + StarPi = Pi++; + StarNi = Ni; + } + else if (StarPi != std::string_view::npos) + { + Pi = StarPi + 1; + Ni = ++StarNi; + } + else + { + return false; + } + } + + while (Pi < Pattern.size() && Pattern[Pi] == '*') + { + ++Pi; + } + return Pi == Pattern.size(); +} + struct Registry::Impl { Impl() @@ -95,12 +133,27 @@ struct Registry::Impl } } - for (auto& [LoggerName, Level] : Levels) + for (auto& [Pattern, Level] : Levels) { - auto It = m_Loggers.find(LoggerName); - if (It != m_Loggers.end()) + if (Pattern.find_first_of("*?") == std::string::npos) { - It->second->SetLevel(Level); + // Exact match — fast path via map lookup. + auto It = m_Loggers.find(Pattern); + if (It != m_Loggers.end()) + { + It->second->SetLevel(Level); + } + } + else + { + // Wildcard pattern — iterate all loggers. + for (auto& [Name, CurLogger] : m_Loggers) + { + if (MatchLoggerPattern(Pattern, Name)) + { + CurLogger->SetLevel(Level); + } + } } } } diff --git a/src/zencore/memory/memory.cpp b/src/zencore/memory/memory.cpp index fc8de5d02..9e19c5db7 100644 --- a/src/zencore/memory/memory.cpp +++ b/src/zencore/memory/memory.cpp @@ -1,5 +1,7 @@ // Copyright Epic Games, Inc. All Rights Reserved. +#include <cstdlib> + #include <zencore/commandline.h> #include <zencore/memory/fmalloc.h> #include <zencore/memory/mallocansi.h> @@ -42,14 +44,6 @@ InitGMalloc() // when using sanitizers, we should use the default/ansi allocator #if ZEN_OVERRIDE_NEW_DELETE - -# if ZEN_MIMALLOC_ENABLED - if (Malloc == MallocImpl::None) - { - Malloc = MallocImpl::Mimalloc; - } -# endif - # if ZEN_RPMALLOC_ENABLED if (Malloc == MallocImpl::None) { @@ -107,6 +101,16 @@ InitGMalloc() } }; + // Check ZEN_MALLOC environment variable (overrides compile-time default, but not command line) + { + const char* EnvMalloc = getenv("ZEN_MALLOC"); + if (EnvMalloc != nullptr) + { + ProcessMallocArg(std::string_view(EnvMalloc)); + } + } + + // Command line --malloc takes highest precedence IterateCommandlineArgs(ProcessArg); switch (Malloc) diff --git a/src/zencore/testutils.cpp b/src/zencore/testutils.cpp index 0cd3f8121..c9908aec8 100644 --- a/src/zencore/testutils.cpp +++ b/src/zencore/testutils.cpp @@ -46,7 +46,7 @@ ScopedTemporaryDirectory::~ScopedTemporaryDirectory() IoBuffer CreateRandomBlob(uint64_t Size) { - thread_local FastRandom Rand{.Seed = 0x7CEBF54E45B9F5D1}; + thread_local FastRandom Rand{.Seed = 0x7CEBF54E45B9F5D1 - uint64_t(GetCurrentThreadId())}; return CreateRandomBlob(Rand, Size); }; |