#ifndef LOGGER_H #define LOGGER_H // Custom includes // Enums enum class LogModule { ENGINE, RENDERER, GLFW, CLEANUP }; enum class LogLevel { EDEBUG, EINIT, // Initialization related EINFO, // General infos EWARN, EERROR, EFATAL }; class NetLogger { public: struct Server{ int priority; char* url[]; // Put the url at the end for bit optimization }; void initNetLogger(Server server); void logNet(char* body[], char* head, Server server); void logNetAsync(); // Todo private: }; class Logger { public: // Members std::string logBuffer; // Log is empty by default std::vector lines; // Functions template void logDisk( LogModule module, LogLevel level, std::string_view message, Args&&... args ); // Logs to a file on disk // Overload void logDisk(std::string_view message); template void logRAM( LogModule module, LogLevel level, std::string_view message, Args&&... args ); // Logs to ram (ImGUI) template void logConsole( LogModule module, LogLevel level, std::string_view message, Args&&... args ); // Logs to the console (crashes, etc...) template void logAll( LogModule module, LogLevel level, std::string_view message, Args&&... args ); // Logs using all of the above, RAM then console and finally disk, maybe multithread (?) template void logError( LogModule module, LogLevel level, std::string_view message, Args&&... args ); // Logs to console and file, in cases of crashes or errors void merge(std::string_view source); // Merges a source dumped logs into this void clear(); private: // Helper functions static std::string_view moduleToString(LogModule m); static std::string_view levelToString(LogLevel l); }; #endif // End of header #ifdef LOGGER_IMPL #ifndef LOGGER_IMPL_H #define LOGGER_IMPL_H // Implementation // Includes //#include // Todo : add it in the flake #include #include #include /* void NetLogger::initNetLogger(Server server) { httplib::Client cli(server.url) // Todo : add error handling } */ template void Logger::logRAM( LogModule module, LogLevel level, std::string_view message, Args&&... args ) { auto now = std::chrono::system_clock::now(); auto timestamp = std::format("{:%Y-%m-%d %H:%M}", now); auto start = logBuffer.size(); fmt::format_to( std::back_inserter(logBuffer), "[{}] ", timestamp ); fmt::format_to( std::back_inserter(logBuffer), "[{}:{}] ", moduleToString(module), levelToString(level) ); fmt::format_to( std::back_inserter(logBuffer), fmt::runtime(message), std::forward(args)... ); logBuffer.push_back('\n'); auto end = logBuffer.size(); lines.emplace_back(logBuffer.data() + start, end - start); } template void Logger::logConsole( LogModule module, LogLevel level, std::string_view message, Args&&... args ) { std::string tempBuffer; auto now = std::chrono::system_clock::now(); auto timestamp = std::format("{:%Y-%m-%d %H:%M}", now); fmt::format_to( std::back_inserter(tempBuffer), "[{}] ", timestamp ); fmt::format_to( std::back_inserter(tempBuffer), "[{}:{}] ", moduleToString(module), levelToString(level) ); fmt::format_to( std::back_inserter(tempBuffer), fmt::runtime(message), std::forward(args)... ); std::cout.write(tempBuffer.data(), tempBuffer.size()); std::cout.put('\n'); } template void Logger::logDisk( LogModule module, LogLevel level, std::string_view message, Args&&... args ) { // Open the logfile std::ofstream logFile ("log.txt", std::ios::out | std::ios::app); if (!logFile.good()) { throw std::runtime_error("Failed to open logfile !"); } // Create a temporary buffer std::string tempBuffer; auto now = std::chrono::system_clock::now(); auto timestamp = std::format("{:%Y-%m-%d %H:%M}", now); fmt::format_to( std::back_inserter(tempBuffer), "[{}] ", timestamp ); fmt::format_to( std::back_inserter(tempBuffer), "[{}:{}] ", moduleToString(module), levelToString(level) ); fmt::format_to( std::back_inserter(tempBuffer), fmt::runtime(message), std::forward(args)... ); logFile << tempBuffer << '\n'; logFile.close(); } // Overload to inject a message void Logger::logDisk(std::string_view message) { std::ofstream logFile ("log.txt", std::ios::out | std::ios::app); if (!logFile.good()) { throw std::runtime_error("Failed to open logfile !"); } logFile << message << '\n'; logFile.close(); } template void Logger::logAll( LogModule module, LogLevel level, std::string_view message, Args&&... args ) { // Logs to all targets ! logRAM( module, level, message, args... ); logConsole( module, level, message, args... ); logDisk( module, level, message, args... ); } template void Logger::logError( LogModule module, LogLevel level, std::string_view message, Args&&... args ) { logDisk( module, level, message, args... ); logConsole( module, level, message, args... ); } void Logger::merge(std::string_view source) { /* This merges a dumped logger into this one Assumes the other logger is already formated. This allows to add arbitrary strings into the logs */ logBuffer += source; } void Logger::clear() { logBuffer.clear(); } std::string_view Logger::moduleToString(LogModule m) { using enum LogModule; switch (m) { case ENGINE: return "Engine"; case RENDERER: return "Renderer"; case GLFW: return "GLFW"; case CLEANUP: return "CLEANUP"; default: return "Unkown"; } } std::string_view Logger::levelToString(LogLevel l) { using enum LogLevel; switch (l) { case EDEBUG: return "Debug"; case EINFO: return "Info"; case EINIT: return "Init"; case EWARN: return "Warn"; case EERROR: return "Error"; case EFATAL: return "Fatal"; default: return "???"; } } #endif #endif