feat(v2/metrics): support OpenMetrics exposition (and exemplars) on Metrics.Handler()

Summary

labkit/v2/metrics.Metrics.Handler() builds its HTTP handler as:

promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{Registry: m.registry})

with EnableOpenMetrics unset. As a result the endpoint never negotiates the OpenMetrics exposition format: even when a scraper sends Accept: application/openmetrics-text, promhttp falls back to the classic Prometheus text format (text/plain; version=0.0.4) and drops exemplars recorded via ObserveWithExemplar.

The v1 labkit/monitoring package exposed OpenMetrics at /metrics; the v2 metrics package is a regression in this respect.

Why it matters

Exemplars (a histogram sample tagged with the active trace_id) are how an operator jumps from an aggregate latency spike to a specific distributed trace. client_golang only serializes them when the handler is built with promhttp.HandlerOpts{EnableOpenMetrics: true}. Because metrics.Config/Handler() expose no such option, a service that records exemplars cannot surface them through LabKit's handler — it must hand-roll promhttp.HandlerFor(m.Gatherer(), promhttp.HandlerOpts{EnableOpenMetrics: true}), bypassing Handler() / MountOn() and the httpserver probe-listener auto-mount (which hardcodes cfg.Metrics.Handler() at GET /-/metrics, with no override hook).

Concrete downstream case: the GitLab Artifact Registry metrics framework (gitlab-org/ops/artifact-registry#56 (closed)) records exemplars on its HTTP and DB latency histograms but cannot expose them via the probe /-/metrics, so it currently has to bypass Handler() entirely.

Proposed change

Add an EnableOpenMetrics bool to metrics.Config and plumb it into Handler():

type Config struct {
    // ... existing fields ...

    // EnableOpenMetrics serves the OpenMetrics exposition format when the
    // scraper negotiates it (Accept: application/openmetrics-text). This is
    // required for exemplar serialization. Defaults to false (classic
    // Prometheus text exposition only).
    EnableOpenMetrics bool
}

func (m *Metrics) Handler() http.Handler {
    m.handlerOnce.Do(func() {
        m.handler = promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{
            Registry:          m.registry,
            EnableOpenMetrics: m.enableOpenMetrics,
        })
    })
    return m.handler
}

Default false preserves current behavior exactly; opting in is a single config field, and Handler() / MountOn() / the httpserver probe-listener auto-mount remain usable for services that record exemplars.

References