mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user