Skip to content

fix(observability): use posthog-node captureException SDK method for UI grouping#3659

Merged
PierreBrisorgueil merged 2 commits into
masterfrom
fix/sdk-captureexception
May 11, 2026
Merged

fix(observability): use posthog-node captureException SDK method for UI grouping#3659
PierreBrisorgueil merged 2 commits into
masterfrom
fix/sdk-captureexception

Conversation

@PierreBrisorgueil
Copy link
Copy Markdown
Contributor

@PierreBrisorgueil PierreBrisorgueil commented May 11, 2026

Summary

  • Replaces legacy client.capture({event: '$exception', props}) with SDK native client.captureException(err, distinctId, additionalProperties)
  • PostHog SDK auto-populates $exception_list (with parsed stack frames) and $exception_fingerprint — required by the Error Tracking UI for grouping/display
  • Error Tracking page was empty in prod despite events flowing because $exception_list = null (legacy format not recognized by UI)

Closes #3658. Plan ref: infra/docs/superpowers/plans/2026-05-11-posthog-observability-wave4.md.

Files changed

  • lib/services/analytics.jscaptureException() implementation (5 lines)
  • lib/services/tests/analytics.captureException.unit.tests.js — assertions shifted to captureException mock, +1 new requestId test
  • lib/services/tests/analytics.service.unit.tests.js — same assertion shift
  • lib/services/tests/logger.posthog.transport.integration.tests.js — same assertion shift

Test plan

  • npm run lint — clean
  • npm run test:unit analytics path — 145 tests pass (7 in captureException suite, 4 in logger transport)
  • npm run test:coverage — 1785 pass, only pre-existing billing.extraBalance.listLedger.perf MongoDB failure (unrelated $sortArray)
  • CI green
  • CodeRabbit clean

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced error tracking mechanism to improve error grouping and attribution with request IDs and source identification.
  • Tests

    • Updated error tracking tests to verify improved exception capture behavior.

Review Change Stack

…UI grouping

Replaces legacy client.capture({event: '\$exception', props}) with SDK
client.captureException(err, distinctId, additionalProperties) so PostHog
auto-populates \$exception_list + \$exception_fingerprint — required by the
Error Tracking UI for grouping/display. Fixes #3658.
Copilot AI review requested due to automatic review settings May 11, 2026 13:47
@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.20%. Comparing base (829b91b) to head (50fe7ff).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3659   +/-   ##
=======================================
  Coverage   89.20%   89.20%           
=======================================
  Files         136      136           
  Lines        4668     4669    +1     
  Branches     1451     1452    +1     
=======================================
+ Hits         4164     4165    +1     
  Misses        392      392           
  Partials      112      112           
