#include "CLogger.h" #include "CEngine.h" #include "CTerminal.h" #include "CFunction.h" #include "CStartupArgsManager.h" #include "CConVarManager.h" #include "CWrapable.h" #include #include #include std::wofstream* CLogger::m_OpenStream(std::string path) { if (streams.find(path) == streams.end()) { std::wofstream wf; wf.open(path, std::ios::app | std::ios::out); if (!wf.is_open()) { return nullptr; } streams[path] = std::move(wf); } return &streams[path]; } bool CLogger::V_Init() { std::string outPath, errPath, slowModeDelay; bool notasync = false; bool forceasync = false; bool nofiles = false; COMPONENT_CALL_GET(outPath, CStartupArgsManager, GetArgumentValue("-out")); COMPONENT_CALL_GET(errPath, CStartupArgsManager, GetArgumentValue("-err")); COMPONENT_CALL_GET(slowModeDelay, CStartupArgsManager, GetArgumentValue("-slowloggerdelay")); COMPONENT_CALL_GET(notasync, CStartupArgsManager, IsArgumentSet("-synclogger")); COMPONENT_CALL_GET(forceasync, CStartupArgsManager, IsArgumentSet("-asynclogger")); COMPONENT_CALL_GET(nofiles, CStartupArgsManager, IsArgumentSet("-loggernofiles")); COMPONENT_CALL_GET(m_slowMode, CStartupArgsManager, IsArgumentSet("-slowlogger")); if(m_slowMode && !slowModeDelay.empty()) { m_slowModeDelay = StringUtils::FromStr(slowModeDelay); } if(notasync) { m_isAsync = false; } if(forceasync) { m_isAsync = true; } if(!nofiles) { outstream = m_OpenStream(outPath); errstream = m_OpenStream(errPath); } if (m_isAsync) { outqueue.reserve(128); errqueue.reserve(128); m_thread = std::make_unique(&CLogger::m_ThreadedWorker, this); } return true; } void CLogger::V_PostInit() { auto mgr = CEngine::GetInstance()->Components.GetComponentTyped(); mgr->AddConVar("time.limit.logger.maxfps", new CWrapable(20)); mgr->AddConVar("time.limit.logger.yield", new CWrapable(true)); mgr->AddConVar("time.limit.logger.adaptive_sleep", new CWrapable(true)); mgr->AddConVar("time.limit.logger.strict_sleep", new CWrapable(false)); } void CLogger::V_Update() { if(CClock::now() - lastsync >= std::chrono::duration(sync_datas_interval)) { auto mgr = CEngine::GetInstance()->Components.GetComponentTyped(); async_data data; data.adaptive_sleep = mgr->GetConVarValue("time.limit.logger.maxfps"); data.yield = mgr->GetConVarValue("time.limit.logger.yield"); data.adaptive_sleep = mgr->GetConVarValue("time.limit.logger.adaptive_sleep"); data.strict_sleep = mgr->GetConVarValue("time.limit.logger.strict_sleep"); { std::lock_guard _lock(async_data_mutex); threaded_async = data; } lastsync = CClock::now(); } } CLogger::~CLogger() { if (m_isAsync) { AskedToStop.store(true, std::memory_order_relaxed); while (!Stopped) { std::this_thread::yield(); } if(m_thread->joinable()) { m_thread->join(); } } if (outstream && outstream->is_open()) { outstream->flush(); outstream->close(); } if (errstream && errstream->is_open()) { errstream->flush(); errstream->close(); } } void CLogger::m_flushOut(const std::wstring& str) { if (outstream && outstream->is_open()) { (*outstream) << str; outstream->flush(); } if(!CEngine::GetInstance()->GetStopFlag()) { COMPONENT_CALL(CTerminal, Print(str)); } m_Wait(); } void CLogger::m_flushErr(const std::wstring& err) { if (errstream && errstream->is_open()) { (*errstream) << err; errstream->flush(); } if(!CEngine::GetInstance()->GetStopFlag()) { COMPONENT_CALL(CTerminal, Error(err)); } m_Wait(); } void CLogger::Out(const std::wstring& str) { if (m_isAsync) { std::lock_guard lock(mutex); outqueue.push_back(str); } else { m_flushOut(str); } OnOut(str); } void CLogger::Err(const std::wstring& err) { if (m_isAsync) { std::lock_guard lock(mutex); errqueue.push_back(err); } else { m_flushErr(err); } OnErr(err); } void CLogger::Outln(const std::wstring& str) { Out(str + L"\n"); } void CLogger::Errln(const std::wstring& err) { Err(err + L"\n"); } void CLogger::SetAsync() { m_isAsync = true; } bool CLogger::IsAsync() { return m_isAsync; } void CLogger::m_ThreadedWorker() { std::vector outcopy, errcopy; while (true) { //mgr->AddConVar("time.limit.logger.maxfps", new CWrapable(20)); //mgr->AddConVar("time.limit.logger.yield", new CWrapable(true)); //mgr->AddConVar("time.limit.logger.adaptive_sleep", new CWrapable(true)); //mgr->AddConVar("time.limit.logger.strict_sleep", new CWrapable(false)); async_data asdata; { std::lock_guard _lock(async_data_mutex); asdata = threaded_async; } Sleeper.Start(asdata.maxfps); { std::lock_guard lock(mutex); if(!outqueue.empty()) { outcopy.swap(outqueue); } if(!errqueue.empty()) { errcopy.swap(errqueue); } } for (const auto& s : outcopy) { m_flushOut(s); } for (const auto& e : errcopy) { m_flushErr(e); } outcopy.clear(); errcopy.clear(); if (AskedToStop.load(std::memory_order_acquire)) { Stopped.store(true, std::memory_order_release); break; } Sleeper.End(asdata.yield, asdata.adaptive_sleep, asdata.strict_sleep); } } bool CLogger::V_ScriptInit(std::shared_ptr state, sol::table table) { table.set_function("out", [this](const std::wstring& text) { Out(text); }); table.set_function("outln", [this](const std::wstring& text) { Outln(text); }); table.set_function("err", [this](const std::wstring& err) { Err(err); }); table.set_function("errln", [this](const std::wstring& err) { Errln(err); }); return true; } void CLogger::m_Wait() const { if(!m_slowMode) { return; } std::this_thread::sleep_for(std::chrono::milliseconds(m_slowModeDelay)); } sol::object CLogger::V_GetScriptUserType(sol::state_view state) { return sol::make_object(state, this); } LINK_COMPONENT_TO_CLASS(CLogger, logger);