#pragma once #include #include #include #include #include #include #include namespace wowee { namespace core { // Suppress the wingdi.h "#define ERROR 0" macro for the entire header so that // LogLevel::ERROR inside template bodies compiles correctly on Windows. #ifdef _WIN32 #pragma push_macro("ERROR") #undef ERROR #endif enum class LogLevel { DEBUG, INFO, WARNING, ERROR, FATAL }; class Logger { public: static Logger& getInstance(); void log(LogLevel level, const std::string& message); void setLogLevel(LogLevel level); bool shouldLog(LogLevel level) const; template void debug(Args&&... args) { if (!shouldLog(LogLevel::DEBUG)) return; log(LogLevel::DEBUG, format(std::forward(args)...)); } template void info(Args&&... args) { if (!shouldLog(LogLevel::INFO)) return; log(LogLevel::INFO, format(std::forward(args)...)); } template void warning(Args&&... args) { if (!shouldLog(LogLevel::WARNING)) return; log(LogLevel::WARNING, format(std::forward(args)...)); } template void error(Args&&... args) { if (!shouldLog(LogLevel::ERROR)) return; log(LogLevel::ERROR, format(std::forward(args)...)); } template void fatal(Args&&... args) { if (!shouldLog(LogLevel::FATAL)) return; log(LogLevel::FATAL, format(std::forward(args)...)); } private: static constexpr int kDefaultMinLevelValue = #if defined(NDEBUG) || defined(WOWEE_RELEASE_LOGGING) static_cast(LogLevel::WARNING); #else static_cast(LogLevel::INFO); #endif Logger() = default; ~Logger() = default; Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; template std::string format(Args&&... args) { std::ostringstream oss; (oss << ... << args); return oss.str(); } std::atomic minLevel_{kDefaultMinLevelValue}; std::mutex mutex; std::ofstream fileStream; bool fileReady = false; bool echoToStdout_ = true; std::chrono::steady_clock::time_point lastFlushTime_{}; uint32_t flushIntervalMs_ = 250; bool dedupeEnabled_ = true; uint32_t dedupeWindowMs_ = 250; LogLevel lastLevel_ = LogLevel::DEBUG; std::string lastMessage_; std::chrono::steady_clock::time_point lastMessageTime_{}; uint64_t suppressedCount_ = 0; void emitLineLocked(LogLevel level, const std::string& message); void flushSuppressedLocked(); void ensureFile(); }; // Convenience macros #define LOG_DEBUG(...) wowee::core::Logger::getInstance().debug(__VA_ARGS__) #define LOG_INFO(...) wowee::core::Logger::getInstance().info(__VA_ARGS__) #define LOG_WARNING(...) wowee::core::Logger::getInstance().warning(__VA_ARGS__) #define LOG_ERROR(...) wowee::core::Logger::getInstance().error(__VA_ARGS__) #define LOG_FATAL(...) wowee::core::Logger::getInstance().fatal(__VA_ARGS__) } // namespace core } // namespace wowee // Restore the ERROR macro now that all LogLevel::ERROR references are done. #ifdef _WIN32 #pragma pop_macro("ERROR") #endif