> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo OTel Col Metrics

> Configuring the OpenTelemetry Collector to ingest metrics via OTLP push and Prometheus scrape with example configs, application code, logs, and troubleshooting tips

This guide demonstrates how to configure the OpenTelemetry Collector to receive metrics using two common approaches:

* Push-based (OTLP) — your application pushes metrics to the Collector.
* Pull-based (Prometheus-style) — the Collector scrapes your application for metrics.

Below are compact, corrected Collector configuration and application examples used in the demo, followed by representative Collector console output showing metrics ingestion. Use these snippets to validate both ingestion models and to troubleshoot common issues.

<Callout icon="lightbulb" color="#1CB2FE">
  This article shows two common ways to get metrics into the Collector: OTLP push and Prometheus scrape. You can enable both simultaneously by configuring both receivers and adding them to your metrics pipeline.
</Callout>

## Collector: common pieces

The Collector configuration below contains attribute enrichment, an OTLP receiver, a Prometheus receiver (for scraping), processors, and exporters. The critical parts for metrics are the `receivers` and the `service.pipelines.metrics` section that wires receivers into the metrics pipeline.

```yaml theme={null}
# collectors-config.yaml
extensions:
  health_check: {}

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  prometheus:
    config:
      scrape_configs:
        - job_name: "python-app"
          scrape_interval: 15s
          static_configs:
            - targets: ["python-app:8000"]

processors:
  batch:
    timeout: 15s
    send_batch_size: 512
  attributes/add_env:
    actions:
      - key: deployment.environment
        value: production
        action: insert
      - key: service.region
        value: us-west-2
        action: upsert

exporters:
  debug:
    verbosity: detailed
  otlp/jaeger:
    endpoint: http://jaeger:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [attributes/add_env, batch]
      exporters: [debug, otlp/jaeger]
    metrics:
      receivers: [prometheus, otlp]
      processors: [attributes/add_env, batch]
      exporters: [debug]
    logs:
      receivers: [otlp]
      exporters: [debug]
```

Key points:

* The `prometheus` receiver uses Prometheus-style `scrape_configs` so the Collector behaves like Prometheus and pulls metrics from `python-app:8000`.
* The `otlp` receiver accepts OTLP over both gRPC and HTTP (useful for client push exporters).
* For metrics ingestion, make sure the receiver(s) are listed under `service.pipelines.metrics.receivers`.

## Quick comparison: Push vs Pull

| Aspect             |                                               Push (OTLP) | Pull (Prometheus scrape)                                               |
| ------------------ | --------------------------------------------------------: | ---------------------------------------------------------------------- |
| How metrics arrive |                  Application pushes to Collector via OTLP | Collector scrapes `/metrics` endpoint                                  |
| Typical endpoint   |                        `http://collector:4318/v1/metrics` | `http://your-app:8000/metrics`                                         |
| Use case           | Instrumented apps pushing telemetry (e.g., SDK exporters) | Existing Prometheus exporters or instrumented apps exposing `/metrics` |
| Collector role     |                                 OTLP receiver (HTTP/gRPC) | Prometheus receiver (scraper)                                          |
| Good for           |                  Short-lived batches, centralized pushing | Long-lived services with Prometheus exposition                         |

## Push-based metrics (OTLP)

For push-based metrics, configure an OTLP metrics exporter in your application to send metrics to the Collector. When using the HTTP OTLP exporter, provide the full URL including `/v1/metrics`.

Example Python configuration using OTLP HTTP metrics exporter and a periodic metric reader:

```python theme={null}
# metrics_push.py
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.resources import Resource

def configure_meter_push() -> metrics.Meter:
    export_url = "http://localhost:4318/v1/metrics"  # Collector OTLP HTTP endpoint
    otlp_exporter = OTLPMetricExporter(endpoint=export_url)

    # Export every 5 seconds
    reader = PeriodicExportingMetricReader(otlp_exporter, export_interval_millis=5000)

    provider = MeterProvider(resource=Resource.create({}), metric_readers=[reader])
    metrics.set_meter_provider(provider)

    return metrics.get_meter(name="cooling-heating", version="0.1.2")
```

Create observable gauges in the app (example):

