project-aster/aster_core/logger.h

181 lines
6.1 KiB
C++

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