Skip to content

distributedtracing: export Prometheus metrics via OTLP#8450

Merged
sspaink merged 7 commits intoopen-policy-agent:mainfrom
Munken:metrics-via-otlp
Apr 8, 2026
Merged

distributedtracing: export Prometheus metrics via OTLP#8450
sspaink merged 7 commits intoopen-policy-agent:mainfrom
Munken:metrics-via-otlp

Conversation

@Munken
Copy link
Copy Markdown
Contributor

@Munken Munken commented Mar 27, 2026

Why the changes in this PR are needed?

Users running OPA as a sidecar alongside an OpenTelemetry collector currently need a
dedicated OTel sidecar just to scrape OPA's Prometheus /metrics endpoint. This PR
lets OPA push its Prometheus metrics directly to an OTel collector via OTLP, eliminating
that extra sidecar and simplifying deployment (see #7591).

What are the changes in this PR?

  • Use the OTel Prometheus bridge
    to read from OPA's existing prometheus.Registry and export metrics through a
    periodic OTLP exporter (gRPC or HTTP), reusing the same distributed_tracing
    address and TLS configuration as traces.
  • Two new config fields under distributed_tracing:
    • metrics (bool, default false) — opt-in to push Prometheus metrics via OTLP.
    • metrics_export_interval_ms (int, default 60000) — interval between exports.
  • Expose Gatherer() on the Prometheus provider so the registry can be passed to the bridge.
  • Graceful shutdown of the MeterProvider on server stop.
  • Unit tests for config parsing, defaults, and validation.
  • E2E test with a mock OTLP HTTP collector that verifies http_request_duration_seconds arrives.
  • Documentation updates in configuration.md.

Notes to assist PR review:

  • The feature is fully opt-in; metrics: false by default means zero impact on existing deployments.
  • It is not possible to enable metrics without also enabling tracing. This is because type is what enables tracing.
  • The implementation follows the "good enough" approach discussed in Export metrics via OTLP #7591 — bridging
    existing Prometheus metrics rather than rewriting them with the OTel SDK.
  • metrics_export_interval_ms is validated to be a positive value.
  • All OTel dependencies are aligned at v1.40.0 to match the existing trace exporter versions.

Further comments:

@Munken
Copy link
Copy Markdown
Contributor Author

Munken commented Mar 27, 2026

Also tested manually tested with

distributed_tracing:
    type: http
    address: localhost:4318
    encryption: "off"
    metrics: true
    metrics_export_interval_ms: 5000

Local otel stack:

docker-compose:

services:
  # Jaeger for storing traces
  jaeger:
    image: jaegertracing/jaeger:2.10.0
    ports:
      - '16686:16686' # Web UI
      - '4317' # OTLP gRPC
      - '4318' # OTLP HTTP

  # Prometheus for storing metrics
  prometheus:
    image: prom/prometheus:v3.5.0
    ports:
      - '9090:9090' # Web UI
      - '4318' # OTLP HTTP
    command:
      - --web.enable-otlp-receiver
      # Mirror these flags from the Dockerfile, because `command` overwrites the default flags.
      # https://github.com/prometheus/prometheus/blob/5b5fee08af4c73230b2dae35964816f7b3c29351/Dockerfile#L23-L24
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus

  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.135.0
    volumes:
      - ./otel-collector-config.yml:/etc/otelcol-contrib/config.yaml
    ports:
      - '4318:4318' # OTLP HTTP ( exposed to the host )
      - '4317:4317' # OTLP gRPC ( exposed to the host )
    depends_on:
      - jaeger
      - prometheu

otel-config

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true
  otlphttp/prometheus:
    endpoint: http://prometheus:9090/api/v1/otlp
  debug:
    verbosity: detailed
  # verbosity: normal

processors:
  batch:

extensions:
  health_check:
  pprof:
  zpages:

service:
  extensions: [pprof, zpages, health_check]
  pipelines:
    traces:
      receivers: [otlp]
      exporters:
        - otlp/jaeger
        # Enable debug exporter to see traces in the logs
        #- debug
      processors: [batch]

    metrics:
      receivers:
        - otlp 
      processors: [batch]
      exporters:
        - otlphttp/prometheus
        # Enable debug exporter to see metrics in the logs
        - debug
image

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 27, 2026

Deploy Preview for openpolicyagent ready!

Name Link
🔨 Latest commit 9b771d6
🔍 Latest deploy log https://app.netlify.com/projects/openpolicyagent/deploys/69d65f5e3125e50008395ca7
😎 Deploy Preview https://deploy-preview-8450--openpolicyagent.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Munken Munken force-pushed the metrics-via-otlp branch 3 times, most recently from c33a3f6 to f29dc3c Compare March 30, 2026 18:43
service_name: opa
sample_percentage: 50
encryption: "off"
metrics: false
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of changing this to something like metrics_export: "OTLP"? Might be other protocols in the future we could support and using a boolean would force us to add a new config option. Would also be nice if it was more explicit that this config option enables OTLP exporting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case I would suggest using metrics_export: otlp/grpc or metrics_export: otlp/http. That way it can be enabled without also enabling tracing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a good idea to me 👍

Copy link
Copy Markdown
Contributor Author

@Munken Munken Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found a downside, the type is coupled to the address via the port eg 4317 for grpc and 4318 for http.

So if we want full flexibility to set these independently that it should probably be moved out of the tracing config section.

Alternatively, one could add a metrics_adress but then we are almost there.

Copy link
Copy Markdown
Contributor Author

@Munken Munken Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sspaink I've pushed a new commit that seperates the metrics export from the tracing.
There is some overlap in config though, so I've seperated the shared logic also.

Main downside with this approach is that one has to potentially config TLS twice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having to configure TLS twice isn't great, but I do think this new approach will make it easier to make changes in the future that makes it worth it. Thanks for updating it!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could introduce a common section that both inherent from. Would also move the service name there

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would still need to support the TLS in the tracing section for backwards compatibility though. I don't think it is required for this change so could be in a separate PR. If you are up for the challenge a common section could be helpful.

@Munken Munken force-pushed the metrics-via-otlp branch from 6e1d2f7 to b84a34b Compare April 5, 2026 20:08
Add support for pushing OPA's existing Prometheus metrics to an
OpenTelemetry collector via OTLP, eliminating the need for a dedicated
scraper sidecar. Uses the OTel Prometheus bridge to read from OPA's
prometheus.Registry and export through an OTLP metric exporter (gRPC
or HTTP), reusing the same address and TLS configuration as traces.

New config fields: distributed_tracing.metrics (bool, default false)
and distributed_tracing.metrics_export_interval_ms (int, default 60000).

Fixes open-policy-agent#7591

Signed-off-by: Michael Munch <mm.munk@gmail.com>
@Munken Munken force-pushed the metrics-via-otlp branch from b84a34b to 4b46098 Compare April 5, 2026 20:24
Extract metrics export from distributed_tracing into its own
metrics_export config section with independent type (otlp/grpc,
otlp/http), address, and TLS settings. This allows exporting
Prometheus metrics via OTLP without enabling tracing, and to a
different endpoint than traces.

- Extract shared TLS helpers into internal/tlsutil
- Add MetricsExport field to top-level Config
- Create internal/metricsexport package with Init, config parsing
- Remove metrics fields from distributedtracing
- Update runtime to call metricsexport.Init separately
- Move e2e tests to v1/test/e2e/metricsexport
- Add Metrics Export section to configuration docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Michael Munch <mm.munk@gmail.com>
@Munken Munken force-pushed the metrics-via-otlp branch from 4b46098 to 5858ec6 Compare April 5, 2026 20:33
Signed-off-by: Michael Munch <mm.munk@gmail.com>
Munken and others added 2 commits April 7, 2026 19:11
Modules like containerd, go-sqlbuilder, OpenTelemetry, and golang.org/x/*
were at older versions than main after a rebase. Upgrade them to match or
exceed main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Michael Munch <mm.munk@gmail.com>
Copy link
Copy Markdown
Member

@sspaink sspaink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for working on this. Just a nit regarding the copyright headers.

Co-authored-by: Sebastian Spaink <sebastianspaink@gmail.com>
Signed-off-by: Michael Munch <mm.munk@gmail.com>
@Munken
Copy link
Copy Markdown
Contributor Author

Munken commented Apr 7, 2026

LGTM, thanks for working on this. Just a nit regarding the copyright headers.

Thanks!

I applied the suggestion.
Merge at your discretion.

@sspaink sspaink enabled auto-merge (squash) April 8, 2026 14:00
@sspaink sspaink merged commit 12b7290 into open-policy-agent:main Apr 8, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants