Skip to content

chore(observability): replace Sentry with PostHog error tracking#1859

Merged
riderx merged 10 commits intomainfrom
codex/remove-sentry-posthog
Mar 25, 2026
Merged

chore(observability): replace Sentry with PostHog error tracking#1859
riderx merged 10 commits intomainfrom
codex/remove-sentry-posthog

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Mar 25, 2026

Summary (AI generated)

  • Remove Sentry runtime and deploy wiring from Cloudflare workers, Supabase function entrypoints, Bun manifests, and GitHub Actions
  • Replace backend exception capture with PostHog manual error-tracking events using real $exception payloads, $exception_list, and $exception_fingerprint
  • Add and harden unit coverage for PostHog exception capture, stable exception identity, and shared error-handler scheduling

Motivation (AI generated)

The codebase no longer needs a split observability setup where backend exception reporting depends on Sentry while product analytics already use PostHog. Moving fully to PostHog reduces operational overhead and aligns backend exception telemetry with the team's current analytics platform.

Business Impact (AI generated)

This removes Sentry-specific deployment and secret management, keeps backend failures visible in the PostHog error-tracking product, and simplifies ongoing maintenance for releases and production observability. That lowers tooling complexity without dropping backend exception visibility.

Test Plan (AI generated)

  • bunx vitest run tests/posthog.unit.test.ts tests/on-error-posthog.unit.test.ts tests/tracking.unit.test.ts
  • bunx vitest run tests/on-error-posthog.unit.test.ts
  • bun lint:backend
  • bunx deno check --config supabase/functions/deno.json supabase/functions/_backend/utils/posthog.ts

Generated with AI

Summary by CodeRabbit

  • New Features

    • Enhanced exception ingestion and richer error reporting for improved diagnostics and platform visibility.
  • Tests

    • Expanded unit tests covering exception capture, payload/endpoint handling, fingerprinting, and invalid-host behavior to ensure robust error submission.
  • Chores

    • Simplified deployment config by removing an unused auth token reference.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Removed Sentry token from CI deploy step and replaced event-based PostHog error reporting with a new direct $exception ingestion flow, including host normalization, stack-to-frames parsing, fingerprinting, HTTP submission with timeout handling, and expanded unit tests for endpoint and payload validation.

Changes

Cohort / File(s) Summary
CI/CD Configuration
​.github/workflows/build_and_deploy.yml
Removed SENTRY_AUTH_TOKEN environment variable from the deploy_api job step environment.
PostHog exception ingestion
supabase/functions/_backend/utils/posthog.ts
Rewrote capturePosthogException to submit PostHog $exception payloads directly: added env validation (POSTHOG_API_KEY), host normalization (getPostHogExceptionUrl), stack parsing (parseExceptionFrames), request-path extraction, fingerprinting, AbortController-based POST with 5s timeout, and detailed success/failure logging.
Tests
tests/posthog.unit.test.ts
Updated tests to use shared envState for POSTHOG_API_HOST, assert normalized exception endpoint URL and payload shape (token, $exception, properties.url_path, fingerprint, frames), verify AbortSignal usage, and add a test for invalid PostHog host handling.

Sequence Diagram

sequenceDiagram
    participant App as Backend App
    participant Handler as capturePosthogException
    participant Parser as parseExceptionFrames
    participant PostHog as PostHog API

    App->>Handler: send error, request, kind
    Handler->>Handler: validate POSTHOG_API_KEY & POSTHOG_API_HOST
    Handler->>Parser: parse error.stack -> frames
    Parser-->>Handler: frames
    Handler->>Handler: compute $exception_fingerprint & build payload (token,timestamp,properties)
    Handler->>PostHog: POST /i/v0/e/ (JSON) with AbortSignal (5s)
    PostHog-->>Handler: 200 OK / error
    Handler-->>App: return true / false (log result)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I dug through stacks and traced each line,

