From 5e4e1a53966707074fd2beb757603ffcec60f463 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Mon, 15 Sep 2025 04:00:26 +0300 Subject: [PATCH 1/4] fix(logging): refine emscripten executor and rotation --- .../logit_cpp/logit/detail/TaskExecutor.hpp | 76 ++++++++++++++----- .../logit_cpp/logit/loggers/FileLogger.hpp | 4 +- .../logit/loggers/UniqueFileLogger.hpp | 19 ++--- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/include/logit_cpp/logit/detail/TaskExecutor.hpp b/include/logit_cpp/logit/detail/TaskExecutor.hpp index 670e4cf..08a9d13 100644 --- a/include/logit_cpp/logit/detail/TaskExecutor.hpp +++ b/include/logit_cpp/logit/detail/TaskExecutor.hpp @@ -9,6 +9,7 @@ #include #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_PTHREADS__) #include +#include #include #else #include @@ -36,22 +37,39 @@ namespace logit { namespace detail { void add_task(std::function task) { if (!task) return; - if (m_max_queue_size > 0 && m_tasks.size() >= m_max_queue_size) { - switch (m_overflow_policy) { - case QueuePolicy::DropNewest: - ++m_dropped_tasks; - return; - case QueuePolicy::DropOldest: - m_tasks.pop_front(); - ++m_dropped_tasks; - break; - case QueuePolicy::Block: - drain(); + for (;;) { + bool schedule = false; + { + std::lock_guard lk(m_mutex); + if (m_max_queue_size > 0 && m_tasks.size() >= m_max_queue_size) { + switch (m_overflow_policy) { + case QueuePolicy::DropNewest: + ++m_dropped_tasks; + return; + case QueuePolicy::DropOldest: + m_tasks.pop_front(); + ++m_dropped_tasks; + break; + case QueuePolicy::Block: + break; // handled after unlocking + } + if (m_overflow_policy == QueuePolicy::Block && m_tasks.size() >= m_max_queue_size) { + // fall through to drain outside lock + } else { + m_tasks.emplace_back(std::move(task)); + schedule = !m_scheduled; + m_scheduled = m_scheduled || schedule; + break; + } + } else { + m_tasks.emplace_back(std::move(task)); + schedule = !m_scheduled; + m_scheduled = m_scheduled || schedule; break; + } } + drain(); } - const bool schedule = m_tasks.empty(); - m_tasks.push_back(std::move(task)); if (schedule) { emscripten_async_call(&TaskExecutor::drain_thunk, this, 0); } @@ -60,11 +78,21 @@ namespace logit { namespace detail { void wait() { drain(); } void shutdown() { drain(); } - void set_max_queue_size(std::size_t size) { m_max_queue_size = size; } - void set_queue_policy(QueuePolicy policy) { m_overflow_policy = policy; } + void set_max_queue_size(std::size_t size) { + std::lock_guard lk(m_mutex); + m_max_queue_size = size; + } + void set_queue_policy(QueuePolicy policy) { + std::lock_guard lk(m_mutex); + m_overflow_policy = policy; + } private: - TaskExecutor() : m_max_queue_size(0), m_overflow_policy(QueuePolicy::Block), m_dropped_tasks(0) {} + TaskExecutor() + : m_max_queue_size(0), + m_overflow_policy(QueuePolicy::Block), + m_dropped_tasks(0), + m_scheduled(false) {} ~TaskExecutor() = default; TaskExecutor(const TaskExecutor&) = delete; TaskExecutor& operator=(const TaskExecutor&) = delete; @@ -72,18 +100,28 @@ namespace logit { namespace detail { TaskExecutor& operator=(TaskExecutor&&) = delete; std::deque> m_tasks; + std::mutex m_mutex; std::size_t m_max_queue_size; QueuePolicy m_overflow_policy; std::atomic m_dropped_tasks; + bool m_scheduled; static void drain_thunk(void* arg) { static_cast(arg)->drain(); } void drain() { - while (!m_tasks.empty()) { - auto task = std::move(m_tasks.front()); - m_tasks.pop_front(); + for (;;) { + std::function task; + { + std::lock_guard lk(m_mutex); + if (m_tasks.empty()) { + m_scheduled = false; + break; + } + task = std::move(m_tasks.front()); + m_tasks.pop_front(); + } task(); } } diff --git a/include/logit_cpp/logit/loggers/FileLogger.hpp b/include/logit_cpp/logit/loggers/FileLogger.hpp index 2b05a1f..4294ac0 100644 --- a/include/logit_cpp/logit/loggers/FileLogger.hpp +++ b/include/logit_cpp/logit/loggers/FileLogger.hpp @@ -232,6 +232,8 @@ namespace logit { void wait() override { if (!m_config.async) return; detail::TaskExecutor::get_instance().wait(); + std::lock_guard lock(m_mutex); + if (m_file.is_open()) m_file.flush(); } private: @@ -333,7 +335,7 @@ namespace logit { } } if (m_file.is_open()) { - m_file << message << std::endl; + m_file << message << '\n'; m_current_file_size += static_cast(message.size() + 1); } remove_old_logs(); diff --git a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp index 7baf689..ce49f13 100644 --- a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp +++ b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -396,7 +395,7 @@ namespace logit { if (!fs::is_regular_file(entry.status())) continue; std::string filename = entry.path().filename().string(); if (is_valid_log_filename(filename)) { - const int64_t file_ts = get_timestamp_from_filename(filename); + const int64_t file_ts = get_date_ts_from_filename(filename); if (file_ts < threshold_ts) { fs::remove(entry.path()); } @@ -407,7 +406,7 @@ namespace logit { for (const auto& file_path : file_list) { std::string filename = get_file_name(file_path); if (is_valid_log_filename(filename)) { - const int64_t file_ts = get_timestamp_from_filename(filename); + const int64_t file_ts = get_date_ts_from_filename(filename); if (file_ts < threshold_ts) { # if defined(_WIN32) remove(utf8_to_ansi(file_path).c_str()); @@ -424,16 +423,14 @@ namespace logit { /// \param filename The filename to check. /// \return True if the filename matches the pattern, false otherwise. bool is_valid_log_filename(const std::string& filename) const { - static const std::regex pattern(R"((\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-\d{3})-[a-zA-Z0-9]{1,}\.log)"); - return std::regex_match(filename, pattern); + return filename.size() >= 10 && filename[4] == '-' && filename[7] == '-'; } - /// \brief Extracts the timestamp from the filename. - /// \param filename The filename to extract the timestamp from. - /// \return The timestamp in milliseconds. - int64_t get_timestamp_from_filename(const std::string& filename) const { - std::string datetime_str = filename.substr(0, 10); // "YYYY-MM-DD" - return time_shield::ts(datetime_str); + /// \brief Extracts the date timestamp from the filename. + /// \param filename The filename to extract the date from. + /// \return The date timestamp. + int64_t get_date_ts_from_filename(const std::string& filename) const { + return time_shield::ts(filename.substr(0, 10)); } /// \brief Gets the current timestamp in milliseconds. From b4b6c076318cca80f36ec85bef85c7b50bba39b7 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Mon, 15 Sep 2025 04:40:51 +0300 Subject: [PATCH 2/4] fix(logging): validate compressed unique log filenames --- include/logit_cpp/logit/loggers/UniqueFileLogger.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp index ce49f13..67e2126 100644 --- a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp +++ b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp @@ -18,6 +18,7 @@ #include #include #include +#include namespace logit { @@ -423,7 +424,9 @@ namespace logit { /// \param filename The filename to check. /// \return True if the filename matches the pattern, false otherwise. bool is_valid_log_filename(const std::string& filename) const { - return filename.size() >= 10 && filename[4] == '-' && filename[7] == '-'; + static const std::regex pattern( + R"((\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-\d{3})-[a-zA-Z0-9]{1,}\.log(?:\.gz|\.zst)?)"); + return std::regex_match(filename, pattern); } /// \brief Extracts the date timestamp from the filename. From 6167919dc45ed18bccb90e7f352838b883e83cb2 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Mon, 15 Sep 2025 04:40:55 +0300 Subject: [PATCH 3/4] fix(logger): refine unique log filename regex --- include/logit_cpp/logit/loggers/UniqueFileLogger.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp index 67e2126..6a008b3 100644 --- a/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp +++ b/include/logit_cpp/logit/loggers/UniqueFileLogger.hpp @@ -425,7 +425,7 @@ namespace logit { /// \return True if the filename matches the pattern, false otherwise. bool is_valid_log_filename(const std::string& filename) const { static const std::regex pattern( - R"((\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-\d{3})-[a-zA-Z0-9]{1,}\.log(?:\.gz|\.zst)?)"); + R"(^(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-\d{3})-[-_A-Za-z0-9]+\.log(\.gz|\.zst)?$)"); return std::regex_match(filename, pattern); } From 97f993f78a17c75e0c58471e10a3b8007d1bb2e0 Mon Sep 17 00:00:00 2001 From: Aster Seker Date: Mon, 15 Sep 2025 04:49:01 +0300 Subject: [PATCH 4/4] fix(task-executor): keep schedule flag in scope --- include/logit_cpp/logit/detail/TaskExecutor.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/logit_cpp/logit/detail/TaskExecutor.hpp b/include/logit_cpp/logit/detail/TaskExecutor.hpp index 08a9d13..4475f8d 100644 --- a/include/logit_cpp/logit/detail/TaskExecutor.hpp +++ b/include/logit_cpp/logit/detail/TaskExecutor.hpp @@ -37,8 +37,9 @@ namespace logit { namespace detail { void add_task(std::function task) { if (!task) return; + bool schedule = false; for (;;) { - bool schedule = false; + schedule = false; { std::lock_guard lk(m_mutex); if (m_max_queue_size > 0 && m_tasks.size() >= m_max_queue_size) {