fix: rotate web-probe jsonl on start (#702)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-22 23:22:11 +08:00
committed by GitHub
parent 8aca736294
commit e49764796e
3 changed files with 80 additions and 10 deletions
+21 -3
View File
@@ -78,6 +78,7 @@ interface NodeWebProbeObserveOptions {
tailLines: number;
maxFiles: number;
collectFile: string | null;
analyzeArchivePrefix: string | null;
full: boolean;
stateDir: string | null;
jobId: string | null;
@@ -6905,6 +6906,7 @@ function parseNodeWebProbeObserveOptions(
"--tail-lines",
"--max-files",
"--file",
"--archive-prefix",
"--state-dir",
"--job-id",
"--type",
@@ -6922,6 +6924,8 @@ function parseNodeWebProbeObserveOptions(
if (jobId !== null && !isSafeWebObserveJobId(jobId)) throw new Error(`unsafe web-probe observe --job-id: ${jobId}`);
const collectFile = optionValue(args, "--file") ?? null;
if (collectFile !== null && !isSafeWebObserveCollectFile(collectFile)) throw new Error(`unsafe web-probe observe --file: ${collectFile}`);
const analyzeArchivePrefix = optionValue(args, "--archive-prefix") ?? null;
if (analyzeArchivePrefix !== null && !isSafeWebObserveArchivePrefix(analyzeArchivePrefix)) throw new Error(`unsafe web-probe observe --archive-prefix: ${analyzeArchivePrefix}`);
if (observeActionRaw !== "start" && stateDir === null && jobId === null) {
throw new Error("web-probe observe status|command|stop|collect|analyze requires --state-dir or --job-id");
}
@@ -6942,6 +6946,7 @@ function parseNodeWebProbeObserveOptions(
tailLines: positiveIntegerOption(args, "--tail-lines", 5, 200),
maxFiles: positiveIntegerOption(args, "--max-files", 80, 5000),
collectFile,
analyzeArchivePrefix,
full: args.includes("--full"),
stateDir,
jobId,
@@ -7396,7 +7401,8 @@ function runNodeWebProbeObserveStart(
const jobId = `webobs-${Date.now().toString(36)}-${randomBytes(3).toString("hex")}`;
const timestamp = new Date().toISOString().replace(/[-:]/gu, "").replace(/[.]\d{3}Z$/u, "Z");
const day = timestamp.slice(0, 8);
const stateDir = `.state/web-observe/${safeWebObserveSegment(options.node)}/${safeWebObserveSegment(options.lane)}/${day.slice(0, 4)}/${day.slice(4, 6)}/${day.slice(6, 8)}/${timestamp}_${safeWebObserveTargetSegment(options.targetPath)}_${jobId}`;
const defaultStateDir = `.state/web-observe/${safeWebObserveSegment(options.node)}/${safeWebObserveSegment(options.lane)}/${day.slice(0, 4)}/${day.slice(4, 6)}/${day.slice(6, 8)}/${timestamp}_${safeWebObserveTargetSegment(options.targetPath)}_${jobId}`;
const stateDir = options.stateDir ?? defaultStateDir;
const runnerB64 = Buffer.from(nodeWebObserveRunnerSource(), "utf8").toString("base64");
const webProbeProxy = nodeWebProbeHostProxyEnv(spec);
const script = [
@@ -7943,7 +7949,8 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
"analysis_stdout=\"$state_dir/analysis/analyzer-stdout.json\"",
"analysis_stderr=\"$state_dir/analysis/analyzer-stderr.log\"",
"set +e",
"UNIDESK_WEB_OBSERVE_STATE_DIR=\"$state_dir\" node \"$analyzer\" >\"$analysis_stdout\" 2>\"$analysis_stderr\"",
`UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX=${shellQuote(options.analyzeArchivePrefix ?? "")}`,
"UNIDESK_WEB_OBSERVE_STATE_DIR=\"$state_dir\" UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX=\"$UNIDESK_WEB_OBSERVE_ANALYZE_ARCHIVE_PREFIX\" node \"$analyzer\" >\"$analysis_stdout\" 2>\"$analysis_stderr\"",
"analyzer_exit=$?",
"set -e",
"report_json=\"$state_dir/analysis/report.json\"",
@@ -8030,6 +8037,7 @@ function runNodeWebProbeObserveAnalyze(options: NodeWebProbeObserveOptions, spec
"const compact = source ? {",
" ok: analyzerExit === 0,",
" counts: source.counts ?? null,",
" jsonlScope: objectOrNull(source.jsonlScope),",
" analysisWindow: objectOrNull(source.analysisWindow),",
" archiveSummary: objectOrNull(source.archiveSummary),",
" sampleMetrics: metrics,",
@@ -8203,6 +8211,7 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
const analyzer = record(analysis?.analyzer);
const result = record(value.result);
const counts = record(analysis?.counts);
const jsonlScope = record(analysis?.jsonlScope);
const analysisWindow = record(analysis?.analysisWindow);
const archiveSummary = record(analysis?.archiveSummary);
const sampleMetrics = record(analysis?.sampleMetrics);
@@ -8335,10 +8344,11 @@ function renderWebObserveAnalyzeTable(value: Record<string, unknown>): string {
]]),
"",
] : []),
webObserveTable(["ID", "NODE", "LANE", "SAMPLES", "CONTROL", "NETWORK", "CONSOLE", "ARTIFACTS"], [[
webObserveTable(["ID", "NODE", "LANE", "SCOPE", "SAMPLES", "CONTROL", "NETWORK", "CONSOLE", "ARTIFACTS"], [[
webObserveText(value.id),
webObserveText(value.node),
webObserveText(value.lane),
webObserveShort([webObserveText(jsonlScope?.mode ?? "current"), jsonlScope?.archivePrefix ? webObserveText(jsonlScope.archivePrefix) : ""].filter((part) => part && part !== "-").join(":"), 36),
webObserveText(counts?.samples),
webObserveText(counts?.control),
webObserveText(counts?.network),
@@ -9356,6 +9366,14 @@ function isSafeWebObserveCollectFile(value: string): boolean {
&& /^[A-Za-z0-9_.\/-]+$/u.test(value);
}
function isSafeWebObserveArchivePrefix(value: string): boolean {
return value.length > 0
&& value.length <= 120
&& !value.includes("\0")
&& !value.includes("..")
&& /^[A-Za-z0-9_.-]+$/u.test(value);
}
function isSafeWebObserveJobId(value: string): boolean {
return /^webobs-[A-Za-z0-9_.-]+$/u.test(value);
}