# Send Python runtime metrics

Copy as Markdown[Open in ChatGPT](https://chatgpt.com/?q=Read%20https%3A%2F%2Fcoralogix.com%2Fdocs%2Fuser-guides%2Fapm-v2%2Ffeatures%2Fruntime-metrics%2Fsend-python-metrics.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fcoralogix.com%2Fdocs%2Fuser-guides%2Fapm-v2%2Ffeatures%2Fruntime-metrics%2Fsend-python-metrics.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)

This guide walks through the steps to make a Python service emit runtime metrics in the OpenTelemetry format so that they appear in the [Runtime tab](https://coralogix.com/docs/user-guides/apm-v2/features/runtime-metrics/python.md) in the APM entity drilldown.

Use this guide if your service is Python-based but the Runtime tab shows an empty state, or if you are setting up a new Python service and want the tab populated from day one.

No official semantic convention yet

Python runtime metrics do not yet have an official OpenTelemetry semantic-convention specification. They come from the contrib package [`opentelemetry-instrumentation-system-metrics`](https://pypi.org/project/opentelemetry-instrumentation-system-metrics/), and the exact metric names depend on the package version - older releases used a `process.runtime.cpython.*` prefix, while newer ones use `process.*`, `process.runtime.*`, and `cpython.gc.*`. After you instrument the service, confirm in Explore that its runtime metrics appear; the Runtime tab shows a how-to-enable state for any panel whose metric is not reported. CPython also reports no garbage-collection overhead metric, so - unlike the Java Runtime tab - the Python tab has no GC-overhead panel.

## What you need[​](#what-you-need "Direct link to What you need")

* A Python service running Python 3.9 or later, already reporting to Coralogix [APM](https://coralogix.com/docs/user-guides/apm-v2/getting-started/apm-onboarding-tutorial.md).
* The OpenTelemetry [`opentelemetry-instrumentation-system-metrics`](https://pypi.org/project/opentelemetry-instrumentation-system-metrics/) package, plus the OTLP exporter and SDK.
* A Coralogix Send-Your-Data API key.
* The OpenTelemetry endpoint for your Coralogix domain.
* Permission to install packages, set environment variables, or modify the service startup command.

For the full base setup - virtual environment, traces, and framework instrumentation - see [Python OpenTelemetry instrumentation](https://coralogix.com/docs/opentelemetry/instrumentation-options/python-opentelemetry-instrumentation.md). This guide covers only the runtime-metrics layer on top of it.

## Step 1: Install the instrumentation packages[​](#step-1-install-the-instrumentation-packages "Direct link to Step 1: Install the instrumentation packages")

Add the system-metrics instrumentation, the OTLP exporter, and the SDK to your dependencies.

```
pip install \

    opentelemetry-instrumentation-system-metrics \

    opentelemetry-exporter-otlp \

    opentelemetry-sdk
```

The `opentelemetry-instrumentation-system-metrics` package provides the `SystemMetricsInstrumentor`, which collects process and runtime metrics through the OpenTelemetry Metrics SDK and pushes them over OTLP.

## Step 2: Enable the OTLP metrics exporter[​](#step-2-enable-the-otlp-metrics-exporter "Direct link to Step 2: Enable the OTLP metrics exporter")

Point the OTLP metrics exporter at Coralogix. Set the exporter type and the connection details as environment variables.

```
export OTEL_METRICS_EXPORTER="otlp"

export OTEL_EXPORTER_OTLP_ENDPOINT="<coralogix-otel-endpoint>"

export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <CXPrivateKey>"
```

Replace `<CXPrivateKey>` with your Send-Your-Data API key and `<coralogix-otel-endpoint>` with the OpenTelemetry endpoint for your selected Coralogix domain.

## Step 3: Set the resource attributes[​](#step-3-set-the-resource-attributes "Direct link to Step 3: Set the resource attributes")

The Runtime tab correlates metrics to a service through the `service.name` resource attribute, and groups Python instances in the selector and heatmap by `k8s.pod.name`. When `k8s.pod.name` is absent, the backend falls back to other resource attributes - such as `container.name`, `host.name`, and `host.id` - to tell instances apart, but for pod-level granularity in the selector, set `k8s.pod.name` explicitly.

```
export OTEL_RESOURCE_ATTRIBUTES="service.name=<ServiceName>,k8s.pod.name=<PodName>"
```

In Kubernetes, pod-level attributes are not auto-detected by the Python SDK. To populate `k8s.pod.name` and `k8s.namespace.name`, choose one of:

* **Downward API environment variables**: pass pod metadata into the container as env vars and append them to `OTEL_RESOURCE_ATTRIBUTES`. Example: `OTEL_RESOURCE_ATTRIBUTES=...,k8s.pod.name=$(POD_NAME),k8s.namespace.name=$(POD_NAMESPACE)`.
* **OpenTelemetry Operator auto-instrumentation**: install the OpenTelemetry Operator and create an `Instrumentation` custom resource. The Operator injects the Python instrumentation and the right resource attributes automatically.
* **Collector-side enrichment**: run the [`k8sattributes`](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/k8sattributesprocessor) processor in your OpenTelemetry Collector. The processor adds Kubernetes attributes to telemetry as it passes through, so the service itself does not need to know about Kubernetes.

Without one of these, `k8s.pod.name` is missing and the backend distinguishes instances only by the coarser attributes it can detect, which groups multiple pods on the same node together - too coarse for per-pod debugging.

| Attribute                     | Required                  | Purpose                                                                                                                                                              |
| ----------------------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `service.name`                | Yes                       | Identifies the service in the catalog. Without it, metrics arrive but cannot be correlated to a service row.                                                         |
| `service.namespace`           | Recommended               | Disambiguates services with the same name in different teams or environments.                                                                                        |
| `k8s.pod.name`                | Yes for per-pod filtering | The attribute the instances selector and heatmap group by. Set it explicitly via the Downward API, OTel Operator, or Collector enrichment for pod-level granularity. |
| `host.name`                   | Fallback                  | Used to tell instances apart when `k8s.pod.name` is not present (host-level, not pod-level).                                                                         |
| `deployment.environment.name` | Recommended               | Required for the environment filter.                                                                                                                                 |

## Step 4: Enable the system metrics instrumentor[​](#step-4-enable-the-system-metrics-instrumentor "Direct link to Step 4: Enable the system metrics instrumentor")

Activate the instrumentor one of two ways.

**In code**

Create the metric provider, point an OTLP exporter at Coralogix, and start the instrumentor. The example below is representative - validate it against your SDK version and confirm the exact metric names.

```
from opentelemetry import metrics

from opentelemetry.sdk.metrics import MeterProvider

from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader

from opentelemetry.sdk.resources import Resource, SERVICE_NAME

from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter

from opentelemetry.instrumentation.system_metrics import SystemMetricsInstrumentor



# Resource identifies the service and the instance

resource = Resource.create({

    SERVICE_NAME: "<ServiceName>",

    "k8s.pod.name": "<PodName>",

})



# OTLP exporter reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS from the environment

reader = PeriodicExportingMetricReader(OTLPMetricExporter())

provider = MeterProvider(resource=resource, metric_readers=[reader])

metrics.set_meter_provider(provider)



# Start collecting Python runtime metrics

SystemMetricsInstrumentor().instrument()
```

Calling `instrument()` with no arguments collects the instrumentor's default metric set, which covers the metrics the Runtime tab reads. To limit collection to a subset, pass a configuration dict with the correct metric keys:

```
configuration = {

    "process.runtime.memory": ["rss", "vms"],

    "process.cpu.utilization": None,

    "process.cpu.time": ["user", "system"],

    "process.thread.count": None,

    "process.context_switches": ["involuntary", "voluntary"],

    "cpython.gc.collections": None,

    "cpython.gc.collected_objects": None,

}

SystemMetricsInstrumentor(config=configuration).instrument()
```

**Auto-instrumentation**

If you already run the service under the `opentelemetry-instrument` wrapper, the system metrics instrumentor is picked up automatically once the package is installed - no code change is needed.

```
opentelemetry-instrument \

    --metrics_exporter otlp \

    --service_name <ServiceName> \

    python app.py
```

## Step 5: Restart the service[​](#step-5-restart-the-service "Direct link to Step 5: Restart the service")

Restart the service so it picks up the new packages, environment variables, and instrumentor. On startup, confirm the service runs without import or exporter errors and that it reports no OTLP connection failures in its logs.

## Step 6: Verify metrics are flowing[​](#step-6-verify-metrics-are-flowing "Direct link to Step 6: Verify metrics are flowing")

1. In the Coralogix toolbar, go to **Explore**, then **Metrics**.
2. Search for a Python runtime metric - for example `process.runtime.memory` (or the names your package version emits) - filtered by your service name.
3. Confirm at least one data point has arrived in the last few minutes.

Confirm the metric names

Python runtime metrics have no official OpenTelemetry semantic-convention spec, so the exact names depend on the version of `opentelemetry-instrumentation-system-metrics` you installed. If a metric search returns nothing, browse the metric list in Explore for the names your service actually emits.

Querying by service name in Explore

The OpenTelemetry resource attribute `service.name` (with a dot) is transformed during ingestion to the indexed metric label `serviceName` (camelCase). Use `serviceName` when filtering metrics in Explore or PromQL queries. The Runtime tab handles this mapping automatically - you only need to know the label name when you query the metrics directly.

If no Python runtime metrics arrive at all:

* Confirm `OTEL_METRICS_EXPORTER` is set to `otlp`.
* Confirm `SystemMetricsInstrumentor().instrument()` runs on startup, or that the process is wrapped by `opentelemetry-instrument`.
* Confirm the `OTEL_EXPORTER_OTLP_HEADERS` value contains a valid Send-Your-Data API key.
* Confirm the service has network access to the OpenTelemetry endpoint for your domain.
* Check the service logs for OTLP export errors.

## Step 7: Open the Runtime tab[​](#step-7-open-the-runtime-tab "Direct link to Step 7: Open the Runtime tab")

1. In the Coralogix toolbar, select **APM**.
2. Select your service.
3. Select the **Runtime** tab.

The widgets populate as soon as Python runtime metrics for the service are present in the selected time window. If you still see the empty state, return to Step 6 and confirm the metrics are reaching Coralogix.

## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting")

| Symptom                                                             | Likely cause                                                                         | Fix                                                                                                                   |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| Runtime tab is hidden                                               | The service's detected language is not Python, so the Python tab is not shown for it | Confirm the instrumentation is running and `service.name` matches the service in the catalog                          |
| Tab shows the empty state                                           | No Python runtime metrics arriving for the service                                   | Run through Step 6 - most often `OTEL_METRICS_EXPORTER` is not set to `otlp`, or the instrumentor never started       |
| No Python runtime metrics appear in Explore                         | The instrumentor is not active                                                       | Confirm `SystemMetricsInstrumentor().instrument()` runs, or that the process is wrapped by `opentelemetry-instrument` |
| Widgets render but the per-instance filter shows only one instance  | `k8s.pod.name` is not set, so instances cannot be told apart at pod level            | Set `k8s.pod.name` via the Downward API, OTel Operator, or Collector enrichment                                       |
| Metrics arrive in Explore but the catalog does not show the service | `service.name` is missing or differs from the spans' service name                    | Set `service.name` consistently for traces and metrics                                                                |
| No garbage-collection overhead panel                                | CPython reports no GC-overhead metric                                                | Expected - the Python tab has no GC-overhead panel; use the garbage-collection by-generation panels instead           |

## Next steps[​](#next-steps "Direct link to Next steps")

Read and interpret the metrics in the [Python runtime metrics](https://coralogix.com/docs/user-guides/apm-v2/features/runtime-metrics/python.md) tab.

## Related resources[​](#related-resources "Direct link to Related resources")

* [Python runtime metrics](https://coralogix.com/docs/user-guides/apm-v2/features/runtime-metrics/python.md)
* [Runtime metrics](https://coralogix.com/docs/user-guides/apm-v2/features/runtime-metrics.md)
* [Python OpenTelemetry instrumentation](https://coralogix.com/docs/opentelemetry/instrumentation-options/python-opentelemetry-instrumentation.md)
* [`opentelemetry-instrumentation-system-metrics` on PyPI](https://pypi.org/project/opentelemetry-instrumentation-system-metrics/)
