# Send Node.js 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-nodejs-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-nodejs-metrics.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)

This guide walks through the steps to make a Node.js 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/nodejs.md) for the service in the APM entity drilldown.

Use this guide if your service is Node.js-based but the Runtime tab shows a zero state, or if you are setting up a new Node.js service and want to ensure the tab is populated from day one.

OpenTelemetry semantic conventions only

The Runtime tab reads only the runtime metrics emitted by [`@opentelemetry/instrumentation-runtime-node`](https://www.npmjs.com/package/@opentelemetry/instrumentation-runtime-node) - the `nodejs.*` (event loop) and `v8js.*` (V8 heap and garbage collection) namespaces defined by the OpenTelemetry semantic conventions. Metrics that follow other naming conventions, such as the defaults exposed by `prom-client`, are not recognized.

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

* A Node.js service running Node.js 22.15.0 or later (required by `@opentelemetry/instrumentation-runtime-node`).
* The OpenTelemetry Node.js runtime instrumentation and an OTLP metrics exporter (installed in Step 1).
* A Coralogix Send-Your-Data API key.
* The OpenTelemetry endpoint for your Coralogix domain.
* Permission to set environment variables or modify the service start command.

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

Add the runtime instrumentation, the Node SDK, and an OTLP metrics exporter to your project.

```
npm install --save @opentelemetry/sdk-node

npm install --save @opentelemetry/sdk-metrics

npm install --save @opentelemetry/exporter-metrics-otlp-grpc

npm install --save @opentelemetry/instrumentation-runtime-node
```

This example uses the gRPC metrics exporter. To export over HTTP instead, install `@opentelemetry/exporter-metrics-otlp-http` and use it in place of the gRPC package in the steps below.

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

Create an instrumentation file that registers `RuntimeNodeInstrumentation` with the Node SDK and a periodic metric reader that exports over OTLP to Coralogix. Load this file before your application code.

```
// instrumentation.js

const { NodeSDK } = require('@opentelemetry/sdk-node');

const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');

const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc');

const {

  RuntimeNodeInstrumentation,

} = require('@opentelemetry/instrumentation-runtime-node');



const sdk = new NodeSDK({

  metricReader: new PeriodicExportingMetricReader({

    exporter: new OTLPMetricExporter(),

  }),

  instrumentations: [new RuntimeNodeInstrumentation()],

});



sdk.start();
```

`RuntimeNodeInstrumentation` also accepts a `monitoringPrecision` option (milliseconds, default `10`) that controls how often the event-loop delay metrics are sampled.

With no arguments, `OTLPMetricExporter` reads its endpoint and headers from environment variables. Set them before the process starts:

```
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.

To configure the exporter in code instead, pass `url` and `headers` to `OTLPMetricExporter({ ... })`.

## 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 Node.js 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, set `k8s.pod.name` explicitly.

Set the service name and the other attributes through environment variables that the SDK reads at startup:

```
export OTEL_SERVICE_NAME="<ServiceName>"

export OTEL_RESOURCE_ATTRIBUTES="cx.application.name=<CXApplicationName>,cx.subsystem.name=<CXSubsystemName>"
```

In Kubernetes, `k8s.pod.name` and other pod-level attributes are not auto-detected by the Node SDK - its default resource detectors fill in `host.name`, `host.id`, and process attributes only. 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 Node.js 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 application 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 (such as `host.name`, the Kubernetes node), 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. Set it via `OTEL_SERVICE_NAME`.                         |
| `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`                   | Auto-detected             | Backend fallback for telling 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: Restart the service[​](#step-4-restart-the-service "Direct link to Step 4: Restart the service")

Restart the service so it loads the instrumentation file before your application code. Preload it with the `--require` flag:

```
node --require ./instrumentation.js app.js
```

Or through `NODE_OPTIONS`:

```
export NODE_OPTIONS="--require ./instrumentation.js"

node app.js
```

The instrumentation file must be loaded before the rest of the application so that the SDK starts first.

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

1. In the Coralogix toolbar, go to **Explore**, then **Metrics**.
2. Search for a Node.js runtime metric - for example `v8js.memory.heap.used` or `nodejs.eventloop.utilization` - filtered by your service name.
3. Confirm at least one data point has arrived in the last few minutes.

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 `nodejs.*` or `v8js.*` metrics arrive at all:

* Confirm the instrumentation file is loaded before application code (the `--require` flag or `NODE_OPTIONS`).
* 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.
* Enable SDK diagnostic logging to surface OTLP export errors.

## Step 6: Open the Runtime tab[​](#step-6-open-the-runtime-tab "Direct link to Step 6: 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 `nodejs.*` and `v8js.*` metrics for the service are present in the selected time window. If you still see the zero state, return to Step 5 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 Node.js, so the tab is not shown for it | Confirm the instrumentation is running and `service.name` matches the service in the catalog                |
| Tab shows the zero state                                                      | No `nodejs.*` or `v8js.*` metrics arriving for the service                     | Run through Step 5 - most often the instrumentation file is not preloaded before application code           |
| Prometheus-style runtime metrics appear in Explore but the tab stays empty    | Metrics use `prom-client` naming, not the OpenTelemetry conventions            | Switch to `@opentelemetry/instrumentation-runtime-node`, which emits the `nodejs.*` and `v8js.*` namespaces |
| Widgets render but the per-instance filter and heatmap show only one instance | Resource detection has not picked up `k8s.pod.name` or `host.name`             | 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                                                      |

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

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

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

* [Node.js runtime metrics](https://coralogix.com/docs/user-guides/apm-v2/features/runtime-metrics/nodejs.md)
* [Runtime metrics](https://coralogix.com/docs/user-guides/apm-v2/features/runtime-metrics.md)
* [Node.js OpenTelemetry instrumentation](https://coralogix.com/docs/opentelemetry/instrumentation-options/nodejs-opentelemetry-instrumentation.md)
