OrangePi3588Media/include/utils/logger.h

152 lines
4.1 KiB
C++

#pragma once
#include <chrono>
#include <cstddef>
#include <cctype>
#include <deque>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
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<char>(std::tolower(static_cast<unsigned char>(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<std::mutex> lock(mu_);
max_lines_ = (n == 0) ? 1 : n;
TrimLocked();
}
void SetLevel(LogLevel lvl) {
std::lock_guard<std::mutex> lock(mu_);
min_level_ = lvl;
}
LogLevel GetLevel() const {
std::lock_guard<std::mutex> lock(mu_);
return min_level_;
}
void Log(LogLevel lvl, const std::string& msg) {
{
std::lock_guard<std::mutex> lock(mu_);
if (static_cast<int>(lvl) < static_cast<int>(min_level_)) return;
}
const std::string line = FormatLine(lvl, msg);
{
std::lock_guard<std::mutex> 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<std::string> RecentLines(size_t limit) const {
std::lock_guard<std::mutex> lock(mu_);
if (limit == 0) return {};
if (limit > lines_.size()) limit = lines_.size();
std::vector<std::string> 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<milliseconds>(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<std::string> 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