From 0db21e96deba01dac20eaaec528b8112ec1313cc Mon Sep 17 00:00:00 2001 From: Mihai Fufezan Date: Sun, 4 Aug 2024 19:15:44 +0300 Subject: [PATCH] debug: init --- include/hyprutils/debug/Log.hpp | 76 ++++++++++++++++++++ include/hyprutils/debug/RollingLogFollow.hpp | 70 ++++++++++++++++++ src/debug/Log.cpp | 67 +++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 include/hyprutils/debug/Log.hpp create mode 100644 include/hyprutils/debug/RollingLogFollow.hpp create mode 100644 src/debug/Log.cpp diff --git a/include/hyprutils/debug/Log.hpp b/include/hyprutils/debug/Log.hpp new file mode 100644 index 0000000..16e90dd --- /dev/null +++ b/include/hyprutils/debug/Log.hpp @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "RollingLogFollow.hpp" + +#define LOGMESSAGESIZE 1024 +#define ROLLING_LOG_SIZE 4096 + +enum LogLevel { + NONE = -1, + LOG = 0, + WARN, + ERR, + CRIT, + INFO, + TRACE +}; + +namespace Debug { + inline std::string logFile; + inline std::ofstream logOfs; + inline int64_t* const* disableLogs = nullptr; + inline int64_t* const* disableTime = nullptr; + inline bool disableStdout = false; + inline bool trace = false; + inline bool shuttingDown = false; + inline int64_t* const* coloredLogs = nullptr; + + inline std::string rollingLog = ""; // rolling log contains the ROLLING_LOG_SIZE tail of the log + inline std::mutex logMutex; + + void init(const std::string& IS, const std::string& name); + void close(); + + // + void log(LogLevel level, std::string str); + + template + void log(LogLevel level, std::format_string fmt, Args&&... args) { + std::lock_guard guard(logMutex); + + if (level == TRACE && !trace) + return; + + if (shuttingDown) + return; + + std::string logMsg = ""; + + // print date and time to the ofs + if (disableTime && !**disableTime) { +#ifndef _LIBCPP_VERSION + const auto zt = std::chrono::zoned_time{std::chrono::current_zone(), std::chrono::system_clock::now()}; + const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor(zt.get_local_time())}; +#else + // TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready + const auto hms = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}; +#endif + logMsg += std::format("[{}] ", hms); + } + + // no need for try {} catch {} because std::format_string ensures that vformat never throw std::format_error + // because + // 1. any faulty format specifier that sucks will cause a compilation error. + // 2. and `std::bad_alloc` is catastrophic, (Almost any operation in stdlib could throw this.) + // 3. this is actually what std::format in stdlib does + logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); + + log(level, logMsg); + logMutex.unlock(); + } +}; diff --git a/include/hyprutils/debug/RollingLogFollow.hpp b/include/hyprutils/debug/RollingLogFollow.hpp new file mode 100644 index 0000000..f140010 --- /dev/null +++ b/include/hyprutils/debug/RollingLogFollow.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Debug { + struct RollingLogFollow { + std::unordered_map socketToRollingLogFollowQueue; + std::shared_mutex m; + bool 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); + return socketToRollingLogFollowQueue[socket].empty(); + } + + std::string DebugInfo() { + std::shared_lock r(m); + return std::format("RollingLogFollow, got {} connections", socketToRollingLogFollowQueue.size()); + } + + std::string GetLog(int socket) { + std::unique_lock w(m); + + const std::string ret = socketToRollingLogFollowQueue[socket]; + socketToRollingLogFollowQueue[socket] = ""; + + return ret; + }; + + void AddLog(std::string log) { + std::unique_lock w(m); + running = true; + std::vector to_erase; + for (const auto& p : socketToRollingLogFollowQueue) + socketToRollingLogFollowQueue[p.first] += log + "\n"; + } + + bool IsRunning() { + std::shared_lock r(m); + return running; + } + + void StopFor(int socket) { + std::unique_lock w(m); + socketToRollingLogFollowQueue.erase(socket); + if (socketToRollingLogFollowQueue.empty()) + running = false; + } + + void StartFor(int socket) { + std::unique_lock w(m); + socketToRollingLogFollowQueue[socket] = std::format("[LOG] Following log to socket: {} started\n", socket); + running = true; + } + + static RollingLogFollow& Get() { + static RollingLogFollow instance; + static std::mutex gm; + std::lock_guard lock(gm); + return instance; + }; + }; +} diff --git a/src/debug/Log.cpp b/src/debug/Log.cpp new file mode 100644 index 0000000..da14ff6 --- /dev/null +++ b/src/debug/Log.cpp @@ -0,0 +1,67 @@ +#include +#include +#include + + +void Debug::init(const std::string& IS, const std::string& name) { + logFile = IS + "/" + name + ".log"; + logOfs.open(logFile, std::ios::out | std::ios::app); +} + +void Debug::close() { + logOfs.close(); +} + +void Debug::log(LogLevel level, std::string str) { + if (level == TRACE && !trace) + return; + + if (shuttingDown) + return; + + std::string coloredStr = str; + switch (level) { + case LOG: + str = "[LOG] " + str; + coloredStr = str; + break; + case WARN: + str = "[WARN] " + str; + coloredStr = "\033[1;33m" + str + "\033[0m"; // yellow + break; + case ERR: + str = "[ERR] " + str; + coloredStr = "\033[1;31m" + str + "\033[0m"; // red + break; + case CRIT: + str = "[CRITICAL] " + str; + coloredStr = "\033[1;35m" + str + "\033[0m"; // magenta + break; + case INFO: + str = "[INFO] " + str; + coloredStr = "\033[1;32m" + str + "\033[0m"; // green + break; + case TRACE: + str = "[TRACE] " + str; + coloredStr = "\033[1;34m" + str + "\033[0m"; // blue + break; + default: break; + } + + rollingLog += str + "\n"; + if (rollingLog.size() > ROLLING_LOG_SIZE) + rollingLog = rollingLog.substr(rollingLog.size() - ROLLING_LOG_SIZE); + + if (RollingLogFollow::Get().IsRunning()) + RollingLogFollow::Get().AddLog(str); + + if (!disableLogs || !**disableLogs) { + // log to a file + logOfs << str << "\n"; + logOfs.flush(); + } + + // log it to the stdout too. + if (!disableStdout) + std::cout << ((coloredLogs && !**coloredLogs) ? str : coloredStr) << "\n"; +}