Python metrics installation
Contents
Note: Metrics is in open alpha. Any team can turn it on — open Metrics and select Enable metrics in the onboarding view. Setup details may change before general availability.
The posthog-python SDK includes the posthog.metrics API, so you can record metrics with the same client you use for events and feature flags.
To collect process CPU and memory automatically, use the OpenTelemetry and psutil example below. Installing PostHog or configuring an OTLP exporter alone doesn't collect these measurements.
- 1
Install posthog-python
RequiredTerminalMetrics requires an up-to-date SDK version, so upgrade if you're on an older release.
- 2
Initialize the client
RequiredSet a service name so metrics from different systems stay easy to tell apart. It's attached to every series and used by the Metrics UI for filtering.
PythonUse your project token (the same one you use for capturing events), not a personal API key.
- 3
Record metrics
RequiredUse the metric type that matches what you're measuring:
PythonThe client is thread-safe and pre-aggregates: samples fold into per-series aggregates in memory and flush as one OTLP data point per series every few seconds, so recording in hot paths is cheap. A burst of 10k
count()calls costs one data point on the wire.Keep attribute values small and bounded:
route,status, andplanare good attributes; user IDs, session IDs, and request IDs are not. Every unique combination creates a new series. - 4
Instrument alongside existing metrics
OptionalIf your service already records metrics with Prometheus, StatsD, or another system, don't rip it out. Add the PostHog call next to the existing one, reusing the same metric name and attributes, so both systems chart the same series while you evaluate.
- 5
Flush before exit
RequiredFor short-lived processes (cron jobs, CLIs, serverless functions), flush before exiting so the last aggregation window isn't lost:
Python - 6
Next steps
CheckpointWhat you can do with your metricsAction Description Why you need metrics What metrics show you that events and logs don't Getting started guide Pick the right metric type, add attributes carefully, and chart what matters Group and filter Group by an attribute for one line per value, or filter with key=valuechipsHow metrics works How metrics are ingested, stored, and queried Query with SQL Every metric lands in the posthog.metricstable, queryable from the SQL tab
Collect process CPU and memory
Use psutil to read the current Python process's CPU and memory usage, and OpenTelemetry observable gauges to export the readings to PostHog every 15 seconds. This uses PostHog's standard OTLP endpoint and works alongside posthog-python without requiring it.
Install the dependencies
Start collecting
Save this as process_metrics.py, replace the host and project token, and run python process_metrics.py. Keep it running for at least 30 seconds, then press Ctrl+C to stop.
OpenTelemetry calls the callbacks during collection. They read values without blocking for a sampling interval; you don't need to call record() yourself. The example uses custom metric names to keep these definitions separate from OpenTelemetry's standard process metrics.
For an existing application, run this setup inside the process you want to monitor and replace the while loop with your application's entry point. A separate monitoring script reports its own usage, not your application's. Shut down the provider when your application exits. If you already have an OpenTelemetry meter provider and exporter, register the gauges on that provider instead of creating a second export pipeline.
For multi-worker servers such as Gunicorn, initialize this setup in each worker after it forks, not in the parent process. Each worker needs its own psutil.Process, meter provider, and service.instance.id. Keep the instance ID stable for that worker's lifetime so workers don't overwrite each other's series. Each worker creates two metric series; don't add request IDs or user IDs as attributes.
Understand the measurements
| Metric | Unit | Meaning |
|---|---|---|
app.process.cpu.percent | % | CPU usage since the previous reading. 100 means one fully used core; multi-threaded processes can exceed 100. This isn't divided by the machine's core count or a container's CPU quota. |
app.process.memory.rss | By | Resident set size (RSS), the process's memory resident in physical RAM, in bytes. This isn't virtual memory, Python heap size, or a percentage of a memory limit. |
These readings cover only the current process, not its child processes, the whole host, or the whole container. RSS can include shared pages, so summing workers' RSS can overstate their combined physical memory use.
For host or container usage and capacity, collect those measurements separately, for example with an OpenTelemetry Collector host metrics receiver for host metrics, or existing infrastructure exporters through the Docker or Kubernetes scraping setup. The PostHog metrics agent forwards existing metrics; installing it alone doesn't create CPU or memory measurements.
Chart CPU and memory in PostHog
- Open Metrics in PostHog and select Enable metrics if needed.
- In Viewer, filter to
service.name=billing-workerand selectapp.process.cpu.percentorapp.process.memory.rss. - Choose
avgfor either gauge, then group byservice.instance.idto see one line per worker. Keep this grouping so a busy worker isn't hidden by a fleet-wide average. Don't userateorincreaseon these gauges. - Use a line chart and a recent time range. Enable Auto-refresh while testing. An idle process can report CPU values near zero; memory should remain above zero.
- Click Save as insight or Add to dashboard to keep the chart. See Use your metrics for chart settings and alerts.
The CPU values are already percentages (25 means 25% of one core). Memory values are bytes (1048576 means 1 MiB). If a chart shows raw numbers, interpret them using these units; don't assume it converts bytes to MiB or scales CPU values automatically. Avoid a fixed CPU maximum of 100 when a process can use multiple cores.











