AuroraRuntime/Source/Console/ConsoleFIO/ConsoleFIO.cpp
2021-09-06 11:58:08 +01:00

179 lines
4.4 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ConsoleFIO.cpp
Date: 2021-6-22
Author: Reece
***/
#include <RuntimeInternal.hpp>
#include "ConsoleFIO.hpp"
namespace Aurora::Console::ConsoleFIO
{
static AuList<AuUInt8> gLogBuffer;
static Threading::Primitives::RWLockUnique_t gLogMutex;
static IO::FS::OpenWriteUnique_t gFileHandle;
static const auto & gLogConfig = gRuntimeConfig.console.fio;
static AuString GetLogDirectory()
{
AuString path;
AuString procName;
if (!IO::FS::GetProfileDomain(path))
{
path = ".";
}
if (!Process::GetProcName(procName))
{
procName = "Unknown";
}
path += "/Logs/";
path += procName;
return path;
}
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()
{
AuList<AuString> files;
AuBST<AuString, IO::FS::Stat> fileMeta;
AuUInt32 size {};
auto baseLogPath = GetLogDirectory();
IO::FS::FilesInDirectory(baseLogPath, files);
for (const auto &file : files)
{
IO::FS::Stat stat;
IO::FS::StatFile(baseLogPath + "/" + file, stat);
fileMeta[file] = stat;
size += stat.size;
}
EraseFilesByTimestamp(baseLogPath, files);
// TODO: erase when size >= gLogConfig.maxSizeMb * 1024
}
static void CompressLogs()
{
// TODO: write XZ's
}
void Flush()
{
/// It should be expected that the TLS teardown emergency flush to dispatch after deinit
if (!gLogMutex)
{
return;
}
AU_LOCK_GUARD(gLogMutex->AsReadable());
if (gFileHandle)
{
gFileHandle->Write(gLogBuffer.data(), gLogBuffer.size());
}
gLogBuffer.clear();
}
static bool OpenLogFile()
{
AuString path;
auto tm = Time::ToCivilTime(Time::CurrentClockMS());
path = fmt::format("{}/{:04}-{:02}-{:02}T{:02}-{:02}-{:02}Z.txt",
GetLogDirectory(),
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 static_cast<bool>(gFileHandle);
}
void FIOCleanup()
{
CleanupOldLogs();
CompressLogs();
}
void Init()
{
if (!gLogConfig.enableLogging)
{
return;
}
if (!OpenLogFile())
{
return;
}
gLogMutex = Threading::Primitives::RWLockUnique();
if (!gLogMutex)
{
return;
}
Console::Hooks::AddFunctionalHook([&](const Console::ConsoleMessage &string) -> void
{
AU_LOCK_GUARD(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_IS_MODERNNT_DERIVED)
gLogBuffer.insert(gLogBuffer.end(), AuUInt8('\r'));
#endif
gLogBuffer.insert(gLogBuffer.end(), AuUInt8('\n'));
});
}
void Pump()
{
}
void Exit()
{
Flush();
gLogMutex.reset();
gFileHandle.reset();
}
}