206 lines
5.8 KiB
C++
206 lines
5.8 KiB
C++
#pragma once
|
||
#include <fstream>
|
||
#include <string>
|
||
#include <Windows.h>
|
||
#include <chrono>
|
||
#include <iomanip>
|
||
#include <sstream>
|
||
#include <filesystem>
|
||
#include <deque>
|
||
#include <unordered_set>
|
||
|
||
class Logger
|
||
{
|
||
public:
|
||
|
||
static Logger& Get()
|
||
{
|
||
static Logger instance;
|
||
return instance;
|
||
}
|
||
|
||
Logger(Logger const&) = delete;
|
||
void operator=(Logger const&) = delete;
|
||
|
||
enum class LogLevel
|
||
{
|
||
Info,
|
||
Warning,
|
||
Error,
|
||
Shutdown,
|
||
Initialize,
|
||
Update,
|
||
Render,
|
||
Input,
|
||
Physics,
|
||
Audio,
|
||
Network,
|
||
Scripting,
|
||
AI,
|
||
Resource,
|
||
Memory,
|
||
Debug,
|
||
Count // Do not use this, it's just to get the number of log levels it must at the end
|
||
};
|
||
|
||
// Return the size of the enum class LogLevel as a constant integer
|
||
static constexpr int LogLevelCount = static_cast<int>(LogLevel::Count);
|
||
|
||
struct LogEntry
|
||
{
|
||
std::string message;
|
||
LogLevel level;
|
||
};
|
||
|
||
static const char* LogLevelToString(LogLevel level)
|
||
{
|
||
switch (level)
|
||
{
|
||
case LogLevel::Info: return "INFO";
|
||
case LogLevel::Warning: return "WARNING";
|
||
case LogLevel::Error: return "ERROR";
|
||
case LogLevel::Shutdown: return "SHUTDOWN";
|
||
case LogLevel::Initialize: return "INITIALIZE";
|
||
case LogLevel::Update: return "UPDATE";
|
||
case LogLevel::Render: return "RENDER";
|
||
case LogLevel::Input: return "INPUT";
|
||
case LogLevel::Physics: return "PHYSICS";
|
||
case LogLevel::Audio: return "AUDIO";
|
||
case LogLevel::Network: return "NETWORK";
|
||
case LogLevel::Scripting: return "SCRIPTING";
|
||
case LogLevel::AI: return "AI";
|
||
case LogLevel::Resource: return "RESOURCE";
|
||
case LogLevel::Memory: return "MEMORY";
|
||
case LogLevel::Debug: return "DEBUG";
|
||
default: return "Unknown";
|
||
}
|
||
}
|
||
|
||
Logger()
|
||
{
|
||
char* appdata = nullptr;
|
||
size_t len;
|
||
_dupenv_s(&appdata, &len, "APPDATA");
|
||
if (appdata == nullptr)
|
||
{
|
||
m_appdataPath = "log.log";
|
||
}
|
||
else
|
||
{
|
||
m_appdataPath = appdata;
|
||
}
|
||
free(appdata);
|
||
std::string directoryPath = m_appdataPath + "\\Khaotic Engine";
|
||
CreateDirectoryA(directoryPath.c_str(), NULL);
|
||
|
||
ManageLogFiles(directoryPath);
|
||
|
||
m_logFilePath = directoryPath + "\\" + m_logFileName;
|
||
}
|
||
|
||
void Log(const std::string& message, const std::string& fileName, int lineNumber, LogLevel level = LogLevel::Info)
|
||
{
|
||
if (m_disabledLogLevels.find(level) != m_disabledLogLevels.end())
|
||
{
|
||
return;
|
||
}
|
||
|
||
auto now = std::chrono::system_clock::now();
|
||
auto in_time_t = std::chrono::system_clock::to_time_t(now);
|
||
|
||
std::tm buf;
|
||
localtime_s(&buf, &in_time_t);
|
||
|
||
// Obtenez les millisecondes <20> partir de maintenant
|
||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
|
||
|
||
// Utilisez LogLevelToString pour obtenir la cha<68>ne de caract<63>res du niveau de log
|
||
std::string levelStr = LogLevelToString(level);
|
||
|
||
std::stringstream ss;
|
||
ss << "[" << std::put_time(&buf, "%Y-%m-%d") << "] "
|
||
<< "[" << std::put_time(&buf, "%X") << "." << std::setfill('0') << std::setw(3) << ms.count() << "] "
|
||
<< "[" << levelStr << "] "
|
||
<< "[" << fileName << ":" << lineNumber << "] "
|
||
<< message;
|
||
|
||
Log(ss.str(), level);
|
||
|
||
std::ofstream file(m_logFilePath, std::ios::app);
|
||
if (file.is_open())
|
||
{
|
||
file << ss.str() << std::endl;
|
||
file.close();
|
||
}
|
||
}
|
||
|
||
void Log(const std::string& message, LogLevel level)
|
||
{
|
||
if (m_disabledLogLevels.find(level) != m_disabledLogLevels.end())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (logBuffer.size() >= logBufferSize)
|
||
{
|
||
logBuffer.pop_front();
|
||
}
|
||
logBuffer.push_back({ message, level });
|
||
}
|
||
|
||
const std::deque<LogEntry>& GetLogBuffer() const { return logBuffer; }
|
||
|
||
void ManageLogFiles(const std::string& directoryPath)
|
||
{
|
||
std::vector<std::filesystem::path> logFiles;
|
||
|
||
// Parcourez tous les fichiers dans le dossier
|
||
for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
|
||
{
|
||
// Si le fichier est un fichier de log, ajoutez-le <20> la liste
|
||
if (entry.path().extension() == ".log")
|
||
{
|
||
logFiles.push_back(entry.path());
|
||
}
|
||
}
|
||
|
||
// Si nous avons plus de trois fichiers de log, supprimez le plus ancien
|
||
while (logFiles.size() >= 3)
|
||
{
|
||
// Triez les fichiers par date de modification, le plus ancien en premier
|
||
std::sort(logFiles.begin(), logFiles.end(), [](const std::filesystem::path& a, const std::filesystem::path& b)
|
||
{
|
||
return std::filesystem::last_write_time(a) < std::filesystem::last_write_time(b);
|
||
});
|
||
|
||
// Supprimez le fichier le plus ancien
|
||
std::filesystem::remove(logFiles[0]);
|
||
|
||
// Supprimez-le de la liste
|
||
logFiles.erase(logFiles.begin());
|
||
}
|
||
|
||
// Cr<43>ez un nouveau fichier de log pour cette ex<65>cution
|
||
auto now = std::chrono::system_clock::now();
|
||
auto in_time_t = std::chrono::system_clock::to_time_t(now);
|
||
std::tm buf;
|
||
localtime_s(&buf, &in_time_t);
|
||
|
||
std::stringstream ss;
|
||
ss << "Khaotic_log_" << std::put_time(&buf, "%Y_%m_%d_%Hh%Mm%Ss") << ".log";
|
||
m_logFileName = ss.str();
|
||
}
|
||
|
||
std::unordered_set<LogLevel> m_disabledLogLevels;
|
||
|
||
private:
|
||
std::string m_filename;
|
||
std::string m_appdataPath;
|
||
std::string m_logFileName;
|
||
std::string m_logFilePath;
|
||
|
||
std::deque<LogEntry> logBuffer;
|
||
const size_t logBufferSize = 1000;
|
||
|
||
};
|