// ============================================= // Aster: logger.h // Copyright (c) 2020-2024 Anish Bhobe // ============================================= #pragma once #include "constants.h" #include #include #include struct Logger { enum class LogType : u32 { eError, eWarning, eInfo, eDebug, eVerbose, }; u32 minimum_logging_level{ cast(LogType::eDebug) }; void set_minimum_logging_level(LogType _log_type) { minimum_logging_level = cast(_log_type); } template constexpr static const char *to_cstr() { if constexpr (LogLevel == LogType::eError) return "[ERROR]:"; if constexpr (LogLevel == LogType::eWarning) return "[WARN]: "; if constexpr (LogLevel == LogType::eInfo) return "[INFO]: "; if constexpr (LogLevel == LogType::eDebug) return "[DEBUG]:"; if constexpr (LogLevel == LogType::eVerbose) return "[VERB]: "; } template constexpr static const char *to_color_cstr() { if constexpr (LogLevel == LogType::eError) return ANSI_Red; if constexpr (LogLevel == LogType::eWarning) return ANSI_Yellow; if constexpr (LogLevel == LogType::eInfo) return ANSI_Green; if constexpr (LogLevel == LogType::eDebug) return ANSI_White; if constexpr (LogLevel == LogType::eVerbose) return ANSI_Blue; } template void log(const std::string_view &_message, const char *_loc, u32 _line) const { if (cast(LogLevel) <= minimum_logging_level) { fmt::println("{}{} {}{}| at {}:{}{}", to_color_cstr(), to_cstr(), _message.data(), ANSI_Black, _loc, _line, ANSI_Reset); } #if !defined(NDEBUG) if constexpr (LogLevel == LogType::eError) { debug_break(); } #endif // !defined(NDEBUG) } template void log_cond(const char *_expr_str, const std::string_view &_message, const char *_loc, u32 _line) const { if (cast(LogLevel) <= minimum_logging_level) { fmt::println("{}{} ({}) {}{}| at {}:{}{}", to_color_cstr(), to_cstr(), _expr_str, _message.data(), ANSI_Black, _loc, _line, ANSI_Reset); } #if !defined(NDEBUG) if constexpr (LogLevel == LogType::eError) { debug_break(); } #endif // !defined(NDEBUG) } }; extern Logger g_logger; #define ERROR(...) g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define WARN(...) g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define INFO(...) g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ERROR_IF(expr, ...) \ if (cast(expr)) [[unlikely]] \ g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define WARN_IF(expr, ...) \ if (cast(expr)) [[unlikely]] \ g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define INFO_IF(expr, ...) \ if (cast(expr)) \ g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_ERROR(expr, ...) \ ; \ else if (cast(expr)) [[unlikely]] g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_WARN(expr, ...) \ ; \ else if (cast(expr)) [[unlikely]] g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_INFO(expr, ...) \ ; \ else if (cast(expr)) g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_ERROR(...) \ ; \ else [[unlikely]] g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_WARN(...) \ ; \ else [[unlikely]] g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_INFO(...) \ ; \ else g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #if !defined(DEBUG_LOG_DISABLED) && !defined(NDEBUG) #define DEBUG(...) g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define DEBUG_IF(expr, ...) \ if (cast(expr)) \ g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_DEBUG(expr, ...) \ ; \ else if (cast(expr)) g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_DEBUG(...) \ ; \ else [[unlikely]] g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #else // !defined(DEBUG_LOG_DISABLED) #define DEBUG(msg) \ {} #define DEBUG_IF(expr, msg) \ if (expr) \ (void)msg #define ELSE_IF_DEBUG(expr, msg) \ ; \ if (expr) \ (void)msg #define ELSE_DEBUG(msg) \ ; \ {} #endif // !defined(DEBUG_LOG_DISABLED) #if !defined(VERBOSE_LOG_DISABLED) && !defined(NDEBUG) #define VERBOSE(...) g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define VERBOSE_IF(expr, ...) \ if (cast(expr)) \ g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_VERBOSE(expr, ...) \ ; \ else if (cast(expr)) g_logger.log_cond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_VERBOSE(...) \ ; \ else g_logger.log(fmt::format(__VA_ARGS__), __FILE__, __LINE__) #else // !defined(DEBUG_LOG_DISABLED) #define VERBOSE(msg) \ {} #define VERBOSE_IF(expr, msg) \ if (expr) \ (void)msg #define ELSE_IF_VERBOSE(expr, msg) \ ; \ if (expr) \ (void)msg #define ELSE_VERBOSE(msg) \ ; \ {} #endif // !defined(VERBOSE_LOG_DISABLED) #define DO(code) , code #define CRASH(code) exit(cast(code)) #define THEN_CRASH(code) , CRASH(code)