aboutsummaryrefslogtreecommitdiff
path: root/src/zencore
diff options
context:
space:
mode:
Diffstat (limited to 'src/zencore')
-rw-r--r--src/zencore/include/zencore/logbase.h44
-rw-r--r--src/zencore/include/zencore/logging.h6
-rw-r--r--src/zencore/include/zencore/logging/logger.h33
-rw-r--r--src/zencore/include/zencore/logging/registry.h4
-rw-r--r--src/zencore/logging.cpp164
-rw-r--r--src/zencore/logging/registry.cpp61
-rw-r--r--src/zencore/memory/memory.cpp20
-rw-r--r--src/zencore/testutils.cpp2
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);
};