Files
ObsidianDragon/src/util/logger.cpp
DanS 82d3178119 fix: use reentrant localtime_r/localtime_s off the worker thread
std::localtime shares a process-wide static tm; both sites are reachable from background threads (the RPC price-parse on the worker thread, and Logger::write from worker/monitor threads), so a concurrent localtime call could clobber the struct between the call and the read. Copy into a local std::tm via localtime_r / localtime_s, matching the codebase's existing convention (console_tab rpcTraceTimestamp, app_network, etc.). From the console-tab audit; benign (garbled/stale timestamp only), no crash or state impact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 18:03:10 -05:00

111 lines
2.4 KiB
C++

// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3
#include "logger.h"
#include <cstdarg>
#include <ctime>
#include <chrono>
#include <iomanip>
#include <sstream>
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<std::mutex> lock(mutex_);
if (file_.is_open()) {
file_.close();
}
file_.open(path, std::ios::out | std::ios::app);
initialized_ = file_.is_open();
if (initialized_) {
write("=== Logger initialized ===");
}
return initialized_;
}
void Logger::write(const std::string& message)
{
std::lock_guard<std::mutex> 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<std::chrono::milliseconds>(
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<void(const std::string&)> cb)
{
std::lock_guard<std::mutex> lock(mutex_);
callback_ = std::move(cb);
}
} // namespace util
} // namespace dragonx