Built fingerprints where errors shine.
A timeout set, a host made neat,
Tests hop in to keep things sweet.
Sentry waved — PostHog hums on fleet. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: replacing Sentry with PostHog for error tracking, which aligns with the primary objective of the changeset.
Description check ✅ Passed The description includes summary, motivation, business impact, and a comprehensive test plan with checkmarks. However, it lacks explicit manual testing steps and omits the checklist section from the template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/remove-sentry-posthog

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
supabase/functions/_backend/utils/posthog.ts (1)

140-164: Minor: parseExceptionFrames is called twice with identical arguments.

The function is invoked once at line 140 (for fingerprinting) and again at line 164 (for the exception payload). Consider caching the result to avoid redundant stack parsing.

♻️ Suggested fix
   const serializedError = serializeError(payload.error)
   const distinctId = `backend:${getEnv(c, 'ENV_NAME') || 'unknown'}:${payload.functionName}`
-  const topFrame = parseExceptionFrames(serializedError.stack, payload.functionName)[0]
+  const frames = parseExceptionFrames(serializedError.stack, payload.functionName)
+  const topFrame = frames[0]
   const fingerprint = [
     distinctId,
     payload.kind,

Then at line 164:

       stacktrace: {
         type: 'raw',
-        frames: parseExceptionFrames(serializedError.stack, payload.functionName),
+        frames,
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/functions/_backend/utils/posthog.ts` around lines 140 - 164, The
code calls parseExceptionFrames(serializedError.stack, payload.functionName)
twice (once to compute topFrame for fingerprinting and again for the
body.stacktrace); compute it once into a local variable (e.g., frames =
parseExceptionFrames(serializedError.stack, payload.functionName)), reuse frames
when deriving topFrame and when populating
body.properties.$exception_list[0].stacktrace.frames; update references to
topFrame to use frames[0] and replace the second call with the cached frames
variable to avoid redundant parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@supabase/functions/_backend/utils/posthog.ts`:
- Around line 140-164: The code calls
parseExceptionFrames(serializedError.stack, payload.functionName) twice (once to
compute topFrame for fingerprinting and again for the body.stacktrace); compute
it once into a local variable (e.g., frames =
parseExceptionFrames(serializedError.stack, payload.functionName)), reuse frames
when deriving topFrame and when populating
body.properties.$exception_list[0].stacktrace.frames; update references to
topFrame to use frames[0] and replace the second call with the cached frames
variable to avoid redundant parsing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 418a845e-e76b-45ab-8c5a-925309641ff7

📥 Commits

Reviewing files that changed from the base of the PR and between ebabb47 and 69ee2b7.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • supabase/functions/deno.lock is excluded by !**/*.lock
📒 Files selected for processing (29)
  • .github/workflows/build_and_deploy.yml
  • cloudflare_workers/api/index.ts
  • cloudflare_workers/files/index.ts
  • cloudflare_workers/plugin/index.ts
  • package.json
  • supabase/functions/_backend/utils/hono.ts
  • supabase/functions/_backend/utils/on_error.ts
  • supabase/functions/_backend/utils/posthog.ts
  • supabase/functions/apikey/index.ts
  • supabase/functions/app/index.ts
  • supabase/functions/build/index.ts
  • supabase/functions/bundle/index.ts
  • supabase/functions/channel/index.ts
  • supabase/functions/channel_self/index.ts
  • supabase/functions/deno.json
  • supabase/functions/device/index.ts
  • supabase/functions/files/index.ts
  • supabase/functions/ok/index.ts
  • supabase/functions/organization/index.ts
  • supabase/functions/private/index.ts
  • supabase/functions/replication/index.ts
  • supabase/functions/statistics/index.ts
  • supabase/functions/stats/index.ts
  • supabase/functions/triggers/index.ts
  • supabase/functions/updates/index.ts
  • supabase/functions/updates_debug/index.ts
  • supabase/functions/webhooks/index.ts
  • tests/on-error-posthog.unit.test.ts
  • tests/posthog.unit.test.ts
💤 Files with no reviewable changes (2)
  • supabase/functions/deno.json
  • .github/workflows/build_and_deploy.yml

…osthog

# Conflicts:
#	supabase/functions/_backend/utils/posthog.ts
#	tests/posthog.unit.test.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ffded9a498

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
supabase/functions/_backend/utils/posthog.ts (1)

96-101: Preserve parsed function name in fallback frames.

When filename/line parsing fails, Line 98 currently drops functionName and replaces it with fallbackFunctionName, which reduces stack fidelity.

Proposed tweak
       if (lastColonIndex === -1 || secondLastColonIndex === -1) {
         return {
-          function: fallbackFunctionName,
+          function: functionName || fallbackFunctionName,
           platform: 'custom',
           lang: 'javascript',
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@supabase/functions/_backend/utils/posthog.ts` around lines 96 - 101, The
fallback branch currently discards the parsed functionName and always sets
function to fallbackFunctionName; instead, preserve any existing parsed
functionName and only use fallbackFunctionName when functionName is empty.
Update the return in the block guarded by lastColonIndex/secondLastColonIndex to
set function to functionName || fallbackFunctionName (or otherwise prefer
functionName when present), keeping platform and lang as-is; reference the
variables lastColonIndex, secondLastColonIndex, fallbackFunctionName, and
functionName to locate and fix the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@supabase/functions/_backend/utils/posthog.ts`:
- Around line 136-137: The call to getPostHogExceptionUrl(host) may throw for a
malformed POSTHOG_API_HOST and should be guarded so error reporting doesn't fail
before the surrounding try/catch; wrap the call to getPostHogExceptionUrl in a
try/catch (or validate host first) and on error fallback to
POSTHOG_EXCEPTION_URL or null, assign safely to posthogUrl, and ensure
subsequent fetch logic in this module (uses posthogUrl) handles a null/undefined
URL so the reporting path continues without throwing; reference the host
variable, getEnv(c, 'POSTHOG_API_HOST'), getPostHogExceptionUrl,
POSTHOG_EXCEPTION_URL, and posthogUrl when making the change.
- Around line 171-175: The exception properties currently send the full request
URL (url: c.req.url) which can leak query params/fragments; change this to only
include the path component (e.g., use the request pathname) or a sanitized URL
without query/fragment before adding to the payload. Locate the object that sets
method/request_id/status/url (the code referencing c.req.method,
c.get('requestId'), payload.status, c.req.url) and replace c.req.url with a safe
value such as new URL(c.req.url, `http://${c.req.headers.host ||
'localhost'}`).pathname or an equivalent helper that strips query and fragment,
keeping the property name consistent if other code depends on it.
- Around line 179-186: The POST fetch to posthogUrl inside
capturePosthogException currently lacks a timeout; wrap the request with an
AbortController and a short timeout (matching the backend pattern used in
dns-verification.ts/webhook.ts), start a timer that calls controller.abort()
after the timeout, pass signal: controller.signal into the fetch call that sends
JSON.stringify(body), and ensure the timer is cleared on success/failure so the
request cannot hang indefinitely.

---

Nitpick comments:
In `@supabase/functions/_backend/utils/posthog.ts`:
- Around line 96-101: The fallback branch currently discards the parsed
functionName and always sets function to fallbackFunctionName; instead, preserve
any existing parsed functionName and only use fallbackFunctionName when
functionName is empty. Update the return in the block guarded by
lastColonIndex/secondLastColonIndex to set function to functionName ||
fallbackFunctionName (or otherwise prefer functionName when present), keeping
platform and lang as-is; reference the variables lastColonIndex,
secondLastColonIndex, fallbackFunctionName, and functionName to locate and fix
the logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 88ee1f7e-2d0c-4353-9b4b-96b0356ce66d

📥 Commits

Reviewing files that changed from the base of the PR and between 69ee2b7 and ffded9a.

📒 Files selected for processing (1)
  • supabase/functions/_backend/utils/posthog.ts

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d97efac90

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

@riderx riderx merged commit c1da931 into main Mar 25, 2026
13 of 15 checks passed
@riderx riderx deleted the codex/remove-sentry-posthog branch March 25, 2026 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant