#include #include #include #include #include #include #include #include #include #include extern int isDebug; // 0 = OFF, 1 = ON std::string colorOut_256(std::string_view s, int ForeColor = 7) { std::ostringstream oss; oss << "\033[38;5;" << ForeColor << "m" << s << "\033[m"; return oss.str(); } std::string getSystemTime() { using namespace std::chrono; const auto now = system_clock::now(); const auto now_time_t = system_clock::to_time_t(now); const auto ms = duration_cast(now.time_since_epoch()).count() % 1000; std::tm ltm{}; localtime_s(<m, &now_time_t); std::ostringstream oss; oss << std::put_time(<m, "%Y-%m-%d %H:%M:%S") << '.' << std::setw(3) << std::setfill('0') << ms; return oss.str(); } // Logger mutex to ensure whole log lines are emitted atomically static std::mutex loggerMutex; void postLog(const std::string& message, int level) { const auto timeNow = getSystemTime(); constexpr std::array levelNames = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"}; constexpr std::array levelColors = {34, 27, 220, 196, 124}; const int idx = (level >= 0 && level < static_cast(levelNames.size())) ? level : 1; // default to INFO // Lock to make reading isDebug and all output atomic across threads std::scoped_lock lock(loggerMutex); if (isDebug == 0) { if (idx == 0) return; // skip DEBUG when debug is off std::cout << "[" << timeNow << " - " << levelNames[idx] << "] " << message << "\n"; return; } // isDebug == 1 -> colored output const std::string levelDisplay = colorOut_256(levelNames[idx], levelColors[idx]); std::ostream& out = (idx >= 3) ? static_cast(std::cerr) : static_cast(std::cout); out << "[" << timeNow << " - " << levelDisplay << "] " << message << "\n"; }