#pragma once #include #include #include #include namespace Log { struct SRollingLogFollow { std::unordered_map m_socketToRollingLogFollowQueue; std::shared_mutex m_mutex; bool m_running = false; static constexpr size_t ROLLING_LOG_FOLLOW_TOO_BIG = 8192; // Returns true if the queue is empty for the given socket bool isEmpty(int socket) { std::shared_lock r(m_mutex); return m_socketToRollingLogFollowQueue[socket].empty(); } std::string debugInfo() { std::shared_lock r(m_mutex); return std::format("RollingLogFollow, got {} connections", m_socketToRollingLogFollowQueue.size()); } std::string getLog(int socket) { std::unique_lock w(m_mutex); const std::string ret = m_socketToRollingLogFollowQueue[socket]; m_socketToRollingLogFollowQueue[socket] = ""; return ret; }; void addLog(const std::string_view& log) { std::unique_lock w(m_mutex); m_running = true; std::vector to_erase; for (const auto& p : m_socketToRollingLogFollowQueue) { m_socketToRollingLogFollowQueue[p.first] += log; m_socketToRollingLogFollowQueue[p.first] += "\n"; } } bool isRunning() { std::shared_lock r(m_mutex); return m_running; } void stopFor(int socket) { std::unique_lock w(m_mutex); m_socketToRollingLogFollowQueue.erase(socket); if (m_socketToRollingLogFollowQueue.empty()) m_running = false; } void startFor(int socket) { std::unique_lock w(m_mutex); m_socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket); m_running = true; } static SRollingLogFollow& get() { static SRollingLogFollow instance; static std::mutex gm; std::lock_guard lock(gm); return instance; }; }; }