Observability
PostHog analytics, Sentry crash reporting, test controls, and consent behavior.
The Kotlin template has two optional observability layers: PostHog for product
analytics and Sentry for crash reporting. Both are configured from
local.properties, and both no-op when their keys are absent.
What it provides
| Surface | Status | Implementation |
|---|---|---|
| Product analytics | Integrated | AnalyticsController wraps PostHog Android |
| Typed analytics events | Integrated | AnalyticsEvent sealed class |
| User identity | Integrated | PostHog identify on sign-in, reset on sign-out |
| Crash reporting | Integrated | SentryReporter initializes Sentry Android |
| User tagging | Integrated | Sentry scope user follows SessionController.currentUser |
| Error fallback | Integrated | AppErrorBus + ErrorBoundary + in-app feedback |
Key files
Configuration
# Sentry
SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
# PostHog
POSTHOG_API_KEY=phc_your_key
POSTHOG_HOST=https://us.i.posthog.com
POSTHOG_DISABLED=false
POSTHOG_DEBUG=falseMissing SENTRY_DSN skips Sentry SDK initialization and capture calls return
without sending. Missing POSTHOG_API_KEY, or POSTHOG_DISABLED=true, makes
analytics track() and screen() no-op.
PostHog analytics
SoarApp.onCreate() calls analyticsController.start(). When configured, it
builds a PostHogAndroidConfig with:
| Option | Value |
|---|---|
| API key | BuildConfig.POSTHOG_API_KEY |
| Host | BuildConfig.POSTHOG_HOST, default https://us.i.posthog.com |
| Lifecycle capture | captureApplicationLifecycleEvents = true |
| Debug logging | BuildConfig.POSTHOG_DEBUG |
The controller subscribes to SessionController.currentUser. On sign-in it
identifies the user by Supabase UUID and sends safe account properties such as
email, email domain, account creation time, and environment. On sign-out it
calls PostHog.reset().
Typed app events live in AnalyticsEvent:
| Event | Name | Current call sites |
|---|---|---|
ScreenViewed | screen viewed | Available helper event |
OnboardingCompleted | onboarding completed | OnboardingGate |
PaywallViewed | paywall viewed | Available event type |
ReferralInviteShared | referral invite shared | Available event type |
PurchaseCompleted | purchase completed | PaywallViewModel after purchase |
PurchaseRestored | purchase restored | Available event type |
Add new events by extending the sealed class and keeping property names stable.
Sentry crash reporting
SoarApp.onCreate() also calls sentryReporter.start(). When SENTRY_DSN is
present, the reporter initializes Sentry Android with:
| Option | Value |
|---|---|
| Environment | development for debug builds, production otherwise |
| Release | <applicationId>@<versionName>+<versionCode> |
| Trace sample rate | 1.0 in debug, 0.2 in non-debug |
| Stack traces | Attached |
The reporter follows SessionController.currentUser and tags Sentry scope with
the Supabase user id and email while signed in.
Error boundary
MainActivity wraps the app navigation in:
ErrorBoundary(errorBus = appErrorBus, reporter = sentryReporter)View models and controllers can call AppErrorBus.report(throwable, boundary).
The bus captures the exception in Sentry when configured and stores the active
error in a StateFlow. ErrorBoundary then renders the in-app fallback with:
- Try again, which clears the active error.
- Send a report, which captures a Sentry feedback event.
- A debug-only detail panel with the message and stack trace.
This boundary handles errors routed through AppErrorBus. It does not replace
Sentry's normal uncaught-exception capture for process crashes.
Privacy and Play data safety
The current code starts PostHog from SoarApp.onCreate(), before the legal
ConsentGate is accepted. If your app must wait for consent before tracking,
move analyticsController.start() behind ConsentController.hasAcceptedCurrent
or ship with POSTHOG_DISABLED=true until the user has accepted.
Play Console data safety declarations should reflect the SDKs you actually ship. With the template defaults, expect to account for:
| Data category | Typical source |
|---|---|
| User IDs and email | Supabase Auth, PostHog identify, Sentry user scope |
| App interactions | PostHog events and lifecycle capture |
| Crash logs and diagnostics | Sentry |
| Device identifiers | Advertising ID permission, Firebase, PostHog |
If you remove PostHog or Firebase, revisit AndroidManifest.xml, SDK
dependencies, and the Play data-safety form together.
Earlier planning notes mention a dev-only feature/test/ screen. That screen
is not present in the current Kotlin codebase, so manual observability tests
should be done from the real call sites or by adding your own debug-only
trigger screen.
Verify
- Leave all observability keys empty and confirm the app starts without crashes or network setup errors.
- Add
POSTHOG_API_KEY, sign in, and confirm PostHog identifies the Supabase user id. - Complete onboarding and confirm
onboarding completedis captured. - Complete a test purchase and confirm
purchase completedis captured. - Add
SENTRY_DSN, trigger a routedAppErrorBus.report(), and confirm the fallback screen plus Sentry event. - Sign out and confirm PostHog resets and Sentry clears the user scope.
