diff --git a/engine/src/core/Logger.hpp b/engine/src/core/Logger.hpp index 3eef1e6..971da9a 100644 --- a/engine/src/core/Logger.hpp +++ b/engine/src/core/Logger.hpp @@ -6,13 +6,160 @@ #include -class Logger { -public: - void logVersion() { - std::cout << "ICEngine Version: " << PROJECT_VERSION << std::endl; - } +namespace ICEngine { - void logCrash(const std::string& errorMessage) { - std::cerr << "Crash detected in version " << PROJECT_VERSION << ": " << errorMessage << std::endl; - } -}; + enum LogLevel { + OFF = 0, + TRACE, + DEBUG, + WARN, + ERROR, + CRITICAL, + FATAL + }; + + class Logger { + private: + Logger(const char *name, std::ostream *stream) : name(name) { + if (stream) { + streams.push_back(stream); + } + } + friend class Log; + + public: + void SetLevel(LogLevel level) { + this->level = level; + } + + template + void Log(LogLevel level, const char *file, int line, const char *func, const std::string &formatStr, Args &&... args) { + if (level < this->level) return; + std::string formattedMessage = format(formatStr, std::forward(args)...); + std::string logMessage = formatMessage(level, file, line, func, formattedMessage); + + for (auto stream: streams) { + (*stream) << logMessage << std::endl; + } + } + + private: + const char *name; + std::vector streams; + LogLevel level = DEBUG; + + static const char *levelToString(LogLevel level) { + switch (level) { + case TRACE: return "TRACE"; + case DEBUG: return "DEBUG"; + case WARN: return "WARN"; + case ERROR: return "ERROR"; + case CRITICAL: return "CRITICAL"; + case FATAL: return "FATAL"; + default: return "UNKNOWN"; + } + } + + static std::string getCurrentTime() { + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + std::ostringstream oss; + oss << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S"); + return oss.str(); + } + + static std::string truncateFilePath(const char *file) { + std::string path(file); + size_t pos = path.find_last_of("/\\"); + if (pos != std::string::npos && pos > 3) { + return "..." + path.substr(pos - 3); + } + return path; + } + + template + std::string format(const std::string &formatStr, Args&&... args) { + std::ostringstream oss; + formatHelper(oss, formatStr, std::forward(args)...); + return oss.str(); + } + + void formatHelper(std::ostringstream &oss, const std::string &formatStr) { + oss << formatStr; + } + + template + void formatHelper(std::ostringstream &oss, const std::string &formatStr, T &&firstArg, Args&&... remainingArgs) { + size_t pos = formatStr.find("{}"); + if (pos != std::string::npos) { + oss << formatStr.substr(0, pos) << std::forward(firstArg); + formatHelper(oss, formatStr.substr(pos + 2), std::forward(remainingArgs)...); + } else { + oss << formatStr; + } + } + + // Formats the log message with metadata (timestamp, file, line, etc.) + std::string formatMessage(LogLevel level, const char *file, int line, const char *func, const std::string &message) { + std::ostringstream oss; + oss << getCurrentTime() << " "; + oss << truncateFilePath(file) << ":" << line << ":" << func << " "; + oss << "[" << levelToString(level) << "] "; + oss << "[" << name << "]: " << message; + return oss.str(); + } + }; + + class Log { + public: + static void Init() { + GetCoreLogger(); + GetAppLogger(); + } + + static std::shared_ptr &GetCoreLogger() { + static std::shared_ptr CoreLogger(new Logger("CORE", &std::cout)); + return CoreLogger; + } + + static std::shared_ptr &GetAppLogger() { + static std::shared_ptr AppLogger(new Logger("APP", &std::cout)); + return AppLogger; + } + + private: + Log() = delete; + Log(const Log &) = delete; + Log &operator=(const Log &) = delete; + }; + +#ifndef NDEBUG + #define CORE_LOG_TRACE(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::TRACE, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define CORE_LOG_DEBUG(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::DEBUG, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define CORE_LOG_WARN(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::WARN, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define CORE_LOG_ERROR(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::ERROR, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define CORE_LOG_CRITICAL(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::CRITICAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define CORE_LOG_FATAL(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::FATAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + + #define LOG_TRACE(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::TRACE, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define LOG_DEBUG(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::DEBUG, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define LOG_WARN(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::WARN, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define LOG_ERROR(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::ERROR, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define LOG_CRITICAL(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::CRITICAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) + #define LOG_FATAL(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::FATAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__) +#else + #define CORE_LOG_TRACE(message, ...) + #define CORE_LOG_DEBUG(message, ...) + #define CORE_LOG_WARN(message, ...) + #define CORE_LOG_ERROR(message, ...) + #define CORE_LOG_CRITICAL(message, ...) + #define CORE_LOG_FATAL(message, ...) + + #define LOG_TRACE(message, ...) + #define LOG_DEBUG(message, ...) + #define LOG_WARN(message, ...) + #define LOG_ERROR(message, ...) + #define LOG_CRITICAL(message, ...) + #define LOG_FATAL(message, ...) + #endif +}