// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "logger.h" #include #include #include #include #include #include #include namespace dragonx { namespace util { Logger::Logger() = default; Logger::~Logger() { if (file_.is_open()) { file_.close(); } } Logger& Logger::instance() { static Logger logger; return logger; } bool Logger::init(const std::string& path) { std::lock_guard lock(mutex_); if (file_.is_open()) { file_.close(); } // W7-4: cap the log's growth — if the existing file is already large, rotate it to a single .1 // backup before reopening in append mode, so a long-lived or verbose session can't grow it // without bound. { std::error_code ec; const auto sz = std::filesystem::file_size(path, ec); constexpr std::uintmax_t kMaxLogBytes = 10ull * 1024ull * 1024ull; // 10 MB if (!ec && sz > kMaxLogBytes) { std::filesystem::rename(path, path + ".1", ec); // replaces any previous .1 backup if (ec) std::filesystem::remove(path, ec); // fall back to truncation if rename fails } } file_.open(path, std::ios::out | std::ios::app); initialized_ = file_.is_open(); if (initialized_) { // Write the banner directly, NOT via write(): write() re-locks the non-recursive mutex_ we // already hold here, which would deadlock (latent — init() was previously never called, W7-2). file_ << "=== Logger initialized ===" << std::endl; file_.flush(); } return initialized_; } void Logger::write(const std::string& message) { std::lock_guard lock(mutex_); // Get current timestamp auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); auto ms = std::chrono::duration_cast( now.time_since_epoch()) % 1000; std::stringstream ss; // Reachable from worker/monitor threads — std::localtime shares a process-wide static tm, so use the // reentrant variant into a local tm (the logger's own mutex can't protect other localtime callers). std::tm tmv{}; #ifdef _WIN32 localtime_s(&tmv, &time); #else localtime_r(&time, &tmv); #endif ss << std::put_time(&tmv, "%Y-%m-%d %H:%M:%S"); ss << '.' << std::setfill('0') << std::setw(3) << ms.count(); ss << " | " << message; std::string line = ss.str(); // Write to file if open if (file_.is_open()) { file_ << line << std::endl; file_.flush(); } // Also write to stdout in debug mode #ifdef DRAGONX_DEBUG printf("%s\n", line.c_str()); #endif // Forward to callback (e.g. ConsoleTab) with the raw message if (callback_) { callback_(message); } } void Logger::writef(const char* format, ...) { char buffer[4096]; va_list args; va_start(args, format); vsnprintf(buffer, sizeof(buffer), format, args); va_end(args); write(buffer); } void Logger::setCallback(std::function cb) { std::lock_guard lock(mutex_); callback_ = std::move(cb); } } // namespace util } // namespace dragonx