#include "log_buffer.hpp" #include #include #include "dusk/logging.h" namespace dusk::mods::log { namespace { struct BufferState { std::mutex mutex; std::vector ring; size_t head = 0; size_t count = 0; uint64_t nextSeq = 0; std::vector modIds; uint16_t intern(std::string_view modId) { for (size_t i = 0; i < modIds.size(); ++i) { if (modIds[i] == modId) { return static_cast(i); } } modIds.emplace_back(modId); return static_cast(modIds.size() - 1); } }; BufferState g_buffer; } // namespace void emit(Source source, const std::string& modId, LogLevel level, const std::string& message) { const auto timeMs = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); { std::lock_guard lock{g_buffer.mutex}; if (g_buffer.ring.empty()) { g_buffer.ring.resize(k_capacity); } auto& slot = g_buffer.ring[(g_buffer.head + g_buffer.count) % k_capacity]; if (g_buffer.count == k_capacity) { g_buffer.head = (g_buffer.head + 1) % k_capacity; } else { ++g_buffer.count; } slot.seq = g_buffer.nextSeq++; slot.timeMs = timeMs; slot.level = level; slot.modIndex = g_buffer.intern(modId); slot.source = source; // assign() reuses the slot's capacity once the ring has wrapped slot.message.assign(message); } auto logLevel = borealis::LogLevel::Info; switch (level) { case LOG_LEVEL_TRACE: logLevel = borealis::LogLevel::Trace; break; case LOG_LEVEL_DEBUG: logLevel = borealis::LogLevel::Debug; break; case LOG_LEVEL_INFO: logLevel = borealis::LogLevel::Info; break; case LOG_LEVEL_WARN: logLevel = borealis::LogLevel::Warning; break; case LOG_LEVEL_ERROR: logLevel = borealis::LogLevel::Error; break; } const borealis::Log modLog{modId.c_str()}; modLog.report(logLevel, "{}", message); } Range copy_since(uint64_t sinceSeq, std::vector& out) { std::lock_guard lock{g_buffer.mutex}; const Range range{ .firstSeq = g_buffer.nextSeq - g_buffer.count, .nextSeq = g_buffer.nextSeq, }; for (uint64_t seq = std::max(sinceSeq, range.firstSeq); seq < range.nextSeq; ++seq) { out.push_back(g_buffer.ring[(g_buffer.head + (seq - range.firstSeq)) % k_capacity]); } return range; } std::vector ids() { std::lock_guard lock{g_buffer.mutex}; return g_buffer.modIds; } void clear() { std::lock_guard lock{g_buffer.mutex}; g_buffer.head = 0; g_buffer.count = 0; } } // namespace dusk::mods::log