```python theme={null}
# inside your application startup
import random
from opentelemetry.metrics import Observation

meter = configure_meter_push()

def temperature_callback(options):
    return [Observation(random.uniform(60.0, 90.0))]

def humidity_callback(options):
    return [Observation(random.uniform(0.0, 1.0))]

meter.create_observable_gauge(
    "current_temperature_fahrenheit",
    description="Current Temperature",
    unit="degF",
    callbacks=[temperature_callback],
)

meter.create_observable_gauge(
    "current_humidity_percentage",
    description="Current Humidity",
    unit="1",  # fraction 0.0 - 1.0
    callbacks=[humidity_callback],
)
```

Run the application:

```bash theme={null}
python scripts/metrics-push.py
```

Collector debug output when receiving pushed metrics (cleaned and truncated for clarity):

```text theme={null}
otel-collector | 2025-09-30T00:12:22.523Z info service@v0.135.0/service.go:211 Starting otelcol-contrib...
otel-collector | 2025-09-30T00:12:22.523Z info extensions/extensions.go:41 Starting extensions..
otel-collector | 2025-09-30T00:12:22.523Z info otlpreceiver@v0.135.0/otlpreceiver.go:121 Starting GRPC server {"endpoint":"[::]:4317"}
otel-collector | 2025-09-30T00:12:22.524Z info otlpreceiver@v0.135.0/otlpreceiver.go:179 Starting HTTP server {"endpoint":"[::]:4318"}
otel-collector | 2025-09-30T00:12:22.534Z info service@v0.135.0/service.go:234 Everything is ready. server
```

When the Collector's `debug` exporter prints pushed metrics it may show:

```text theme={null}
ResourceMetrics #0
Resource attributes:
  telemetry.sdk.language: Str(python)
  telemetry.sdk.name: Str(opentelemetry)
  telemetry.sdk.version: Str(1.0.0)
ScopeMetrics #0
InstrumentationScope cooling-heating 0.1.2
Metric #0
Descriptor:
  - Name: current_temperature_fahrenheit
  - Description: Current Temperature
  - Unit: degF
  - DataType: Gauge
NumberDataPoints #0
Timestamp: 2025-09-30T00:14:45.251669000 UTC
Value: 78.554979
Metric #1
Descriptor:
  - Name: current_humidity_percentage
  - Description: Current Humidity
  - Unit: 1
  - DataType: Gauge
NumberDataPoints #0
Timestamp: 2025-09-30T00:14:45.251669000 UTC
Value: 0.407680
```

This verifies successful push-based ingestion: the app exported metrics to `http://localhost:4318/v1/metrics`, the Collector received them via its OTLP HTTP receiver, and the `debug` exporter printed them.

## Pull-based metrics (Prometheus scraping)

For Prometheus-style scraping, your application exposes an HTTP endpoint that returns Prometheus-formatted metrics (commonly using the `prometheus_client` library). The Collector scrapes that endpoint using the Prometheus receiver.

Example Python configuration using `PrometheusMetricReader`:

```python theme={null}
# metrics_pull.py
from prometheus_client import start_http_server
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.resources import Resource
from opentelemetry.exporter.prometheus import PrometheusMetricReader

def configure_meter_pull():
    # Expose metrics at http://localhost:8000/metrics for Prometheus scraping
    start_http_server(port=8000, addr="localhost")

    reader = PrometheusMetricReader()
    provider = MeterProvider(resource=Resource.create({}), metric_readers=[reader])
    metrics.set_meter_provider(provider)
    return metrics.get_meter(name="cooling-heating", version="0.1.2")
```

Observable gauges and Flask integration example:

```python theme={null}
# inside your application startup
import random
from opentelemetry.metrics import Observation
from flask import Flask

meter = configure_meter_pull()

def temperature_callback(options):
    return [Observation(random.uniform(60.0, 90.0))]

def humidity_callback(options):
    return [Observation(random.uniform(0.0, 1.0))]

meter.create_observable_gauge(
    "current_temperature_fahrenheit",
    description="Current Temperature",
    unit="degF",
    callbacks=[temperature_callback],
)

meter.create_observable_gauge(
    "current_humidity_percentage",
    description="Current Humidity",
    unit="1",
    callbacks=[humidity_callback],
)

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello, metrics!"
```

Start the app:

```bash theme={null}
python3 scripts/metrics-pull.py
```

Collector logs when starting and scraping (important parts):

