Repository navigation
feat: add opt-in macOS runtime metrics - #1063
marandaneto wants to merge 1 commit into
Conversation
posthog-python Compliance ReportDate: 2026-10-15T01:27:37.476966+00:00 ✅ All Tests Passed!121/121 tests passed Capture_V1 Tests✅ 95/95 tests passed View Details
Capture_Ai Tests✅ 5/5 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
Feature_Flags_Local_Evaluation Tests✅ 4/4 tests passed View Details
|
There was a problem hiding this comment.
PostHog Review · Standard · reviewed de1c620: 1 Must fix, 2 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
| self._collect_runtime_metrics() | ||
| try: | ||
| with self._flush_lock: | ||
| self._do_flush() |
There was a problem hiding this comment.
P1 · Prevent overlapping runtime timer flushes
When exports take longer than flush_interval, _collect_runtime_metrics() arms another timer before the current export finishes. Subsequent ticks accumulate threads waiting on _flush_lock, and those queued flushes send immediately after failures, bypassing _do_flush() retry backoff. Keep one timer flush in flight and defer rearming until it completes.
| self._runtime_metrics = _MacOSProcessMetrics() | ||
| with self._lock: | ||
| self._arm_flush_timer() |
There was a problem hiding this comment.
P2 · Serialize runtime collector startup with shutdown
When late module setup() enables runtime metrics concurrently with shutdown(), shutdown can finish while _MacOSProcessMetrics() initializes. Startup then installs the collector and arms its timer after _stop_runtime_metrics() has finished, so the closed client continues collecting and exporting. Serialize startup and stop under _runtime_lock and recheck lifecycle state.
| "process.pid": self._pid, | ||
| "process.runtime_id": uuid4().hex, | ||
| } | ||
| self.start_time_ms = int(time() * 1000) |
There was a problem hiding this comment.
P2 · Align cumulative counter values with their start timestamp
When collection starts after the process has consumed CPU or performed GC, _reset() declares collection startup as the cumulative start, but collect() exports lifetime totals. OTLP interprets the initial interval as accumulating from zero, attributing pre-start work to that interval and inflating its rates. Use the actual counter start or subtract initialization baselines.
💡 Motivation and Context
The metrics API currently requires applications to collect and submit process measurements themselves. This adds opt-in automatic runtime metrics on macOS without adding dependencies.
Sampling starts when the client is created and uses
flush_interval, which defaults to 10 seconds. It also runs withsync_mode=True.shutdown()collects a final sample, flushes, and stops the timer. Other platforms warn and skip automatic collection. The feature is disabled by default.The collector follows the OpenTelemetry process and CPython runtime conventions for the metrics it implements:
process.cpu.timeandprocess.cpu.utilization, split bycpu.mode.process.memory.usagefor current RSS andprocess.memory.utilizationfor RSS divided by total physical RAM. The latter is not utilization against a container limit.process.thread.countfor native threads, including threads created by C extensions.cpython.gc.collections,cpython.gc.collected_objects, andcpython.gc.uncollectable_objects, split by generation. These measure collection activity, not heap size or GC pauses.Two custom metrics supplement these conventions.
app.process.cpu.percenttreats 100% as one busy core.app.process.cpu.ctx_switch.totalreports combined context switches because the native reader does not provide the voluntary/involuntary split required by the standard metric.Only the current process is measured, not its children or the host as a whole. Forked workers get independent identities and CPU baselines. Automatic observations use cumulative sums where appropriate, while manual counts, gauges, and histograms keep their existing behavior. Collection uses the existing transport and
before_sendhook. The example menu includes a 60-second runtime metrics demo.No published PostHog SDK spec currently defines automatic runtime metrics. This is a new metrics configuration option.
💚 How did you test it?
posthoganalyticsmirror.de1c6209f4254aa359a8c2bab8402230e74ef433with no actionable findings.Dashboard
Python Runtime Metrics dashboard
📝 Checklist
If releasing new changes
.sampo/changesets/.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Implemented with Pi, using Git, GitHub CLI, uv, PostHog tools, and native browser automation. The user directed the scope and chose dependency-free macOS collection with OpenTelemetry metric semantics. Profiling, host monitoring, additional platforms, and external runtime dependencies are not included. An isolated Pi autoreview checked the final branch diff. Human review is still required.
Pi session:
01a13c7d-7fef-7004-b65a-26fde3f09a6e(local session, not publicly shared).