diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-10-11 19:30:36 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-10-11 19:30:36 +0800 |
commit | ea80856073d4896da5df78d60369b327346a0b56 (patch) | |
tree | c5bd85b508374066a66864ba1317129ac49db6fc | |
parent | 9768b98d46982501c3b34c171aa51389b06add3b (diff) | |
download | cru-ea80856073d4896da5df78d60369b327346a0b56.tar.gz cru-ea80856073d4896da5df78d60369b327346a0b56.tar.bz2 cru-ea80856073d4896da5df78d60369b327346a0b56.zip |
Split and log debug tag.
-rw-r--r-- | include/cru/base/StringUtil.h | 47 | ||||
-rw-r--r-- | include/cru/base/log/Logger.h | 8 | ||||
-rw-r--r-- | src/base/log/Logger.cpp | 43 | ||||
-rw-r--r-- | src/ui/host/WindowHost.cpp | 12 | ||||
-rw-r--r-- | test/base/StringUtilTest.cpp | 8 |
5 files changed, 101 insertions, 17 deletions
diff --git a/include/cru/base/StringUtil.h b/include/cru/base/StringUtil.h index 8f085283..ef6359ff 100644 --- a/include/cru/base/StringUtil.h +++ b/include/cru/base/StringUtil.h @@ -1,10 +1,57 @@ #pragma once #include "Base.h" +#include "Bitmask.h" #include <functional> +#include <stdexcept> #include <type_traits> +#include <vector> namespace cru { +namespace details { +struct SplitOptionsTag {}; +} // namespace details +using SplitOption = Bitmask<details::SplitOptionsTag>; +struct SplitOptions { + static constexpr SplitOption RemoveEmpty = SplitOption::FromOffset(1); +}; + +template <typename TString> +std::vector<TString> Split(const TString& str, const TString& sep, + SplitOption options = {}) { + using size_type = typename TString::size_type; + + if (sep.empty()) throw std::invalid_argument("Sep can't be empty."); + if (str.empty()) return {}; + + size_type current_pos = 0; + std::vector<TString> result; + + while (current_pos != TString::npos) { + if (current_pos == str.size()) { + if (!options.Has(SplitOptions::RemoveEmpty)) { + result.push_back({}); + } + break; + } + + auto next_pos = str.find(sep, current_pos); + auto sub = str.substr(current_pos, next_pos - current_pos); + if (!(options.Has(SplitOptions::RemoveEmpty) && sub.empty())) { + result.push_back(sub); + } + current_pos = + next_pos == TString::npos ? TString::npos : next_pos + sep.size(); + } + + return result; +} + +inline std::vector<std::string> Split(const char* str, const std::string& sep, + SplitOption options = {}) { + return Split<std::string>(std::string(str), sep, options); +} + using CodePoint = std::int32_t; constexpr CodePoint k_invalid_code_point = -1; diff --git a/include/cru/base/log/Logger.h b/include/cru/base/log/Logger.h index 1ef53654..f44bad72 100644 --- a/include/cru/base/log/Logger.h +++ b/include/cru/base/log/Logger.h @@ -6,7 +6,9 @@ #include <list> #include <memory> #include <mutex> +#include <string_view> #include <thread> +#include <unordered_set> #include <utility> #include <vector> @@ -40,6 +42,11 @@ class CRU_BASE_API Logger : public Object2 { void AddLogTarget(std::unique_ptr<ILogTarget> source); void RemoveLogTarget(ILogTarget* source); + void AddDebugTag(std::string tag); + void RemoveDebugTag(const std::string& tag); + void LoadDebugTagFromEnv(const char* env_var = "CRU_LOG_DEBUG", + std::string sep = ","); + public: void Log(LogLevel level, std::string tag, std::string message) { Log(LogInfo(level, std::move(tag), std::move(message))); @@ -59,6 +66,7 @@ class CRU_BASE_API Logger : public Object2 { private: std::mutex log_queue_mutex_; + std::unordered_set<std::string> debug_tags_; std::condition_variable log_queue_condition_variable_; std::list<LogInfo> log_queue_; bool log_stop_; diff --git a/src/base/log/Logger.cpp b/src/base/log/Logger.cpp index 87a56cd6..e8714f26 100644 --- a/src/base/log/Logger.cpp +++ b/src/base/log/Logger.cpp @@ -1,8 +1,10 @@ #include "cru/base/log/Logger.h" +#include "cru/base/StringUtil.h" #include "cru/base/log/StdioLogTarget.h" #include <algorithm> #include <condition_variable> +#include <cstdlib> #include <ctime> #include <format> #include <memory> @@ -68,7 +70,9 @@ std::string MakeLogFinalMessage(const LogInfo &log_info) { } } // namespace -Logger::Logger() : log_stop_(false), log_thread_(&Logger::LogThreadRun, this) {} +Logger::Logger() : log_stop_(false), log_thread_(&Logger::LogThreadRun, this) { + LoadDebugTagFromEnv(); +} Logger::~Logger() { { @@ -79,29 +83,43 @@ Logger::~Logger() { log_thread_.join(); } -void Logger::Log(LogInfo log_info) { -#ifndef CRU_DEBUG - if (log_info.level == LogLevel::Debug) { - return; +void Logger::AddDebugTag(std::string tag) { + std::unique_lock lock(log_queue_mutex_); + debug_tags_.insert(std::move(tag)); +} + +void Logger::RemoveDebugTag(const std::string &tag) { + std::unique_lock lock(log_queue_mutex_); + debug_tags_.erase(tag); +} + +void Logger::LoadDebugTagFromEnv(const char *env_var, std::string sep) { + auto env = std::getenv(env_var); + if (env != nullptr) { + for (auto tag : Split(std::string(env), sep)) { + AddDebugTag(std::move(tag)); + } } -#endif +} +void Logger::Log(LogInfo log_info) { std::unique_lock lock(log_queue_mutex_); log_queue_.push_back(std::move(log_info)); log_queue_condition_variable_.notify_one(); } void Logger::LogThreadRun() { + std::list<LogInfo> queue; + bool stop = false; + while (true) { - std::list<LogInfo> queue; - bool stop = false; std::vector<ILogTarget *> target_list; { std::unique_lock lock(log_queue_mutex_); log_queue_condition_variable_.wait( lock, [this] { return !log_queue_.empty() || log_stop_; }); - std::swap(queue, log_queue_); + queue = std::move(log_queue_); stop = log_stop_; } @@ -114,6 +132,13 @@ void Logger::LogThreadRun() { for (const auto &target : target_list) { for (auto &log_info : queue) { + if (log_info.level == LogLevel::Debug && + std::ranges::none_of(debug_tags_, + [&log_info](const std::string &tag) { + return log_info.tag.starts_with(tag); + })) { + continue; + } target->Write(log_info.level, MakeLogFinalMessage(log_info)); } queue.clear(); diff --git a/src/ui/host/WindowHost.cpp b/src/ui/host/WindowHost.cpp index 59e383c5..88e8cc87 100644 --- a/src/ui/host/WindowHost.cpp +++ b/src/ui/host/WindowHost.cpp @@ -21,13 +21,8 @@ using platform::gui::INativeWindow; using platform::gui::IUiApplication; namespace event_names { -#ifdef CRU_DEBUG -// clang-format off -#define CRU_DEFINE_EVENT_NAME(name) constexpr const char16_t* name = CRU_MAKE_UNICODE_LITERAL(name); -// clang-format on -#else -#define CRU_DEFINE_EVENT_NAME(name) constexpr const char16_t* name = u""; -#endif +#define CRU_DEFINE_EVENT_NAME(name) \ + constexpr const char16_t* name = CRU_MAKE_UNICODE_LITERAL(name); CRU_DEFINE_EVENT_NAME(LoseFocus) CRU_DEFINE_EVENT_NAME(GainFocus) @@ -202,7 +197,8 @@ void WindowHost::RelayoutWithSize(const Size& available_size, for (auto& action : after_layout_stable_action_) action(); after_layout_event_.Raise(AfterLayoutEventArgs{}); after_layout_stable_action_.clear(); - if constexpr (debug_flags::layout) CRU_LOG_TAG_DEBUG("A relayout is finished."); + if constexpr (debug_flags::layout) + CRU_LOG_TAG_DEBUG("A relayout is finished."); } void WindowHost::Repaint() { diff --git a/test/base/StringUtilTest.cpp b/test/base/StringUtilTest.cpp index 1da6e963..6b826d74 100644 --- a/test/base/StringUtilTest.cpp +++ b/test/base/StringUtilTest.cpp @@ -6,6 +6,14 @@ using cru::Index; using cru::k_invalid_code_point; +TEST_CASE("StringUtil Split", "[string]") { + using cru::Split; + REQUIRE(Split("abc", "b") == std::vector<std::string>{"a", "c"}); + REQUIRE(Split("abcd", "bc") == std::vector<std::string>{"a", "d"}); + REQUIRE(Split("abcdbcd", "bc") == std::vector<std::string>{"a", "d", "d"}); + REQUIRE(Split("aaa", "a") == std::vector<std::string>{"", "", "", ""}); +} + TEST_CASE("StringUtil Utf8NextCodePoint", "[string]") { using cru::Utf8NextCodePoint; std::string_view text = "aπ你🤣!"; |