feat(telemetry): Sentry + PostHog quality pass (#546)

Comprehensive telemetry quality improvements across Sentry and PostHog, grounded in an audit of the live data plus current best-practice research.

Sentry: job_id/instance_id tags, operational fingerprinting, PII-safe settings context on bug events, web tag population + extension-noise filtering, an early-crash buffer, http status/method kept on breadcrumbs, and a gated-off-by-default performance-tracing re-enable (tracesSampler that zeroes db/redis/queue-poll root spans + drops the Redis integration) with worker job spans and canonical-host cron monitors.

PostHog: history_change SPA pageviews, instance_id super property for fleet rollups, enriched tool_used (formats, byte sizes, is_batch, execution_hint, real error_kind taxonomy), the previously-dead result_saved/batch_processed/ai_bundle_prompted events fired, search click-through, editor + Automate authoring + auth instrumentation, a before_send PII boundary, and minimal opt-in landing-site pageviews.
This commit is contained in:
SnapOtter
2026-07-17 01:51:48 +00:00
committed by GitHub
parent 9247947704
commit 86251434b5
36 changed files with 936 additions and 54 deletions
+23
View File
@@ -23,6 +23,8 @@ const TAG_ALLOWLIST = new Set([
"subsystem",
"status_code",
"input_format",
"job_id",
"instance_id",
]);
const URL_RE = /https?:\/\/[^\s"')]+/g;
@@ -59,6 +61,16 @@ function scrubBreadcrumb(entry: unknown): AnyEvent | null {
if (b[k] !== undefined) out[k] = b[k];
}
if (typeof b.message === "string") out.message = scrubText(b.message);
// For http breadcrumbs keep the non-PII status_code + method (the url is the
// sensitive part, dropped with the rest of `data`): they answer "what request
// failed right before the error".
if (b.category === "http") {
const data = asObj(b.data);
const safe: AnyEvent = {};
if (data?.status_code !== undefined) safe.status_code = data.status_code;
if (typeof data?.method === "string") safe.method = data.method;
if (Object.keys(safe).length) out.data = safe;
}
return out;
}
@@ -102,6 +114,17 @@ export function buildBeforeSend(isActive: () => boolean) {
if (os?.name) keep.os = { name: os.name, version: os.version };
const runtime = asObj(ctx?.runtime);
if (runtime?.name) keep.runtime = { name: runtime.name, version: runtime.version };
// The `tool` context is set only by reportError from an already-vetted
// settings projection; re-enforce primitives-only here as a final boundary.
const tool = asObj(ctx?.tool);
if (tool) {
const safe: AnyEvent = {};
for (const [k, v] of Object.entries(tool)) {
if (typeof v === "number" || typeof v === "boolean") safe[k] = v;
else if (typeof v === "string" && v.length <= 32) safe[k] = v;
}
if (Object.keys(safe).length) keep.tool = safe;
}
event.contexts = Object.keys(keep).length ? keep : undefined;
const tags = asObj(event.tags);