aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/logging.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-12 15:03:03 +0100
committerGitHub Enterprise <[email protected]>2026-03-12 15:03:03 +0100
commit81bc43aa96f0059cecb28d1bd88338b7d84667f9 (patch)
treea3428cb7fddceae0b284d33562af5bf3e64a367e /src/zencore/logging.cpp
parentupdate fmt 12.0.0 -> 12.1.0 (#828) (diff)
downloadzen-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.cpp164
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