// ============================================= // Aster: logger.h // Copyright (c) 2020-2024 Anish Bhobe // ============================================= #pragma once #include "constants.h" #include #include struct Logger { enum class LogType : u32 { eError, eWarning, eInfo, eDebug, eVerbose, }; u32 m_MinimumLoggingLevel{Cast(LogType::eDebug)}; void SetMinimumLoggingLevel(LogType logType) { m_MinimumLoggingLevel = Cast(logType); } template constexpr static const char * ToCstr() { if constexpr (TLogLevel == LogType::eError) return "[ERROR]:"; if constexpr (TLogLevel == LogType::eWarning) return "[WARN]: "; if constexpr (TLogLevel == LogType::eInfo) return "[INFO]: "; if constexpr (TLogLevel == LogType::eDebug) return "[DEBUG]:"; if constexpr (TLogLevel == LogType::eVerbose) return "[VERB]: "; } template constexpr static const char * ToColorCstr() { if constexpr (TLogLevel == LogType::eError) return ansi_color::Red; if constexpr (TLogLevel == LogType::eWarning) return ansi_color::Yellow; if constexpr (TLogLevel == LogType::eInfo) return ansi_color::Green; if constexpr (TLogLevel == LogType::eDebug) return ansi_color::White; if constexpr (TLogLevel == LogType::eVerbose) return ansi_color::Blue; } template void Log(const std::string_view &message, const char *loc, u32 line) const { if (Cast(TLogLevel) <= m_MinimumLoggingLevel) { fmt::println("{}{} {} {} at {}:{}{}", ToColorCstr(), ToCstr(), message.data(), ansi_color::Black, loc, line, ansi_color::Reset); } #if !defined(NDEBUG) if constexpr (TLogLevel == LogType::eError) { debug_break(); } #endif // !defined(NDEBUG) } template void LogCond(const char *exprStr, const std::string_view &message, const char *loc, u32 line) const { if (Cast(TLogLevel) <= m_MinimumLoggingLevel) { fmt::println("{}{} ({}) {} {} at {}:{}{}", ToColorCstr(), ToCstr(), exprStr, message.data(), ansi_color::Black, loc, line, ansi_color::Reset); } #if !defined(NDEBUG) if constexpr (TLogLevel == LogType::eError) { debug_break(); } #endif // !defined(NDEBUG) } }; extern Logger g_Logger; #define MIN_LOG_LEVEL(LOG_LVL) g_Logger.SetMinimumLoggingLevel(LOG_LVL) #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.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define WARN_IF(expr, ...) \ if (Cast(expr)) [[unlikely]] \ g_Logger.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define INFO_IF(expr, ...) \ if (Cast(expr)) \ g_Logger.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_ERROR(expr, ...) \ ; \ else if (Cast(expr)) \ [[unlikely]] g_Logger.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_WARN(expr, ...) \ ; \ else if (Cast(expr)) \ [[unlikely]] g_Logger.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_INFO(expr, ...) \ ; \ else if (Cast(expr)) \ g_Logger.LogCond(#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.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_DEBUG(expr, ...) \ ; \ else if (Cast(expr)) \ g_Logger.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_DEBUG(...) \ ; \ else 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.LogCond(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__) #define ELSE_IF_VERBOSE(expr, ...) \ ; \ else if (Cast(expr)) \ g_Logger.LogCond(#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 ABORT(code) exit(Cast(code)) #define THEN_ABORT(code) , ABORT(code)