Prometheus metrics exporter

Prometheus exporter implementing metrics.Exporter (an alias for monitoring_domain.MetricsExporter). Bridges OpenTelemetry metrics to the Prometheus exposition format and serves them on the health-probe /metrics endpoint.

Overview

The exporter wraps go.opentelemetry.io/otel/exporters/prometheus. Wiring is one option with no config. Construct the exporter and pass it to piko.WithMetricsExporter. Every metric recorded through OTEL instrumentation appears at /metrics with no registry plumbing or handler mounting on your side. That covers Piko's own counters and your service's custom OTEL instruments. The process-style metrics in the scrape come from the monitoring service watchdog, which declares watchdog.* OTEL instruments through the global otel.Meter in monitoring_domain/otel.go and records values such as watchdog.rss_bytes, watchdog.fd_count, and watchdog.scheduler_latency_p99_nanos. These share the global MeterProvider set in driver_handlers/otel.go, the same provider the exporter reader attaches to, so they reach the scrape through the same path. The monitoring service MetricsReader() returns a separate ManualReader that consumes metrics into a telemetry store for the gRPC transport and does not feed the Prometheus reader. The exporter itself registers no Go runtime or process collectors, and Piko registers no standard OTEL runtime or process instrumentation.

The exporter satisfies the metrics.Exporter port. A compile-time assertion guards the contract. Any backend that implements the port drops in here, and a mock substitutes for it in tests.

New() returns the metrics.Exporter interface and an error. MustNew() panics instead of returning the error and helps at startup where a failure should crash the process. The exporter has no Config struct. The surface stays minimal because Prometheus owns the scrape configuration on the other side. Piko's health probe handles endpoint addressing (default port 9090, path configurable through healthProbe.metricsPath in piko.yaml).

The exporter applies WithoutTargetInfo, so it does not emit the target_info metric. That matches Prometheus practice when service identity already lives in scrape job labels. The handler enables OpenMetrics. A scraper that negotiates the OpenMetrics content type receives exemplars and the full OpenMetrics output. A classic-exposition scraper receives the text format. The exporter is safe for concurrent use.

Requirements

  • Prometheus (or any Prometheus-compatible scraper, for example Grafana Agent, VictoriaMetrics) configured to scrape Piko's health-probe port.

Configuration

import (
    prometheus "piko.sh/piko/wdk/metrics/metrics_exporter_prometheus"
)

exporter, err := prometheus.New()
if err != nil {
    return err
}

Or the panic-on-failure variant:

exporter := prometheus.MustNew()

Bootstrap

ssr := piko.New(
    piko.WithMetricsExporter(exporter),
)

WithMetricsExporter enables the health-probe metrics endpoint and registers the exporter's reader with the OTEL MeterProvider. OTEL-recorded metrics surface at /metrics on the probe port. The endpoint rides the same health-probe server that already serves /live and /ready, so metrics reuse the existing listener instead of starting a new one.

The health-probe server binds to 127.0.0.1 by default. An external scraper cannot reach a loopback address. Set healthProbe.bindAddress to 0.0.0.0 in piko.yaml, or call piko.WithHealthBindAddress("0.0.0.0"), to expose the endpoint. The metrics path defaults to /metrics. Piko enables the endpoint by default. Disable it with healthProbe.metricsEnabled: false or piko.WithHealthMetricsEnabled(false) to keep /live and /ready without exposing metrics.

See also

Sibling exporters / consumers:

Monitoring transport (alternative consumer of OTEL data):

Framework docs:

External: