All posts

Integrating Error Tracking With Your Analytics

August 14, 20268 min read

Why errors belong next to traffic

A deploy goes out. Conversions drop 8%. Your analytics shows the drop; your error tracker shows a spike in a checkout exception. Separately, each is a shrug. Together, it is a cause.

Getting them into one place is mostly an integration problem, and it has a few well-worn edges.

The adapter pattern

Do not write "the Sentry integration". Write an adapter interface and make Sentry the first implementation:

{
  provider: 'sentry',
  label: 'Sentry',
  pollAll: ({ silent }) => /* … */,
  handleWebhook: (payload, rawBody, signature) => /* … */,
}

Everything provider-agnostic — encrypted credential storage, upsert and dedup, poll orchestration, webhook routing — lives outside the adapter. Adding Rollbar or Bugsnag then becomes one file rather than a second subsystem.

The routing follows: POST /api/integrations/:provider/webhook dispatches through a registry instead of hardcoding a path per vendor.

Poll and webhook, not one or the other

Webhooks are near-instant but unreliable: they get missed, retried out of order, or silently dropped when an endpoint has an outage.

Polling is reliable but delayed.

Use both. Webhooks for latency, a periodic poll as the reconciling backstop. The poll is what makes the system self-healing — a missed webhook is corrected within one cycle rather than lost forever.

Verify webhook signatures

An unauthenticated webhook endpoint accepts anything anyone posts:

const expected = crypto.createHmac('sha256', secret)
  .update(rawBody).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
  return res.status(401).end();
}

Two details. Verify against the raw body, not the parsed object — JSON re-serialisation changes bytes and breaks the HMAC. And use timingSafeEqual, because a plain === leaks the signature through timing.

That means the webhook route needs its own body parser with a verify hook to capture the raw bytes, mounted before any global JSON parser.

Adaptive polling

Polling every project every five minutes wastes calls on projects that produce one error a week.

Back off when nothing changes: start at the base interval, double it after each idle poll up to a ceiling, and reset to the floor the moment something arrives. A busy project stays current; a quiet one drops to hourly on its own.

Back off hard on auth failures — hours, not minutes. A revoked token will not fix itself, and retrying every five minutes just fills the logs.

Store what you will report on

Do not mirror the provider's full payload. Store the fields your reports use — title, level, status, count, users affected, regression flag, release, first and last seen — and link out for the rest. Mirroring everything means tracking their schema changes forever.

Try InsightsTrack free

Open-source, self-hosted, privacy-first analytics — explore the live demo, no install required.

Open live dashboard