> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/open-telemetry/opentelemetry-rust/llms.txt
> Use this file to discover all available pages before exploring further.

# Prometheus Integration

> Export metrics to Prometheus using OTLP - the recommended approach for Prometheus integration

<Warning>
  The dedicated `opentelemetry-prometheus` crate has been **discontinued** as of version 0.29. It depends on an unmaintained `protobuf` crate with known security vulnerabilities.
</Warning>

## Recommended Approach: OTLP Exporter

For Prometheus integration, use the **OTLP exporter** instead. Prometheus natively supports the OTLP protocol, providing a more stable, secure, and actively maintained solution.

### Why OTLP?

* **Active maintenance**: Part of the OpenTelemetry specification
* **Security**: No dependency on unmaintained libraries
* **Native support**: Prometheus 2.47+ has built-in OTLP receiver
* **Consistency**: Same exporter works with multiple backends
* **Future-proof**: Official OpenTelemetry standard

## Quick Start

### 1. Run Prometheus with OTLP Support

Create a minimal `prometheus.yml` configuration:

```yaml theme={null}
global:
  scrape_interval: 15s
```

Run Prometheus with OTLP receiver enabled:

```bash theme={null}
docker run -p 9090:9090 \
  -v ./prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --web.enable-otlp-receiver
```

Prometheus is now ready to accept OTLP metrics at: `http://localhost:9090/api/v1/otlp/v1/metrics`

### 2. Configure Your Application

Add dependencies to `Cargo.toml`:

```toml theme={null}
[dependencies]
opentelemetry = "0.31"
opentelemetry_sdk = "0.31"
opentelemetry-otlp = { version = "0.31", features = ["http-proto", "metrics"] }
```

### 3. Export Metrics to Prometheus

```rust theme={null}
use opentelemetry::global;
use opentelemetry::metrics::Meter;
use opentelemetry::KeyValue;
use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig};
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
    // Create OTLP metric exporter configured for Prometheus
    let exporter = MetricExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary)
        .with_endpoint("http://localhost:9090/api/v1/otlp/v1/metrics")
        .build()?;

    // Create meter provider
    let meter_provider = SdkMeterProvider::builder()
        .with_periodic_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("my-service")
                .build()
        )
        .build();

    global::set_meter_provider(meter_provider.clone());

    // Get a meter
    let meter = global::meter("my-meter");

    // Create and use metrics
    let counter = meter
        .u64_counter("http_requests_total")
        .with_description("Total HTTP requests")
        .with_unit("requests")
        .build();

    counter.add(1, &[KeyValue::new("method", "GET")]);
    counter.add(1, &[KeyValue::new("method", "POST")]);

    // Shutdown to flush metrics
    meter_provider.shutdown()?;

    Ok(())
}
```

### 4. Query Metrics in Prometheus

Open the Prometheus web UI at `http://localhost:9090` and query:

```promql theme={null}
http_requests_total
```

You should see your metrics with labels.

## Complete Example with Multiple Metric Types

```rust theme={null}
use opentelemetry::global;
use opentelemetry::KeyValue;
use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig};
use opentelemetry_sdk::metrics::SdkMeterProvider;
use opentelemetry_sdk::Resource;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
    // Initialize meter provider
    let exporter = MetricExporter::builder()
        .with_http()
        .with_protocol(Protocol::HttpBinary)
        .with_endpoint("http://localhost:9090/api/v1/otlp/v1/metrics")
        .build()?;

    let meter_provider = SdkMeterProvider::builder()
        .with_periodic_exporter(exporter)
        .with_resource(
            Resource::builder()
                .with_service_name("metrics-demo")
                .build()
        )
        .build();

    global::set_meter_provider(meter_provider.clone());

    let meter = global::meter("example-meter");

    // Counter - monotonically increasing value
    let request_counter = meter
        .u64_counter("http_requests_total")
        .with_description("Total number of HTTP requests")
        .with_unit("requests")
        .build();

    // Histogram - distribution of values
    let request_duration = meter
        .f64_histogram("http_request_duration_seconds")
        .with_description("HTTP request duration")
        .with_unit("s")
        .build();

    // Observable Gauge - current value at collection time
    let active_connections = meter
        .u64_observable_gauge("active_connections")
        .with_description("Number of active connections")
        .with_unit("connections")
        .build();

    // Simulate application metrics
    for i in 0..10 {
        // Record requests
        request_counter.add(1, &[
            KeyValue::new("method", "GET"),
            KeyValue::new("endpoint", "/api/users"),
        ]);

        // Record request duration
        request_duration.record(0.150 + (i as f64 * 0.01), &[
            KeyValue::new("method", "GET"),
            KeyValue::new("status", "200"),
        ]);

        tokio::time::sleep(Duration::from_secs(1)).await;
    }

    // Shutdown to export final metrics
    meter_provider.shutdown()?;

    Ok(())
}
```

## Migration from Old Prometheus Exporter

If you're migrating from the discontinued `opentelemetry-prometheus` crate:

### Before (Old Approach)

```rust theme={null}
use opentelemetry_prometheus::exporter;
use prometheus::{Registry, TextEncoder};

let registry = Registry::new();
let exporter = exporter()
    .with_registry(registry.clone())
    .build()?;

let provider = SdkMeterProvider::builder()
    .with_reader(exporter)
    .build();
```

