feat(logging): implement logger and logging macros

This commit is contained in:
illyum 2024-09-15 04:14:31 -06:00
parent 9db13726b5
commit bf2847cc09

View File

@ -6,13 +6,160 @@
#include <pch.hpp>
class Logger {
public:
void logVersion() {
std::cout << "ICEngine Version: " << PROJECT_VERSION << std::endl;
namespace ICEngine {
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;
}
void logCrash(const std::string& errorMessage) {
std::cerr << "Crash detected in version " << PROJECT_VERSION << ": " << errorMessage << std::endl;
template<typename... Args>
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>(args)...);
std::string logMessage = formatMessage(level, file, line, func, formattedMessage);
for (auto stream: streams) {
(*stream) << logMessage << std::endl;
}
};
}
private:
const char *name;
std::vector<std::ostream *> 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<typename... Args>
std::string format(const std::string &formatStr, Args&&... args) {
std::ostringstream oss;
formatHelper(oss, formatStr, std::forward<Args>(args)...);
return oss.str();
}
void formatHelper(std::ostringstream &oss, const std::string &formatStr) {
oss << formatStr;
}
template<typename T, typename... Args>
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<T>(firstArg);
formatHelper(oss, formatStr.substr(pos + 2), std::forward<Args>(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<Logger> &GetCoreLogger() {
static std::shared_ptr<Logger> CoreLogger(new Logger("CORE", &std::cout));
return CoreLogger;
}
static std::shared_ptr<Logger> &GetAppLogger() {
static std::shared_ptr<Logger> 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
}