feat(server): opt-in uncaught exception capture - #671
Draft
cat-ph wants to merge 1 commit into
Draft
Conversation
This was referenced Aug 3, 2026
Adds captureUncaughtExceptions (default false) to the server PostHogConfig. When enabled, the core PostHogErrorTrackingAutoCaptureIntegration installs a Thread.defaultUncaughtExceptionHandler that captures the throwable as a fatal, unhandled $exception (mechanism UncaughtExceptionHandler), flushes, then delegates to the previously installed handler. Core changes (all additive; Android behavior and the released install(PostHogInterface) path unchanged): - Gate strategy seam on the integration so the server can install with a local-only gate (no remote config, which the server SDK never fetches); Android keeps the remote errorTracking.autocaptureExceptions gate. - Captures flow through an internal CaptureTarget seam so the server's stateless client can drive the integration. - Handler-install ownership is tracked per integration instance, so closing a second opted-in client (whose install was a process-wide no-op) does not tear down the handler a still-open first client owns. - New @PostHogInternal PostHogCapturedThrowables identity marker (weak, ReferenceQueue-pruned). The guard is directional: log mirrors consult it, the uncaught handler only marks — a crash is always captured as the authoritative fatal/unhandled record even if the same instance was logged first (logger.error(..., e); throw e), and marking keeps post-crash log mirrors from re-reporting it. - Repeated setup() cannot replace the owning integration with a non-owning one, which would leave the global handler installed after close(). - With no previous default handler to chain to, the handler reproduces the JVM's built-in stderr crash output so enabling capture never hides crashes from stderr log collection. - Server config KDoc documents the flushAt implication for the crash path. # Conflicts: # posthog/api/posthog.api # posthog/src/main/java/com/posthog/errortracking/PostHogErrorTrackingAutoCaptureIntegration.kt # posthog/src/test/java/com/posthog/errortracking/PostHogErrorTrackingAutoCaptureIntegrationTest.kt
cat-ph
force-pushed
the
cat/java-et-server-config
branch
from
August 3, 2026 23:03
b4e866a to
ffe3132
Compare
cat-ph
force-pushed
the
cat/java-et-uncaught
branch
from
August 3, 2026 23:03
29a1276 to
c9db1af
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
Third PR in the 4-PR JVM error-tracking stack: opt-in capture of uncaught JVM exceptions for the server SDK.
PostHogConfig.captureUncaughtExceptions(also on theBuilder), default off. When enabled,PostHog.setupinstalls a globalThread.defaultUncaughtExceptionHandlerthat captures the crashing exception as a fatal, unhandled$exceptionevent (mechanismUncaughtExceptionHandler), flushes, then delegates to the previously registered handler.close()removes it again.PostHogErrorTrackingAutoCaptureIntegrationtakes an optional caller-suppliedenabledGateinstead of always using the built-in gate (localerrorTrackingConfig.autoCapture+ remote config as a kill-switch, i.e. the behavior from Bug: error tracking autocapture misses crashes on first launch before remote config resolves #648 is preserved for Android).CaptureTargetseam (installWith), because the corePostHogInterfaceand the stateless server client share no capture supertype.Exception in thread "..."stderr output, so opting into capture never hides a crash from stderr log collection.PostHogCapturedThrowablesguard: a weak, identity-keyed, process-wide set letting independent capture paths avoid double-reporting the sameThrowableinstance. It is deliberately directional — the uncaught handler only marks (a crash is always captured as the authoritative fatal/unhandled record, even if the same instance was logged first), while log-mirror paths (the appender in PR 4) consult it and skip instances already reported. It never keeps a throwable or its stack alive.Delivery caveat documented in the KDoc and changeset:
flush()drains the queue synchronously on the crashing thread, but the preceding capture enqueues asynchronously, so under an immediate hard exit the final exception is best-effort — the same guarantee the Android SDK gives. Services that care should keepflushAtlow.💚 How did you test it?
PostHogErrorTrackingAutoCaptureIntegrationTest(local-only gate installs/refuses,CaptureTargetcapture+flush, capture-not-suppressed-by-dedup, marking for later log mirrors, JVM stderr fallback, non-owning instance cannot tear down the installed handler), 2 inPostHogCapturedThrowablesTest(identity keying, value-equal throwables both captured), and 6 in the new serverPostHogUncaughtExceptionTest(off by default, install+chain+restore on close, fatal/unhandled/mechanism assertions over a real/batchrequest, repeated setup keeps ownership, second client does not stack a handler, works with no remote config)../gradlew :posthog:test :posthog-server:testpass (PostHogErrorTrackingAutoCaptureIntegrationTest33 tests,PostHogCapturedThrowablesTest2,PostHogUncaughtExceptionTest6), plus:posthog:apiCheck/:posthog-server:apiCheckandspotlessCheck. API dumps regenerated; additive only.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file🔗 Stacked PR
Position 3 of 4. Base:
cat/java-et-server-config(PR #670).captureExceptionoptionscat/java-et-logback— newposthog-server-logbackappender module