feat(telemetry): add a safe input_format tag to worker error reports (#541)

Every worker tool error now carries an input_format tag (file extension only, never the filename) for triage, derived once at the worker error path and added to the scrubber allowlist.
This commit is contained in:
SnapOtter
2026-07-16 19:27:16 +08:00
committed by GitHub
parent 631d82eaae
commit 281b4a06e3
5 changed files with 33 additions and 2 deletions
+2 -1
View File
@@ -39,7 +39,7 @@ import { db, schema } from "../db/index.js";
import { trackEvent } from "../lib/analytics.js";
import { analyticsEnabled } from "../lib/analytics-gate.js";
import { resolveConcurrency } from "../lib/env.js";
import { reportError } from "../lib/error-report.js";
import { reportError, safeFormatTag } from "../lib/error-report.js";
import { friendlyError } from "../lib/errors.js";
import { logger } from "../lib/logger.js";
import { jobDuration, jobsTotal } from "../lib/metrics.js";
@@ -1047,6 +1047,7 @@ export function startWorkers(): void {
source: "worker",
pool,
toolId: (job.data as ToolJobData | undefined)?.toolId,
inputFormat: safeFormatTag((job.data as ToolJobData | undefined)?.filename),
});
});
+14
View File
@@ -35,6 +35,8 @@ export interface ReportContext {
method?: string;
statusCode?: number;
subsystem?: string;
/** Safe input format (file extension) for triage; never the filename. */
inputFormat?: string;
}
export function classifyError(err: unknown, source?: ReportContext["source"]): ErrorClass {
@@ -99,6 +101,17 @@ export function errorSignature(err: unknown): string {
return `${name}:${code}:${frame}`;
}
/**
* The lowercase file extension as a safe, non-PII tag value, or undefined. The
* filename itself can carry user data, but a short alphanumeric extension (jpg,
* png, pdf, mp4) is a safe triage signal for which input format failed.
*/
export function safeFormatTag(filename?: string): string | undefined {
if (!filename) return undefined;
const m = filename.match(/\.([a-z0-9]{1,8})$/i);
return m ? m[1].toLowerCase() : undefined;
}
/** Fire-and-forget; never throws, never blocks. */
export async function reportError(err: unknown, ctx: ReportContext): Promise<void> {
try {
@@ -115,6 +128,7 @@ export async function reportError(err: unknown, ctx: ReportContext): Promise<voi
scope.setTag("error_class", cls);
const code = extractErrorCode(err);
if (code) scope.setTag("error_code", code);
if (ctx.inputFormat) scope.setTag("input_format", ctx.inputFormat);
if (ctx.toolId) scope.setTag("tool_id", ctx.toolId);
if (ctx.pool) scope.setTag("pool", ctx.pool);
if (ctx.route) scope.setTag("route", ctx.route);
+1
View File
@@ -22,6 +22,7 @@ const TAG_ALLOWLIST = new Set([
"deploy_mode",
"subsystem",
"status_code",
"input_format",
]);
const URL_RE = /https?:\/\/[^\s"')]+/g;