fix(gateway): route watch trace spam to artifacts

This commit is contained in:
Peter Steinberger
2026-05-04 23:41:07 +01:00
parent 864b1be1b3
commit a167acee67
6 changed files with 156 additions and 2 deletions

View File

@@ -9,6 +9,8 @@ const TMUX_ATTACH_FORCE_VALUES = new Set(["1", "true", "yes", "on"]);
const DEFAULT_PROFILE_NAME = "main";
const DEFAULT_BENCHMARK_PROFILE_DIR = ".artifacts/gateway-watch-profiles";
const RUN_NODE_CPU_PROF_DIR_ENV = "OPENCLAW_RUN_NODE_CPU_PROF_DIR";
const RUN_NODE_OUTPUT_LOG_ENV = "OPENCLAW_RUN_NODE_OUTPUT_LOG";
const RUN_NODE_FILTER_SYNC_IO_STDERR_ENV = "OPENCLAW_RUN_NODE_FILTER_SYNC_IO_STDERR";
const RAW_WATCH_SCRIPT = "scripts/watch-node.mjs";
const TMUX_CWD_ENV_KEY = "OPENCLAW_GATEWAY_WATCH_CWD";
const TMUX_CWD_OPTION_KEY = "@openclaw.gateway_watch.cwd";
@@ -19,6 +21,8 @@ const TMUX_CHILD_ENV_KEYS = [
"OPENCLAW_HOME",
"OPENCLAW_PROFILE",
RUN_NODE_CPU_PROF_DIR_ENV,
RUN_NODE_FILTER_SYNC_IO_STDERR_ENV,
RUN_NODE_OUTPUT_LOG_ENV,
"OPENCLAW_SKIP_CHANNELS",
"OPENCLAW_STATE_DIR",
"OPENCLAW_TRACE_SYNC_IO",
@@ -50,6 +54,11 @@ const readArgValue = (args, flag) => {
return null;
};
const joinArtifactPath = (dir, basename) => {
const normalizedDir = String(dir || DEFAULT_BENCHMARK_PROFILE_DIR).replace(/[\\/]+$/g, "");
return `${normalizedDir || "."}/${basename}`;
};
const resolveGatewayWatchBenchmarkArgs = ({ args = [], env = process.env } = {}) => {
const passthroughArgs = [];
let benchmarkDir = null;
@@ -98,6 +107,13 @@ const resolveGatewayWatchBenchmarkArgs = ({ args = [], env = process.env } = {})
nextEnv[RUN_NODE_CPU_PROF_DIR_ENV] =
benchmarkDir || nextEnv[RUN_NODE_CPU_PROF_DIR_ENV] || DEFAULT_BENCHMARK_PROFILE_DIR;
nextEnv.OPENCLAW_TRACE_SYNC_IO ??= "0";
if (nextEnv.OPENCLAW_TRACE_SYNC_IO === "1") {
nextEnv[RUN_NODE_OUTPUT_LOG_ENV] ??= joinArtifactPath(
nextEnv[RUN_NODE_CPU_PROF_DIR_ENV],
"gateway-watch-output.log",
);
nextEnv[RUN_NODE_FILTER_SYNC_IO_STDERR_ENV] ??= "1";
}
}
return {
args: benchmarkNoForceSeen
@@ -105,6 +121,10 @@ const resolveGatewayWatchBenchmarkArgs = ({ args = [], env = process.env } = {})
: passthroughArgs,
benchmarkNoForce: benchmarkNoForceSeen,
benchmarkProfileDir: nextEnv[RUN_NODE_CPU_PROF_DIR_ENV] || null,
benchmarkTraceOutputLog:
nextEnv[RUN_NODE_FILTER_SYNC_IO_STDERR_ENV] === "1"
? nextEnv[RUN_NODE_OUTPUT_LOG_ENV] || null
: null,
env: nextEnv,
};
};
@@ -250,6 +270,12 @@ export const runGatewayWatchTmuxMain = (params = {}) => {
if (resolvedArgs.benchmarkProfileDir) {
log(deps.stderr, `gateway:watch benchmark CPU profiles: ${resolvedArgs.benchmarkProfileDir}`);
}
if (resolvedArgs.benchmarkTraceOutputLog) {
log(
deps.stderr,
`gateway:watch benchmark trace output: ${resolvedArgs.benchmarkTraceOutputLog}`,
);
}
if (resolvedArgs.benchmarkNoForce) {
log(deps.stderr, "gateway:watch benchmark running without --force");
}

View File

@@ -386,6 +386,7 @@ const getSignalExitCode = (signal) => (isSignalKey(signal) ? SIGNAL_EXIT_CODES[s
const RUN_NODE_OUTPUT_LOG_ENV = "OPENCLAW_RUN_NODE_OUTPUT_LOG";
const RUN_NODE_CPU_PROF_DIR_ENV = "OPENCLAW_RUN_NODE_CPU_PROF_DIR";
const RUN_NODE_FILTER_SYNC_IO_STDERR_ENV = "OPENCLAW_RUN_NODE_FILTER_SYNC_IO_STDERR";
const RUN_NODE_BUILD_LOCK_TIMEOUT_ENV = "OPENCLAW_RUN_NODE_BUILD_LOCK_TIMEOUT_MS";
const RUN_NODE_BUILD_LOCK_POLL_ENV = "OPENCLAW_RUN_NODE_BUILD_LOCK_POLL_MS";
const RUN_NODE_BUILD_LOCK_STALE_ENV = "OPENCLAW_RUN_NODE_BUILD_LOCK_STALE_MS";
@@ -585,14 +586,78 @@ const pipeSpawnedOutput = (childProcess, deps) => {
if (!deps.outputTee) {
return;
}
const stderrFilter =
deps.env[RUN_NODE_FILTER_SYNC_IO_STDERR_ENV] === "1"
? createSyncIoTraceStderrFilter(deps)
: null;
childProcess.stdout?.on("data", (chunk) => {
deps.stdout.write(chunk);
deps.outputTee.write(chunk);
});
childProcess.stderr?.on("data", (chunk) => {
deps.stderr.write(chunk);
if (stderrFilter) {
stderrFilter.write(chunk);
} else {
deps.stderr.write(chunk);
}
deps.outputTee.write(chunk);
});
childProcess.stderr?.on("end", () => {
stderrFilter?.flush();
});
};
const createSyncIoTraceStderrFilter = (deps) => {
let buffer = "";
let inSyncIoTrace = false;
const shouldSuppressLine = (line) => {
const text = line.replace(/\r?\n$/, "");
if (/^\(node:\d+\) WARNING: Detected use of sync API/.test(text)) {
inSyncIoTrace = true;
return true;
}
if (!inSyncIoTrace) {
return false;
}
if (text.trim() === "") {
inSyncIoTrace = false;
return true;
}
if (/^\s+at\b/.test(text)) {
return true;
}
inSyncIoTrace = false;
return false;
};
const writeLine = (line) => {
if (!shouldSuppressLine(line)) {
deps.stderr.write(line);
}
};
return {
write(chunk) {
buffer += String(chunk);
while (true) {
const newlineIndex = buffer.indexOf("\n");
if (newlineIndex === -1) {
break;
}
const line = buffer.slice(0, newlineIndex + 1);
buffer = buffer.slice(newlineIndex + 1);
writeLine(line);
}
},
flush() {
if (!buffer) {
return;
}
writeLine(buffer);
buffer = "";
},
};
};
const closeRunNodeOutputTee = async (deps, exitCode) => {