Flag Coverage Δ
integration 59.60% <100.00%> (+<0.01%) ⬆️
unit 64.36% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 829b91b...50fe7ff. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the analytics error-reporting path to use the PostHog Node SDK’s native captureException() API so PostHog Error Tracking can populate $exception_list / $exception_fingerprint and properly group/display exceptions in the UI (fix for #3658).

Changes:

  • Switch AnalyticsService.captureException() from legacy capture({ event: '$exception', ... }) to client.captureException(err, distinctId, additionalProperties).
  • Update unit/integration tests to assert against captureException calls (and add a requestId assertion).
  • Keep existing no-op/safety behavior (no client / swallow SDK errors) while aligning payload shape with PostHog Error Tracking expectations.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
lib/services/analytics.js Replace legacy $exception capture with SDK captureException and pass additional properties for PostHog Error Tracking UI grouping.
lib/services/tests/analytics.captureException.unit.tests.js Update expectations to validate captureException(err, distinctId, props) behavior and include a requestId property test.
lib/services/tests/analytics.service.unit.tests.js Update service-level tests to assert captureException calls and error-swallowing behavior via the new SDK method.
lib/services/tests/logger.posthog.transport.integration.tests.js Update logger→PostHog integration assertions to validate that errors flow through SDK captureException.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Warning

Rate limit exceeded

@PierreBrisorgueil has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 38 minutes and 45 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 76421be5-8430-49b8-9bd7-2808ab5a5247

📥 Commits

Reviewing files that changed from the base of the PR and between 0d91834 and 50fe7ff.

📒 Files selected for processing (3)
  • lib/services/analytics.js
  • lib/services/tests/analytics.captureException.unit.tests.js
  • lib/services/tests/logger.posthog.transport.integration.tests.js

Walkthrough

The PR migrates exception capture in the analytics service from manually emitting '$exception' events via client.capture() to calling PostHog SDK's native client.captureException() method. This enables proper error grouping via canonical $exception_list and $exception_fingerprint fields. The implementation adds requestId, applies default source: 'system' with explicit override support, and merges context properties. All unit, service, and integration tests are updated to verify the new SDK method behavior.

Changes

PostHog SDK captureException Migration

Layer / File(s) Summary
Implementation Update
lib/services/analytics.js
captureException method calls client.captureException(err, distinctId, additionalProperties) with requestId, default source: 'system', merged ctx.properties, and explicit ctx.source override. JSDoc updated to document $exception_list/$exception_fingerprint grouping.
Unit Tests: captureException
lib/services/tests/analytics.captureException.unit.tests.js
PostHog mock extended with captureException. Tests verify default source: 'system', ctx.source and ctx.properties.source precedence, property merging (logMessage, logLevel), requestId inclusion, and null/undefined error handling.
Service Tests: AnalyticsService.captureException
lib/services/tests/analytics.service.unit.tests.js
Service mock extended with captureException. Tests verify correct SDK invocation with error, distinctId, and properties (requestId, source), anonymous fallback, no-op when uninitialized, and silent error swallowing.
Integration Tests: Logger → PostHog Flow
lib/services/tests/logger.posthog.transport.integration.tests.js
Integration tests updated to assert captureException SDK calls from logger.error with proper error objects and metadata. Tests verify posthogCaptured flag prevents duplicate captures.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • pierreb-devkit/Node#3657: Modifies Express logging to pass Error objects ({ error: err }) to PostHog transport for proper deduplication via posthogCaptured flag, complementing this PR's SDK method switch.
  • pierreb-devkit/Node#3653: Also modifies captureException to normalize source ('system'), merge ctx.properties, and attach requestId, with overlapping implementation changes.
  • pierreb-devkit/Node#3641: Restructures PostHog config and initialization logic used by the analytics service that this PR updates.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly summarizes the main change: replacing legacy capture event with SDK's native captureException method to enable UI grouping.
Description check ✅ Passed Description covers summary, files changed, and test plan; however, the template's 'Scope' and 'Validation' sections with checkboxes are not fully completed as a structured form.
Linked Issues check ✅ Passed Code changes fully address issue #3658: replaced legacy capture('$exception') with client.captureException() SDK method across all four test and implementation files, enabling $exception_list and $exception_fingerprint population.
Out of Scope Changes check ✅ Passed All changes are in scope: four files modified (analytics.js implementation + three test files) to align captureException usage with linked issue #3658; no unrelated or extraneous modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sdk-captureexception

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

coderabbitai[bot]
coderabbitai Bot previously requested changes May 11, 2026
Copy link
Copy Markdown

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/services/analytics.js`:
- Around line 176-181: The current construction of additionalProperties lets
ctx.properties.requestId override ctx.requestId because ...(ctx.properties ??
{}) is spread after the explicit requestId key; update additionalProperties so
that the authoritative requestId (ctx.requestId) wins by moving the requestId
assignment after the spread of ctx.properties (or by spreading ctx.properties
first and then adding requestId: ctx.requestId and finally ...explicit),
referencing the additionalProperties object and the ctx.requestId /
ctx.properties symbols to locate the change.

In `@lib/services/tests/logger.posthog.transport.integration.tests.js`:
- Around line 37-49: The test currently asserts payload shape but not
uniqueness; add an explicit call-count assertion to ensure a single emission by
verifying mockPostHogInstance.captureException was called exactly once (use
expect(mockPostHogInstance.captureException).toHaveBeenCalledTimes(1)) in the
test 'logger.error(message, { error }) emits a single $exception event via SDK
captureException' after invoking logger.error and before/after the existing
toHaveBeenCalledWith assertion so duplicate emits fail the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 946b80d2-ac22-4bb9-90d0-692831cf1f8c

📥 Commits

Reviewing files that changed from the base of the PR and between 829b91b and 0d91834.

📒 Files selected for processing (4)
  • lib/services/analytics.js
  • lib/services/tests/analytics.captureException.unit.tests.js
  • lib/services/tests/analytics.service.unit.tests.js
  • lib/services/tests/logger.posthog.transport.integration.tests.js

Comment thread lib/services/analytics.js
Comment thread lib/services/tests/logger.posthog.transport.integration.tests.js
CodeRabbit findings on PR #3659:
- captureException additionalProperties construction let
  ctx.properties.requestId override ctx.requestId. Move requestId
  spread after ctx.properties so the explicit ctx.requestId wins.
- logger.posthog.transport.integration.tests.js asserted payload
  shape but not call count. Add toHaveBeenCalledTimes(1).

+1 unit test for ctx.requestId precedence.
@PierreBrisorgueil PierreBrisorgueil dismissed coderabbitai[bot]’s stale review May 11, 2026 14:08

Findings addressed: requestId precedence + toHaveBeenCalledTimes(1) — see commit message

@PierreBrisorgueil PierreBrisorgueil merged commit 25db1be into master May 11, 2026
5 checks passed
@PierreBrisorgueil PierreBrisorgueil deleted the fix/sdk-captureexception branch May 11, 2026 14:10
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.

fix(observability): use posthog-node captureException SDK method ($exception_list format for UI grouping)

2 participants