diff options
| author | Stefan Boberg <[email protected]> | 2026-03-12 15:03:03 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-12 15:03:03 +0100 |
| commit | 81bc43aa96f0059cecb28d1bd88338b7d84667f9 (patch) | |
| tree | a3428cb7fddceae0b284d33562af5bf3e64a367e /src/zencore/logging.cpp | |
| parent | update fmt 12.0.0 -> 12.1.0 (#828) (diff) | |
| download | zen-81bc43aa96f0059cecb28d1bd88338b7d84667f9.tar.xz zen-81bc43aa96f0059cecb28d1bd88338b7d84667f9.zip | |
Transparent proxy mode (#823)
Adds a **transparent TCP proxy mode** to zenserver (activated via `zenserver proxy`), allowing it to sit between clients and upstream Zen servers to inspect and monitor HTTP/1.x traffic in real time. Primarily useful during development, to be able to observe multi-server/client interactions in one place.
- **Dedicated proxy port** -- Proxy mode defaults to port 8118 with its own data directory to avoid collisions with a normal zenserver instance.
- **TCP proxy core** (`src/zenserver/proxy/`) -- A new transparent TCP proxy that forwards connections to upstream targets, with support for both TCP/IP and Unix socket listeners. Multi-threaded I/O for connection handling. Supports Unix domain sockets for both upstream/downstream.
- **HTTP traffic inspection** -- Parses HTTP/1.x request/response streams inline to extract method, path, status, content length, and WebSocket upgrades without breaking the proxied data.
- **Proxy dashboard** -- A web UI showing live connection stats, per-target request counts, active connections, bytes transferred, and client IP/session ID rollups.
- **Server mode display** -- Dashboard banner now shows the running server mode (Zen Proxy, Zen Compute, etc.).
Supporting changes included in this branch:
- **Wildcard log level matching** -- Log levels can now be set per-category using wildcard patterns (e.g. `proxy.*=debug`).
- **`zen down --all`** -- New flag to shut down all running zenserver instances; also used by the new `xmake kill` task.
- Minor test stability fixes (flaky hash collisions, per-thread RNG seeds).
- Support ZEN_MALLOC environment variable for default allocator selection and switch default to rpmalloc
- Fixed sentry-native build to allow LTO on Windows
Diffstat (limited to 'src/zencore/logging.cpp')
| -rw-r--r-- | src/zencore/logging.cpp | 164 |
1 files changed, 110 insertions, 54 deletions
diff --git a/src/zencore/logging.cpp b/src/zencore/logging.cpp index 099518637..900da6c6d 100644 --- a/src/zencore/logging.cpp +++ b/src/zencore/logging.cpp @@ -25,16 +25,6 @@ namespace zen { LoggerRef TheDefaultLogger; -LoggerRef -Log() -{ - if (TheDefaultLogger) - { - return TheDefaultLogger; - } - return zen::logging::ConsoleLog(); -} - } // namespace zen namespace zen::logging { @@ -66,6 +56,11 @@ EmitLogMessage(LoggerRef& Logger, const LogPoint& Lp, fmt::format_args Args) { ZEN_MEMSCOPE(ELLMTag::Logging); + if (!Logger) + { + Logger = zen::logging::ConsoleLog(); + } + Logger->Log(Lp, Args); if (IsErrorLevel(Lp.Level)) @@ -149,6 +144,52 @@ ConfigureLogLevels(LogLevel Level, std::string_view Loggers) LogLevels[Level] = Loggers; } +// Iterate all configured per-logger level overrides, calling Fn(LoggerName, Level) for each entry. +// Caller must hold LogLevelsLock (shared or exclusive). +template<typename Fn> +static void +ForEachConfiguredLevel(Fn&& Callback) +{ + for (int i = 0; i < LogLevelCount; ++i) + { + LogLevel Level = LogLevel(i); + std::string_view Spec = LogLevels[i]; + + while (!Spec.empty()) + { + std::string_view Entry; + if (auto CommaPos = Spec.find_first_of(','); CommaPos != std::string_view::npos) + { + Entry = Spec.substr(0, CommaPos); + Spec.remove_prefix(CommaPos + 1); + } + else + { + Entry = Spec; + Spec = {}; + } + + Callback(Entry, Level); + } + } +} + +// Find the configured level override for a named logger, if any. +// Patterns may contain wildcards (e.g. "proxy*" matches "proxy" and "proxy.http"). +// Caller must hold LogLevelsLock (shared or exclusive). +static std::optional<LogLevel> +FindConfiguredLevel(std::string_view Name) +{ + std::optional<LogLevel> Result; + ForEachConfiguredLevel([&](std::string_view Pattern, LogLevel Level) { + if (MatchLoggerPattern(Pattern, Name)) + { + Result = Level; + } + }); + return Result; +} + void RefreshLogLevels(LogLevel* DefaultLevel) { @@ -158,31 +199,7 @@ RefreshLogLevels(LogLevel* DefaultLevel) { RwLock::SharedLockScope _(LogLevelsLock); - - for (int i = 0; i < LogLevelCount; ++i) - { - LogLevel CurrentLevel{i}; - - std::string_view Spec = LogLevels[i]; - - while (!Spec.empty()) - { - std::string LoggerName; - - if (auto CommaPos = Spec.find_first_of(','); CommaPos != std::string_view::npos) - { - LoggerName = Spec.substr(0, CommaPos); - Spec.remove_prefix(CommaPos + 1); - } - else - { - LoggerName = Spec; - Spec = {}; - } - - Levels.emplace_back(std::move(LoggerName), CurrentLevel); - } - } + ForEachConfiguredLevel([&](std::string_view Entry, LogLevel Level) { Levels.emplace_back(std::string(Entry), Level); }); } Registry::Instance().SetLevels(Levels, DefaultLevel); @@ -276,11 +293,20 @@ Get(std::string_view Name) { FoundLogger = Default()->Clone(std::string(Name)); Registry::Instance().Register(FoundLogger); + + // Apply any per-logger level override that was configured before this + // logger was created (e.g. via --log-debug=proxy). + RwLock::SharedLockScope LevelsLock(LogLevelsLock); + std::optional<LogLevel> Override = FindConfiguredLevel(Name); + if (Override) + { + FoundLogger->SetLevel(*Override); + } } }); } - return *FoundLogger; + return LoggerRef(*FoundLogger); } std::once_flag ConsoleInitFlag; @@ -349,7 +375,7 @@ ConsoleLog() } }); - return *ConLogger; + return LoggerRef(*ConLogger); } void @@ -367,7 +393,7 @@ InitializeLogging() { ZEN_MEMSCOPE(ELLMTag::Logging); - TheDefaultLogger = *Registry::Instance().DefaultLoggerRaw(); + TheDefaultLogger = LoggerRef(*Registry::Instance().DefaultLoggerRaw()); } void @@ -416,28 +442,14 @@ FlushLogging() namespace zen { -bool -LoggerRef::ShouldLog(logging::LogLevel Level) const +LoggerRef::LoggerRef(logging::Logger& InLogger) : m_Logger(static_cast<logging::LoggerBase*>(&InLogger)) { - return m_Logger->ShouldLog(Level); -} - -void -LoggerRef::SetLogLevel(logging::LogLevel NewLogLevel) -{ - m_Logger->SetLevel(NewLogLevel); -} - -logging::LogLevel -LoggerRef::GetLogLevel() -{ - return m_Logger->GetLevel(); } void LoggerRef::Flush() { - m_Logger->Flush(); + (*this)->Flush(); } thread_local ScopedActivityBase* t_ScopeStack = nullptr; @@ -548,6 +560,50 @@ TEST_CASE("simple.bread") } } +TEST_CASE("MatchLoggerPattern") +{ + // Exact match + CHECK(logging::MatchLoggerPattern("proxy", "proxy")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy", "prox")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy", "proxyz")); + + // Trailing wildcard + CHECK(logging::MatchLoggerPattern("proxy*", "proxy")); + CHECK(logging::MatchLoggerPattern("proxy*", "proxy.http")); + CHECK(logging::MatchLoggerPattern("proxy*", "proxy.anything")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy*", "prox")); + CHECK_FALSE(logging::MatchLoggerPattern("proxy*", "other")); + + // Leading wildcard + CHECK(logging::MatchLoggerPattern("*store", "store")); + CHECK(logging::MatchLoggerPattern("*store", "buildstore")); + CHECK_FALSE(logging::MatchLoggerPattern("*store", "stores")); + + // Wildcard in the middle + CHECK(logging::MatchLoggerPattern("zen*store", "zenstore")); + CHECK(logging::MatchLoggerPattern("zen*store", "zen.buildstore")); + CHECK_FALSE(logging::MatchLoggerPattern("zen*store", "zen.stores")); + + // Multiple wildcards + CHECK(logging::MatchLoggerPattern("*proxy*", "proxy")); + CHECK(logging::MatchLoggerPattern("*proxy*", "proxy.http")); + CHECK(logging::MatchLoggerPattern("*proxy*", "myproxy.http")); + CHECK_FALSE(logging::MatchLoggerPattern("*proxy*", "other")); + + // Single-char wildcard + CHECK(logging::MatchLoggerPattern("prox?", "proxy")); + CHECK_FALSE(logging::MatchLoggerPattern("prox?", "prox")); + CHECK_FALSE(logging::MatchLoggerPattern("prox?", "proxyy")); + + // Star matches empty + CHECK(logging::MatchLoggerPattern("*", "anything")); + CHECK(logging::MatchLoggerPattern("*", "")); + + // Empty pattern + CHECK(logging::MatchLoggerPattern("", "")); + CHECK_FALSE(logging::MatchLoggerPattern("", "notempty")); +} + TEST_SUITE_END(); #endif |