### After (New Approach with OTLP)

```rust theme={null}
use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig};

let exporter = MetricExporter::builder()
    .with_http()
    .with_protocol(Protocol::HttpBinary)
    .with_endpoint("http://localhost:9090/api/v1/otlp/v1/metrics")
    .build()?;

let provider = SdkMeterProvider::builder()
    .with_periodic_exporter(exporter)
    .build();
```

## Metric Types Mapping

OpenTelemetry metrics map to Prometheus as follows:

| OpenTelemetry   | Prometheus | Use Case                                           |
| --------------- | ---------- | -------------------------------------------------- |
| Counter         | Counter    | Monotonically increasing values (requests, errors) |
| Histogram       | Histogram  | Distribution of values (latency, response sizes)   |
| ObservableGauge | Gauge      | Point-in-time values (CPU usage, memory)           |
| UpDownCounter   | Gauge      | Values that go up and down (queue length)          |

## Naming Conventions

Follow Prometheus naming best practices:

```rust theme={null}
// Counter - use _total suffix
let counter = meter.u64_counter("http_requests_total").build();

// Histogram - use base unit suffix
let histogram = meter.f64_histogram("http_request_duration_seconds").build();

// Gauge - descriptive name
let gauge = meter.u64_observable_gauge("active_connections").build();
```

**Naming rules:**

* Use snake\_case
* Include unit suffix (`_seconds`, `_bytes`, `_total`)
* Be descriptive and specific
* Avoid redundant prefixes

## Configuration

### Custom Export Interval

By default, metrics are exported every 60 seconds. Customize this:

```rust theme={null}
use opentelemetry_sdk::metrics::PeriodicReaderBuilder;
use std::time::Duration;

let reader = PeriodicReaderBuilder::new(exporter)
    .with_interval(Duration::from_secs(30))  // Export every 30 seconds
    .build();

let provider = SdkMeterProvider::builder()
    .with_reader(reader)
    .build();
```

### Resource Attributes

Add resource attributes that appear as labels:

```rust theme={null}
use opentelemetry::KeyValue;
use opentelemetry_sdk::Resource;

let resource = Resource::builder()
    .with_service_name("my-service")
    .with_service_version("1.0.0")
    .with_attributes([
        KeyValue::new("environment", "production"),
        KeyValue::new("region", "us-west-2"),
    ])
    .build();

let provider = SdkMeterProvider::builder()
    .with_periodic_exporter(exporter)
    .with_resource(resource)
    .build();
```

## Production Deployment

### Using OpenTelemetry Collector

For production, use the OpenTelemetry Collector as an intermediary:

```yaml theme={null}
# collector-config.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

exporters:
  prometheus:
    endpoint: "0.0.0.0:8889"

service:
  pipelines:
    metrics:
      receivers: [otlp]
      exporters: [prometheus]
```

Run the collector:

```bash theme={null}
docker run -p 4318:4318 -p 8889:8889 \
  -v ./collector-config.yaml:/etc/otel-collector-config.yaml \
  otel/opentelemetry-collector:latest \
  --config=/etc/otel-collector-config.yaml
```

Configure Prometheus to scrape the collector:

```yaml theme={null}
# prometheus.yml
scrape_configs:
  - job_name: 'otel-collector'
    static_configs:
      - targets: ['localhost:8889']
```

Update your application to send to the collector:

```rust theme={null}
let exporter = MetricExporter::builder()
    .with_http()
    .with_endpoint("http://localhost:4318")  // Collector endpoint
    .build()?;
```

**Benefits:**

* Buffering and retry logic
* Multiple backend support
* Metric transformation and filtering
* Service discovery integration

## Troubleshooting

### Metrics Not Appearing

1. **Verify Prometheus OTLP receiver is enabled:**
   ```bash theme={null}
   curl http://localhost:9090/api/v1/status/config | grep otlp
   ```

2. **Check endpoint URL:**
   * Must include full path: `/api/v1/otlp/v1/metrics`
   * Default: `http://localhost:9090/api/v1/otlp/v1/metrics`

3. **Ensure shutdown is called:**
   ```rust theme={null}
   meter_provider.shutdown()?;  // Flushes pending metrics
   ```

### Missing Labels

Ensure attributes are added when recording metrics:

```rust theme={null}
counter.add(1, &[KeyValue::new("label_name", "label_value")]);
```

### Version Compatibility

Prometheus OTLP support requires:

* Prometheus 2.47.0 or later
* OpenTelemetry Rust SDK 0.20.0 or later

## Legacy Prometheus Exporter (Deprecated)

<Warning>
  This section is for reference only. The prometheus exporter is deprecated and should not be used in new projects.
</Warning>

The old `opentelemetry-prometheus` crate provided a pull-based exporter that exposed metrics via an HTTP endpoint. This approach is no longer recommended.

For pull-based metrics, use the OpenTelemetry Collector with the Prometheus exporter as shown in the [Production Deployment](#production-deployment) section.

## Next Steps

<CardGroup cols={2}>
  <Card title="OTLP Exporter" icon="arrow-right" href="/exporters/otlp">
    Learn more about OTLP configuration
  </Card>

  <Card title="Metrics Guide" icon="chart-line" href="/metrics/overview">
    Comprehensive guide to metrics in Rust
  </Card>
</CardGroup>
