PostHog client wrapper.
A thin adapter over the official posthog SDK that:
- Initialises lazily and only if analytics is enabled.
- Pipes every capture through :mod:redaction and :mod:events for
PII stripping and structural validation.
- Fails silently — analytics must never break the host application.
The underlying SDK handles batching, async send on a background
thread, retries, and silent failure on network errors. We layer
"never crash the app" on top by wrapping every call in try/except.
Classes
AnalyticsClient
Send anonymized usage events to PostHog.
Construct once at server / CLI startup, share for the process
lifetime, call :meth:shutdown on exit to flush pending events.
Source code in src/openjarvis/analytics/client.py
| def __init__(self, config: AnalyticsConfig, anon_id: str | None = None) -> None:
self.config = config
self._enabled = is_analytics_enabled(config)
self.anon_id = (
anon_id or get_or_create_anon_id(config.anon_id_path)
if self._enabled
else ""
)
self._lock = threading.Lock()
self._posthog: Any = None
if self._enabled:
self._init_sdk()
|
Functions
capture
capture(event_name: str, properties: dict[str, Any] | None = None) -> None
Send one event. Unknown events are silently dropped.
Runs through redaction → event-spec validation → SDK capture.
Failures at any stage are swallowed; analytics is best-effort.
Source code in src/openjarvis/analytics/client.py
| def capture(
self,
event_name: str,
properties: dict[str, Any] | None = None,
) -> None:
"""Send one event. Unknown events are silently dropped.
Runs through redaction → event-spec validation → SDK capture.
Failures at any stage are swallowed; analytics is best-effort.
"""
if not self.enabled:
return
try:
raw = properties or {}
cleaned = redact(raw)
validated = validate_event(event_name, cleaned)
if validated is None:
logger.debug("Dropped unknown analytics event: %s", event_name)
return
self._posthog.capture(
distinct_id=self.anon_id,
event=event_name,
properties=validated,
)
except Exception as exc:
logger.debug("Analytics capture failed for %s: %s", event_name, exc)
|
flush
Force-flush queued events. Safe to call repeatedly.
Source code in src/openjarvis/analytics/client.py
| def flush(self) -> None:
"""Force-flush queued events. Safe to call repeatedly."""
if self._posthog is None:
return
try:
self._posthog.flush()
except Exception:
pass
|
disable
Stop accepting events without waiting for network delivery.
Source code in src/openjarvis/analytics/client.py
| def disable(self) -> None:
"""Stop accepting events without waiting for network delivery."""
with self._lock:
self._enabled = False
|
shutdown
shutdown(*, wait: bool = True) -> None
Close the SDK, optionally moving network cleanup off the caller.
Source code in src/openjarvis/analytics/client.py
| def shutdown(self, *, wait: bool = True) -> None:
"""Close the SDK, optionally moving network cleanup off the caller."""
with self._lock:
sdk = self._posthog
self._posthog = None
self._enabled = False
if sdk is None:
return
def close_sdk() -> None:
try:
sdk.flush()
sdk.shutdown()
except Exception:
pass
if wait:
close_sdk()
else:
threading.Thread(
target=close_sdk, name="analytics-shutdown", daemon=True
).start()
|
Functions