aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-10-11 19:30:36 +0800
committerYuqian Yang <crupest@crupest.life>2025-10-11 19:30:36 +0800
commitea80856073d4896da5df78d60369b327346a0b56 (patch)
treec5bd85b508374066a66864ba1317129ac49db6fc
parent9768b98d46982501c3b34c171aa51389b06add3b (diff)
downloadcru-ea80856073d4896da5df78d60369b327346a0b56.tar.gz
cru-ea80856073d4896da5df78d60369b327346a0b56.tar.bz2
cru-ea80856073d4896da5df78d60369b327346a0b56.zip
Split and log debug tag.
-rw-r--r--include/cru/base/StringUtil.h47
-rw-r--r--include/cru/base/log/Logger.h8
-rw-r--r--src/base/log/Logger.cpp43
-rw-r--r--src/ui/host/WindowHost.cpp12
-rw-r--r--test/base/StringUtilTest.cpp8
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π你🤣!";