```text theme={null}
otel-collector | 2025-09-30T10:09:45Z info otlpreceiver@v0.135.0/otlpreceiver.go:121 Starting gRPC server {"endpoint":"[::]:4317"}
otel-collector | 2025-09-30T10:09:45Z info otlpreceiver@v0.135.0/otlpreceiver.go:179 Starting HTTP server {"endpoint":"[::]:4318"}
otel-collector | 2025-09-30T10:09:45Z info service@v0.135.0/service.go:234 Everything is ready. Begin running and processing data
```

After scraping (every 15s as configured), the `debug` exporter prints scraped metrics. Example (truncated):

```text theme={null}
2025-09-30T00:24:49.424Z info Metrics {"otelcol.component.kind":"exporter","otelcol.signal":"metrics"}
ResourceMetrics #0
Resource attributes:
  -> service.name: Str(python-app)
  -> service.address: Str(python-app)
  -> service.instance.id: Str(python-app:8000)
  -> server.port: Str(8000)
  -> url.scheme: Str(http)
ScopeMetrics #0
InstrumentationScope github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheus 0.135.0
Metric #0
Descriptor:
  -> Name: python_gc_objects_collected_total
  -> Description: Objects collected during gc
  -> DataType: Sum
  -> IsMonotonic: true
  -> AggregationTemporality: Cumulative
NumberDataPoints #0
  Value: 415.000000
...
Metric #4
Descriptor:
  -> Name: current_humidity_percentage
  -> Description: Current Humidity
  -> DataType: Gauge
NumberDataPoints #0
  Timestamp: 2025-09-30T00:24:49.412Z
  Value: 0.157135
```

This confirms successful scraping: the Prometheus receiver pulled the `/metrics` endpoint from `python-app:8000` and the `debug` exporter displayed the metrics.

## Common pitfalls and fixes

| Symptom                            | Likely cause                                                  | Fix / Tip                                                              |
| ---------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------- |
| No metrics in pipeline             | Receiver defined but not added to `service.pipelines.metrics` | Add the receiver name to `service.pipelines.metrics.receivers`         |
| OTLP HTTP exporter fails to send   | Incorrect endpoint path                                       | Use full path: `http://host:4318/v1/metrics`                           |
| Prometheus static target not found | Missing port or wrong address                                 | Ensure `static_configs.targets` includes port, e.g., `python-app:8000` |
| Scraped metrics missing labels     | Prometheus exposition missing metadata                        | Confirm exporter/library sets desired labels (e.g., `service.name`)    |

Tips:

* You can enable both OTLP push and Prometheus scraping simultaneously by listing both `otlp` and `prometheus` in the `metrics` pipeline.
* When troubleshooting, the `debug` exporter is very helpful to observe exactly what the Collector receives.

## Summary

* Push-based (OTLP): App pushes metrics to the Collector using an OTLP exporter (often HTTP to `/v1/metrics`). See the OTLP protocol docs: [https://opentelemetry.io/docs/reference/specification/protocol/otlp/](https://opentelemetry.io/docs/reference/specification/protocol/otlp/)
* Pull-based (Prometheus): App exposes a `/metrics` endpoint and the Collector scrapes it via the Prometheus receiver. See Prometheus: [https://prometheus.io/](https://prometheus.io/)
* The Collector can accept both approaches simultaneously—configure both receivers and add them to `service.pipelines.metrics.receivers`.

With the configuration and code snippets above you can validate both ingestion models and observe metrics via the Collector's `debug` exporter.

## Links and References

* OpenTelemetry Collector: [https://opentelemetry.io/docs/collector/](https://opentelemetry.io/docs/collector/)
* OTLP protocol: [https://opentelemetry.io/docs/reference/specification/protocol/otlp/](https://opentelemetry.io/docs/reference/specification/protocol/otlp/)
* Prometheus docs: [https://prometheus.io/](https://prometheus.io/)
* Python prometheus\_client: [https://github.com/prometheus/client\_python](https://github.com/prometheus/client_python)
* OpenTelemetry Python metrics: [https://opentelemetry.io/docs/instrumentation/python/metrics/](https://opentelemetry.io/docs/instrumentation/python/metrics/)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/prep-course-opentelemetry-certified-associate-certification-otca/module/f6507634-836f-4fe9-b29d-047d84bfcce7/lesson/54ccbea1-d07d-4179-acf5-34d70e93fa5c" />
</CardGroup>
