// Copyright Epic Games, Inc. All Rights Reserved. #include #include #include #include #include #include #include #include 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() { // Create default logger with a stdout color sink SinkPtr DefaultSink(new AnsiColorStdoutSink()); m_DefaultLogger = Ref(new Logger("", DefaultSink)); m_Loggers[""] = m_DefaultLogger; } ~Impl() { StopPeriodicFlush(); } void Register(Ref InLogger) { std::lock_guard Lock(m_Mutex); if (m_ErrorHandler) { InLogger->SetErrorHandler(m_ErrorHandler); } m_Loggers[std::string(InLogger->Name())] = std::move(InLogger); } void Drop(const std::string& Name) { std::lock_guard Lock(m_Mutex); m_Loggers.erase(Name); } Ref Get(const std::string& Name) { std::lock_guard Lock(m_Mutex); auto It = m_Loggers.find(Name); if (It != m_Loggers.end()) { return It->second; } return {}; } void SetDefaultLogger(Ref InLogger) { std::lock_guard Lock(m_Mutex); if (InLogger) { m_Loggers[std::string(InLogger->Name())] = InLogger; } m_DefaultLogger = std::move(InLogger); } Logger* DefaultLoggerRaw() { return m_DefaultLogger.Get(); } Ref DefaultLogger() { std::lock_guard Lock(m_Mutex); return m_DefaultLogger; } void SetGlobalLevel(LogLevel Level) { m_GlobalLevel.store(Level, std::memory_order_relaxed); std::lock_guard Lock(m_Mutex); for (auto& [Name, CurLogger] : m_Loggers) { CurLogger->SetLevel(Level); } } LogLevel GetGlobalLevel() const { return m_GlobalLevel.load(std::memory_order_relaxed); } void SetLevels(Registry::LogLevels Levels, LogLevel* DefaultLevel) { std::lock_guard Lock(m_Mutex); if (DefaultLevel) { m_GlobalLevel.store(*DefaultLevel, std::memory_order_relaxed); for (auto& [Name, CurLogger] : m_Loggers) { CurLogger->SetLevel(*DefaultLevel); } } for (auto& [Pattern, Level] : Levels) { if (Pattern.find_first_of("*?") == std::string::npos) { // 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); } } } } } void FlushAll() { std::lock_guard Lock(m_Mutex); for (auto& [Name, CurLogger] : m_Loggers) { try { CurLogger->Flush(); } catch (const std::exception&) { } } } void FlushOn(LogLevel Level) { std::lock_guard Lock(m_Mutex); m_FlushLevel = Level; for (auto& [Name, CurLogger] : m_Loggers) { CurLogger->SetFlushLevel(Level); } } void FlushEvery(std::chrono::seconds Interval) { StopPeriodicFlush(); m_PeriodicFlushRunning.store(true, std::memory_order_relaxed); m_FlushThread = std::thread([this, Interval] { while (m_PeriodicFlushRunning.load(std::memory_order_relaxed)) { { std::unique_lock Lock(m_PeriodicFlushMutex); m_PeriodicFlushCv.wait_for(Lock, Interval, [this] { return !m_PeriodicFlushRunning.load(std::memory_order_relaxed); }); } if (m_PeriodicFlushRunning.load(std::memory_order_relaxed)) { FlushAll(); } } }); } void SetFormatter(std::unique_ptr InFormatter) { std::lock_guard Lock(m_Mutex); for (auto& [Name, CurLogger] : m_Loggers) { CurLogger->SetFormatter(InFormatter->Clone()); } } void ApplyAll(void (*Func)(void*, Ref), void* Context) { std::lock_guard Lock(m_Mutex); for (auto& [Name, CurLogger] : m_Loggers) { Func(Context, CurLogger); } } void SetErrorHandler(ErrorHandler* Handler) { std::lock_guard Lock(m_Mutex); m_ErrorHandler = Handler; for (auto& [Name, CurLogger] : m_Loggers) { CurLogger->SetErrorHandler(Handler); } } void Shutdown() { StopPeriodicFlush(); FlushAll(); std::lock_guard Lock(m_Mutex); m_Loggers.clear(); m_DefaultLogger = nullptr; } private: void StopPeriodicFlush() { if (m_FlushThread.joinable()) { m_PeriodicFlushRunning.store(false, std::memory_order_relaxed); { std::lock_guard Lock(m_PeriodicFlushMutex); m_PeriodicFlushCv.notify_one(); } m_FlushThread.join(); } } std::mutex m_Mutex; std::unordered_map> m_Loggers; Ref m_DefaultLogger; std::atomic m_GlobalLevel{Trace}; LogLevel m_FlushLevel{Off}; ErrorHandler* m_ErrorHandler = nullptr; // Periodic flush std::atomic m_PeriodicFlushRunning{false}; std::mutex m_PeriodicFlushMutex; std::condition_variable m_PeriodicFlushCv; std::thread m_FlushThread; }; Registry& Registry::Instance() { static Registry s_Instance; return s_Instance; } Registry::Registry() : m_Impl(std::make_unique()) { } Registry::~Registry() = default; void Registry::Register(Ref InLogger) { m_Impl->Register(std::move(InLogger)); } void Registry::Drop(const std::string& Name) { m_Impl->Drop(Name); } Ref Registry::Get(const std::string& Name) { return m_Impl->Get(Name); } void Registry::SetDefaultLogger(Ref InLogger) { m_Impl->SetDefaultLogger(std::move(InLogger)); } Logger* Registry::DefaultLoggerRaw() { return m_Impl->DefaultLoggerRaw(); } Ref Registry::DefaultLogger() { return m_Impl->DefaultLogger(); } void Registry::SetGlobalLevel(LogLevel Level) { m_Impl->SetGlobalLevel(Level); } LogLevel Registry::GetGlobalLevel() const { return m_Impl->GetGlobalLevel(); } void Registry::SetLevels(LogLevels Levels, LogLevel* DefaultLevel) { m_Impl->SetLevels(Levels, DefaultLevel); } void Registry::FlushAll() { m_Impl->FlushAll(); } void Registry::FlushOn(LogLevel Level) { m_Impl->FlushOn(Level); } void Registry::FlushEvery(std::chrono::seconds Interval) { m_Impl->FlushEvery(Interval); } void Registry::SetFormatter(std::unique_ptr InFormatter) { m_Impl->SetFormatter(std::move(InFormatter)); } void Registry::ApplyAllImpl(void (*Func)(void*, Ref), void* Context) { m_Impl->ApplyAll(Func, Context); } void Registry::SetErrorHandler(ErrorHandler* Handler) { m_Impl->SetErrorHandler(Handler); } void Registry::Shutdown() { m_Impl->Shutdown(); } } // namespace zen::logging