From bfa333849a5fce765716576a400afe1992e1de82 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 25 Jan 2026 06:36:09 +0000 Subject: [PATCH] feat(ai): implement privacy-preserving telemetry aggregator script - Enhance `scripts/agent/telemetry-aggregator.mjs` to parse TAP formatted unit test logs (e.g., `test_unit_debug.log`). - Add logic to identify and group errors by stack trace fingerprint. - Implement PII sanitization for IPs, emails, and keys. - Generate a Markdown report in `ai/reports/` with prioritized issues. - Generate a JSON artifact in `artifacts/` for machine consumption. - Ensure telemetry is opt-in via `ENABLE_TELEMETRY` env var. - Produce initial telemetry report `ai/reports/telemetry-20260125.md`. --- ai/reports/telemetry-20260125.md | 31 +++++++++++++++ scripts/agent/telemetry-aggregator.mjs | 55 ++++++++++++++++---------- 2 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 ai/reports/telemetry-20260125.md diff --git a/ai/reports/telemetry-20260125.md b/ai/reports/telemetry-20260125.md new file mode 100644 index 00000000..d51fb142 --- /dev/null +++ b/ai/reports/telemetry-20260125.md @@ -0,0 +1,31 @@ +# Telemetry Report - 2026-01-25 + +**Total Unique Issues:** 1 +**Generated:** 2026-01-25T06:32:46.314Z + +## Top 10 Priority Issues + +| Priority | Count | Issue | Owner | Sources | +| :--- | :---: | :--- | :--- | :--- | +| **High** | 1 | handleModerationBlock requests a block, clears overrides,... | QA Team | unit-test:test_unit_debug.log | + +## Detailed Breakdown + +### [High] handleModerationBlock requests a block, clears overrides, and refreshes hidden state +- **Occurrences:** 1 +- **Sources:** unit-test:test_unit_debug.log +- **Suggested Owner:** QA Team +- **Fingerprint:** `56489eaa` + +**Stack Trace / Details:** +``` +TestContext. (file://$REPO/tests$REPO/moderation-overrides.test.mjs:228:10) + async Test.run (node:internal/test_runner/test:1054:7) + async Test.processPendingSubtests (node:internal/test_runner/test:744:7) + ... +# Subtest: handleModerationBlock returns false when viewer is logged out +``` + + +--- +*Privacy Notice: PII (IPs, emails, keys) has been sanitized from this report.* diff --git a/scripts/agent/telemetry-aggregator.mjs b/scripts/agent/telemetry-aggregator.mjs index 8146c4b9..5ec7401a 100644 --- a/scripts/agent/telemetry-aggregator.mjs +++ b/scripts/agent/telemetry-aggregator.mjs @@ -5,7 +5,10 @@ import crypto from 'node:crypto'; // Configuration const ARTIFACTS_DIR = 'artifacts'; const REPORTS_DIR = 'ai/reports'; -const LOG_FILES = ['test_unit.log', 'test_unit_debug.log', 'server.log', 'serve.log', 'test_output.log', 'python_server.log', 'npm_output.log']; +// TAP formatted logs +const UNIT_TEST_LOGS = ['test_unit.log', 'test_unit_debug.log', 'test_output.log']; +// Generic logs (subset of original list, excluding TAP logs) +const GENERIC_LOG_FILES = ['server.log', 'serve.log', 'python_server.log', 'npm_output.log']; // Regex patterns for PII sanitization const PII_PATTERNS = [ @@ -56,9 +59,8 @@ function suggestOwner(stack) { // Collectors // ----------------------------------------------------------------------------- -function collectUnitTests() { +function collectUnitTests(logFile = 'test_output.log') { const errors = []; - const logFile = 'test_output.log'; if (!fs.existsSync(logFile)) return errors; const content = fs.readFileSync(logFile, 'utf8'); @@ -76,7 +78,7 @@ function collectUnitTests() { } const title = line.replace(/^not ok \d+ - /, '').trim(); currentError = { - source: 'unit-test', + source: `unit-test:${logFile}`, title: sanitize(title), details: [], severity: 'High' @@ -264,8 +266,8 @@ function collectAgentLogs() { const files = fs.readdirSync(ARTIFACTS_DIR).filter(f => f.endsWith('.log')); for (const file of files) { - // Avoid reparsing if we have specific logic (though generic is fine for now) - if (file === 'test_output.log') continue; + // Avoid reparsing if we have specific logic + if (UNIT_TEST_LOGS.includes(file)) continue; errors.push(...collectGenericLogs(path.join(ARTIFACTS_DIR, file), `agent-log:${file}`)); } @@ -392,33 +394,44 @@ async function main() { // 1. Collect const errors = []; - // Unit Tests - const unitTestErrors = collectUnitTests(); - console.log(`Collected ${unitTestErrors.length} unit test errors.`); - errors.push(...unitTestErrors); + // Unit Tests (TAP logs) + for (const logFile of UNIT_TEST_LOGS) { + const unitErrors = collectUnitTests(logFile); + if (unitErrors.length > 0) { + console.log(`Collected ${unitErrors.length} unit test errors from ${logFile}.`); + errors.push(...unitErrors); + } + } - // Server Logs - for (const logFile of LOG_FILES) { - if (logFile === 'test_output.log') continue; // Handled by collectUnitTests + // Generic Logs + for (const logFile of GENERIC_LOG_FILES) { const genericErrors = collectGenericLogs(logFile, logFile); - console.log(`Collected ${genericErrors.length} errors from ${logFile}.`); - errors.push(...genericErrors); + if (genericErrors.length > 0) { + console.log(`Collected ${genericErrors.length} errors from ${logFile}.`); + errors.push(...genericErrors); + } } // Smoke Tests const smokeTestErrors = collectSmokeTests(); - console.log(`Collected ${smokeTestErrors.length} smoke test errors.`); - errors.push(...smokeTestErrors); + if (smokeTestErrors.length > 0) { + console.log(`Collected ${smokeTestErrors.length} smoke test errors.`); + errors.push(...smokeTestErrors); + } // Fuzz Reports const fuzzErrors = collectFuzzReports(); - console.log(`Collected ${fuzzErrors.length} fuzz reports.`); - errors.push(...fuzzErrors); + if (fuzzErrors.length > 0) { + console.log(`Collected ${fuzzErrors.length} fuzz reports.`); + errors.push(...fuzzErrors); + } // Agent Logs const agentErrors = collectAgentLogs(); - console.log(`Collected ${agentErrors.length} agent log errors.`); - errors.push(...agentErrors); + if (agentErrors.length > 0) { + console.log(`Collected ${agentErrors.length} agent log errors.`); + errors.push(...agentErrors); + } // 2. Aggregate const aggregates = aggregate(errors);