AuroraRuntime/Source/Console/ConsoleLogger/ConsoleLogger.cpp
2021-06-27 22:25:29 +01:00

201 lines
4.9 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ConsoleLogger.cpp
Date: 2021-6-22
Author: Reece
***/
#include <RuntimeInternal.hpp>
#include "ConsoleLogger.hpp"
namespace Aurora::Console::ConsoleLogger
{
static Threading::Threads::ThreadUnique_t gFileWriterThread;
static AuList<AuUInt8> gLogBuffer;
static Threading::Primitives::RWLockUnique_t gLogMutex;
static IO::FS::OpenWriteUnique_t gFileHandle;
static const auto & gLogConfig = gRuntimeConfig.console.logging;
static void EraseFilesByTimestamp(const AuString &path, /*const its not worth reallocation*/ AuList<AuString> &files)
{
if (files.size() <= gLogConfig.maxLogs)
{
return;
}
// our filenames are usually prefixed by an ISO 8601 timestamp where the most significant bits are last (YYYY-MM-DD?HH-MM-SS)
// a quick ghetto sort should be all we need. no need to waste time parsing timestamps
std::sort(files.begin(), files.end());
auto amount = files.size() - gLogConfig.maxLogs;
for (auto x = 0, i = 0; ((x < amount) && (i > files.size())); i++)
{
try
{
if (IO::FS::Remove(path + "/" + files[i]))
{
x++;
}
}
catch (...)
{
}
}
}
static void CleanupOldLogs()
{
AuString path;
AuString procName;
if (!Process::GetProcName(procName))
{
return;
}
if (!IO::FS::GetProfileDomain(path))
{
return;
}
path += "/Logs/";
path += procName;
AuList<AuString> files;
AuBST<AuString, IO::FS::Stat> fileMeta;
AuUInt32 size {};
IO::FS::FilesInDirectory(path, files);
for (const auto &file : files)
{
IO::FS::Stat stat;
IO::FS::StatFile(path + "/" + file, stat);
fileMeta[file] = stat;
size += stat.size;
}
EraseFilesByTimestamp(path, files);
// TODO: erase when size >= gLogConfig.maxSizeMb * 1024
}
static void CompressLogs()
{
}
void Flush()
{
Threading::WaitableLockGuard a(gLogMutex->AsReadable());
if (gFileHandle)
{
gFileHandle->Write(gLogBuffer.data(), gLogBuffer.size());
}
gLogBuffer.clear();
}
static bool OpenLogFile()
{
AuString path;
AuString procName;
if (!Process::GetProcName(procName))
{
return false;
}
if (!IO::FS::GetProfileDomain(path))
{
return false;
}
auto tm = Time::ToCivilTime(Time::CurrentClockMS());
path += fmt::format("/Logs/{}/{:04}-{:02}-{:02}T{:02}-{:02}-{:02}Z.txt",
procName,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
gFileHandle = IO::FS::OpenWriteUnique(path);
return gFileHandle ? true : false;
}
static void LogThreadInit()
{
CleanupOldLogs();
auto thread = Threading::Threads::GetThread();
while (!thread->Exiting())
{
Sleep(500);
Flush();
}
}
void Init()
{
if (!gLogConfig.enableLogging)
{
return;
}
if (!OpenLogFile())
{
return;
}
gLogMutex = Threading::Primitives::RWLockUnique();
if (!gLogMutex)
{
return;
}
Threading::Threads::AbstractThreadVectors handler;
handler.DoRun = [](Threading::Threads::IAuroraThread *)
{
LogThreadInit();
};
gFileWriterThread = Threading::Threads::ThreadUnique(handler);
if (!gFileWriterThread)
{
return;
}
gFileWriterThread->SetName("ConsoleFIO");
gFileWriterThread->Run();
Console::Hooks::AddHook([&](const Console::ConsoleMessage &string) -> void
{
Threading::WaitableLockGuard a(gLogMutex->AsWritable());
auto str = string.ToSimplified();
gLogBuffer.reserve(gLogBuffer.size() + str.size() + 2);
gLogBuffer.insert(gLogBuffer.end(), reinterpret_cast<AuUInt8*>(str.data()), reinterpret_cast<AuUInt8*>(str.data()) + str.size());
#if defined(AURORA_PLATFORM_WIN32)
gLogBuffer.insert(gLogBuffer.end(), AuUInt8('\r'));
#endif
gLogBuffer.insert(gLogBuffer.end(), AuUInt8('\n'));
});
}
void Pump()
{
}
void Exit()
{
Flush();
gFileWriterThread.reset();
gLogMutex.reset();
gFileHandle.reset();
CompressLogs();
}
}