diff --git a/src/utility/Logger.cpp b/src/utility/Logger.cpp new file mode 100644 index 0000000..2a6b832 --- /dev/null +++ b/src/utility/Logger.cpp @@ -0,0 +1,75 @@ +/** + * @file Logger.cpp + * @author Anand Doshi + * @date 2025-01-08 + * + * @copyright Copyright (c) 2025 + * + */ + + +#include "Logger.hpp" +#include "Exceptions.hpp" +#include +#include +#include +#include +#include +#include + +bool Logger::isLoggable(Level level) +{ + if (filter == Filter::quiet) + { + return false; + } + + if (filter == Filter::important) + { + return level == Level::error || level == Level::warning || level == Level::severe; + } + + if (filter == Filter::verbose) + { + return true; + } + + return true; // unknown filter, everything logged +} + +constexpr std::string_view Logger::getLevelName(Logger::Level level) +{ + switch (level) + { + case Logger::Level::error: + return "error"; + case Logger::Level::warning: + return "warning"; + case Logger::Level::severe: + return "severe"; + case Logger::Level::info: + return "info"; + case Logger::Level::fine: + return "fine"; + case Logger::Level::debug: + return "debug"; + default: + ThrowException("Invalid Level enum."); + } +} + +void Logger::log(Level level, const char *file, unsigned int line, const char *func, std::string_view message) +{ + if (!isLoggable(level)) + return; + + std::time_t now = std::time(nullptr); + + char timestamp[std::size(Logger::timestampFormat)]; + std::strftime(timestamp, std::size(Logger::timestampFormat), + "%FT%TZ", std::localtime(&now)); + + char outputInfo[1024]; + snprintf(outputInfo, 1024, "[%s](%s)[%s]@%s:%d:%s() ", timestamp, name.c_str(), getLevelName(level).data(), file, line, func); + logStream << outputInfo << message << '\n'; +} \ No newline at end of file diff --git a/src/utility/Logger.hpp b/src/utility/Logger.hpp new file mode 100644 index 0000000..b9b2418 --- /dev/null +++ b/src/utility/Logger.hpp @@ -0,0 +1,72 @@ +/** + * @file Logger.hpp + * @author Anand Doshi + * @date 2025-01-08 + * + * @copyright Copyright (c) 2025 + * + */ + +#pragma once + +#include +#include + +class Logger +{ +public: + enum class Filter + { + quiet, + verbose, + important + }; + + enum class Level + { + debug, + fine, + info, + severe, + warning, + error + }; + + Logger(std::string_view name) + : Logger{name, Filter::important, std::cout} + { + } + + Logger(std::string_view name, Filter filter) + : Logger{name, filter, std::cout} + { + } + + Logger(std::string_view name, Filter filter, std::ostream &logStream) + : name{name}, filter{filter}, logStream{logStream} + { + } + + Logger() = delete; + + void log(Level level, const char *file, unsigned int line, const char *func, std::string_view message); + + inline void error(const char *file, unsigned int line, const char *func, std::string_view message) { log(Level::error, file, line, func, message); }; + inline void warning(const char *file, unsigned int line, const char *func, std::string_view message) { log(Level::warning, file, line, func, message); }; + inline void severe(const char *file, unsigned int line, const char *func, std::string_view message) { log(Level::severe, file, line, func, message); }; + inline void info(const char *file, unsigned int line, const char *func, std::string_view message) { log(Level::info, file, line, func, message); }; + inline void fine(const char *file, unsigned int line, const char *func, std::string_view message) { log(Level::fine, file, line, func, message); }; + inline void debug(const char *file, unsigned int line, const char *func, std::string_view message) { log(Level::debug, file, line, func, message); }; + +private: + const std::string name; + const Filter filter; + std::ostream &logStream; + + // ISO 8601 date time format + inline static const std::string timestampFormat{"yyyy-mm-ddThh:mm:ssZ"}; + + bool isLoggable(Level level); + + static constexpr std::string_view getLevelName(Level level); +}; \ No newline at end of file