/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Logger.cpp Date: 2022-1-21 Author: Reece ***/ #include #include "Logger.hpp" #include #include namespace Aurora::Logging { using namespace Console; static AuList> gLogTasks; static AuThreadPrimitives::SpinLock gGlobalSpin; static AuThreadPrimitives::SpinLock gTaskSpin; static AuList gFlushableLoggers; Logger::Logger(const AuList> &sinks) : sinks(sinks) { AuMemset(shouldFilter, 0, sizeof(shouldFilter)); { AU_LOCK_GUARD(gGlobalSpin); AuTryInsert(gFlushableLoggers, this); } } Logger::~Logger() { Disable(); } void Logger::WriteLines(AuUInt8 level, const ConsoleMessage &msg) { try { if (msg.line.find('\n') == AuString::npos) [[likely]] { if (WriteNow(level, msg)) { AddToPushQueue(level, msg); } } else [[unlikely]] { Parse::SplitNewlines(msg.line, [&](const AuString &line) { ConsoleMessage dup = msg; dup.line = line; if (WriteNow(level, dup)) { AddToPushQueue(level, dup); } }); } } catch (...) { // Loggers experiencing something fucky is a liablity Hooks::WriteLoggerFailedWarning(); } } void Logger::WriteMessage(AuUInt8 level, const ConsoleMessage &msg) { // Accounts for atomic shutdown { AU_LOCK_GUARD(spin); if (shouldFilter[level]) { return; } } WriteLines(level, msg); } void Logger::AddToPushQueue(AuUInt8 level, const ConsoleMessage &msg) { { AU_LOCK_GUARD(gTaskSpin); auto nice = gLogTasks.size() + 1; if (gLogTasks.capacity() < nice) { gLogTasks.reserve(nice*10); } while (!AuTryInsert(gLogTasks, AuMakeTuple(this, level, msg))) { SysPushErrorMem("Push failed - trying again"); spin.Unlock(); AuThreading::Sleep(100); spin.Lock(); } } Grug::DrachenlordScreech(); } void Logger::PushFilter(AuUInt8 level, bool shouldFilter) { AU_LOCK_GUARD(spin); try { while (!AuTryInsert(filters, AuMakeTuple(level, shouldFilter))) { SysPushErrorMem("Push failed - trying again. wont be able to handle pop - wont syspanic yet"); AuThreading::Sleep(100); } AuMemset(this->shouldFilter, 0, sizeof(this->shouldFilter)); for (auto &tuple : filters) { auto level = AuGet<0>(tuple); auto shouldFilter = AuGet<1>(tuple); this->shouldFilter[level] = shouldFilter; } } catch (...) { } } void Logger::PopFilter() { try { AU_LOCK_GUARD(spin); filters.pop_back(); } catch (...) { } } void ForceFlushLoggers() { decltype(gLogTasks) logTasks; AU_LOCK_GUARD(gGlobalSpin); { AU_LOCK_GUARD(gTaskSpin); try { if (AuTryResize(logTasks, gLogTasks.size())) { for (int i = 0; i < gLogTasks.size(); i++) { logTasks[i] = AuMove(gLogTasks[i]); } gLogTasks.clear(); } } catch (...) { } if (logTasks.empty()) { return; } } try { for (const auto &logEntry : logTasks) { auto &logger = AuGet<0>(logEntry); auto &level = AuGet<1>(logEntry); auto &message = AuGet<2>(logEntry); logger->WriteLater(level, message); } } catch (...) { } } void ForceFlushLoggersNoLock() { decltype(gLogTasks) logTasks; { try { if (AuTryResize(logTasks, gLogTasks.size())) { for (int i = 0; i < gLogTasks.size(); i++) { logTasks[i] = AuMove(gLogTasks[i]); } gLogTasks.clear(); } } catch (...) { } if (logTasks.empty()) { return; } } try { for (const auto &logEntry : logTasks) { auto &logger = AuGet<0>(logEntry); auto &level = AuGet<1>(logEntry); auto &message = AuGet<2>(logEntry); logger->WriteLater(level, message); } } catch (...) { } } static void ForceFlushLogger(Logger *logger) { for (const auto &sink : logger->sinks) { if (!sink) { continue; } try { sink->OnFlush(); } catch (...) { SysPushErrorCatch(); } } } void ForceFlushFlushNoLock() { for (const auto &logger : gFlushableLoggers) { ForceFlushLogger(logger); } } void ForceFlushFlush() { AU_LOCK_GUARD(gGlobalSpin); ForceFlushFlushNoLock(); } void Logger::Disable() { AU_LOCK_GUARD(gGlobalSpin); ForceFlushLoggersNoLock(); ForceFlushLogger(this); { AU_LOCK_GUARD(spin); AuMemset(shouldFilter, 1, sizeof(shouldFilter)); } { AuTryRemove(gFlushableLoggers, this); } } bool Logger::WriteNow(AuUInt8 level, const ConsoleMessage &msg) { bool ret {}; try { for (const auto &sink : this->sinks) { if (!sink) { continue; } try { ret |= sink->OnMessageNonblocking(level, msg); } catch (...) { SysPushErrorCatch("Failed to pump a logger"); } } } catch (...) { ret = true; SysPushErrorCatch(); } return ret; } void Logger::WriteLater(AuUInt8 level, const ConsoleMessage &msg) { try { for (const auto &sink : this->sinks) { if (!sink) { continue; } try { sink->OnMessageBlocking(level, msg); } catch (...) { SysPushErrorCatch("Failed to pump a logger"); } } } catch (...) { SysPushErrorCatch(); } } void InitLoggers() { gLogTasks.reserve(1000); } void DeinitLoggers() { ForceFlushLoggers(); gUserLogger.reset(); } AUKN_SYM ILogger *NewLoggerNew(const AuList> &sinks) { try { auto logger = _new Logger(sinks); if (!logger) { return nullptr; } return logger; } catch (...) { SysPushErrorCatch(); return {}; } } AUKN_SYM void NewLoggerRelease(ILogger *logger) { AuSafeDelete(logger); } }