#pragma once #include #include #include #include #include #include #include #include #include #include namespace rk3588 { enum class LogLevel { Debug, Info, Warn, Error }; inline const char* LogLevelToString(LogLevel lvl) { switch (lvl) { case LogLevel::Debug: return "debug"; case LogLevel::Info: return "info"; case LogLevel::Warn: return "warn"; case LogLevel::Error: return "error"; } return "info"; } inline bool ParseLogLevel(std::string s, LogLevel& out) { for (char& c : s) c = static_cast(std::tolower(static_cast(c))); if (s == "debug" || s == "d") { out = LogLevel::Debug; return true; } if (s == "info" || s == "i") { out = LogLevel::Info; return true; } if (s == "warn" || s == "warning" || s == "w") { out = LogLevel::Warn; return true; } if (s == "error" || s == "e") { out = LogLevel::Error; return true; } return false; } class Logger { public: static Logger& Instance() { static Logger inst; return inst; } void SetMaxLines(size_t n) { std::lock_guard lock(mu_); max_lines_ = (n == 0) ? 1 : n; TrimLocked(); } void SetLevel(LogLevel lvl) { std::lock_guard lock(mu_); min_level_ = lvl; } LogLevel GetLevel() const { std::lock_guard lock(mu_); return min_level_; } void Log(LogLevel lvl, const std::string& msg) { { std::lock_guard lock(mu_); if (static_cast(lvl) < static_cast(min_level_)) return; } const std::string line = FormatLine(lvl, msg); { std::lock_guard lock(mu_); lines_.push_back(line); TrimLocked(); } // Keep existing stdout/stderr logging style for easy board-side debugging. // Use fflush to ensure real-time log output if (lvl == LogLevel::Warn || lvl == LogLevel::Error) { std::cerr << line << "\n"; std::cerr.flush(); } else { std::cout << line << "\n"; std::cout.flush(); } fflush(stdout); fflush(stderr); } std::vector RecentLines(size_t limit) const { std::lock_guard lock(mu_); if (limit == 0) return {}; if (limit > lines_.size()) limit = lines_.size(); std::vector out; out.reserve(limit); const size_t start = lines_.size() - limit; for (size_t i = start; i < lines_.size(); ++i) { out.push_back(lines_[i]); } return out; } private: Logger() = default; static const char* LevelText(LogLevel lvl) { switch (lvl) { case LogLevel::Debug: return "D"; case LogLevel::Info: return "I"; case LogLevel::Warn: return "W"; case LogLevel::Error: return "E"; } return "I"; } static std::string FormatLine(LogLevel lvl, const std::string& msg) { using namespace std::chrono; const auto now = system_clock::now(); const auto ms = duration_cast(now.time_since_epoch()).count(); std::ostringstream oss; oss << "[" << ms << "]"; oss << "[" << LevelText(lvl) << "] "; oss << msg; return oss.str(); } void TrimLocked() { while (lines_.size() > max_lines_) { lines_.pop_front(); } } mutable std::mutex mu_; std::deque lines_; size_t max_lines_ = 2000; LogLevel min_level_ = LogLevel::Info; }; inline void LogDebug(const std::string& msg) { Logger::Instance().Log(LogLevel::Debug, msg); } inline void LogInfo(const std::string& msg) { Logger::Instance().Log(LogLevel::Info, msg); } inline void LogWarn(const std::string& msg) { Logger::Instance().Log(LogLevel::Warn, msg); } inline void LogError(const std::string& msg) { Logger::Instance().Log(LogLevel::Error, msg); } } // namespace rk3588