metrics

package
v2.44.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 17 Imported by: 0

README

metrics

Package metrics provides an isolated Prometheus registry that integrates with the LabKit v2 component lifecycle. There is no shared global state — each Metrics instance owns its own prometheus.Registry, so components and tests never interfere with each other.

Quick start

m, err := metrics.New()
if err != nil {
    log.Fatal(err)
}

a.Register(m) // Start registers go_ and process_ collectors; Shutdown is a no-op

// Mount alongside the existing health endpoints.
srv.Router().Get("/-/metrics", m.Handler())

Defining metrics

Use BuildName to prefix metric names with the configured namespace (default "gitlab"), then register with MustRegister:

requestsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
    Name: m.BuildName("http", "requests_total"),
    Help: "Total number of HTTP requests partitioned by feature category and status.",
}, []string{metrics.LabelFeatureCategory, metrics.LabelStatus})

m.MustRegister(requestsTotal)

// Increment on every request:
requestsTotal.WithLabelValues("code_review", "2xx").Inc()

BuildName(subsystem, name) follows the Prometheus convention of joining parts with underscores, omitting empty segments:

Call Result
m.BuildName("http", "requests_total") gitlab_http_requests_total
m.BuildName("worker", "jobs_total") gitlab_worker_jobs_total
m.BuildName("", "up") gitlab_up

Standard labels

Use the label name constants to keep dashboards and alert rules consistent across services:

Constant Value Use for
LabelComponent "component" Logical sub-system, e.g. "api", "sidekiq"
LabelFeatureCategory "feature_category" Handbook feature category for SLO ownership
LabelEndpointID "endpoint_id" Low-cardinality endpoint identifier, e.g. "GET /api/v4/projects/{id}"
LabelStatus "status" HTTP response status class, e.g. "2xx" or "5xx"

Standard bucket sets

DurationBuckets provides SLO-aligned histogram boundaries for HTTP and RPC latency (100ms to 60s). The five boundaries include 1 (satisfied threshold) and 10 (tolerated threshold) to match the Workhorse and Rails SLI thresholds used across GitLab's metrics catalog:

prometheus.NewHistogramVec(prometheus.HistogramOpts{
    Name:    m.BuildName("http", "request_duration_seconds"),
    Buckets: metrics.DurationBuckets,
}, []string{metrics.LabelEndpointID, metrics.LabelStatus})

Memory metrics

Start registers a cgroup memory collector, so every service reports the memory limit the kernel enforces on it alongside its current usage.

gitlab_memory_usage_bytes / gitlab_memory_limit_bytes{source="cgroup_max"}
Metric Type Labels Meaning
gitlab_memory_limit_bytes gauge source Memory limit applying to this process
gitlab_memory_usage_bytes gauge Working set, matching container_memory_working_set_bytes
gitlab_memory_anon_bytes gauge Anonymous memory: the part the GC can act on
gitlab_memory_events_total counter event max and high event counts

The source label distinguishes three different quantities:

source Meaning
cgroup_max The hard limit. Exceeding it gets the process OOM-killed.
cgroup_high The soft throttling threshold. Exceeding it causes reclaim stalls, which show up as latency with no OOM event to explain them.
system MemTotal, published only when no hard cgroup limit applies. A cgroup with memory.max = max but memory.high set reports both system and cgroup_high.

Always select on source. cgroup_high is routinely set below cgroup_max, so min(gitlab_memory_limit_bytes) silently switches to the soft limit, and sum(gitlab_memory_limit_bytes) double-counts.

Absent means unknown, never zero

When a memory value could not be determined, it is omitted rather than published as zero, so absent() alerting works and the ratio never divides by zero.

What the numbers refer to

The limit is the minimum over the process's cgroup and all its ancestors: a container's own cgroup routinely reads max while the pod cgroup carries the limit the kernel enforces. Usage, anonymous memory and events are read from the process's own cgroup, so in a multi-container pod the limit may be the pod's while usage is one container's, and utilisation will read low.

If the process's own cgroup carries no memory interface files, because its parent never enabled +memory in cgroup.subtree_control, everything is read from the nearest ancestor that does. Usage there may include sibling cgroups, but the alternative is falling back to MemTotal and reporting the machine's memory as the ceiling while an ancestor enforces something far smaller.

Note that applications that place child processes in their own cgroups — Gitaly's spawned git processes, for example — are not fully described by these metrics. LabKit reports the application's own cgroup; per-child accounting stays application-specific.

Every value is read on each scrape. Only the resolved cgroup path is cached, for 30 seconds, since resolution parses mountinfo but its answer changes only if the process is migrated between cgroups. So usage tracks the current scrape, and a limit rewritten by in-place pod vertical scaling (KEP-1287) or VPA is picked up immediately rather than reported stale.

cgroup v1 and non-Linux
cgroup v2 cgroup v1
source="cgroup_high" Published when memory.high is set Never — v1 has no soft threshold
event="high" Always present Never present
gitlab_memory_anon_bytes memory.stat anon total_rss, which also includes swap cache

The absence of the v1 series is stable rather than flapping, so rate() sees no counter resets — but a mixed v1/v2 fleet has a ragged series set.

Nothing is exported on macOS or Windows.

Set DisableMemoryCollector to opt out.

HTTP service example

The pattern below wires Metrics into an httpserver.Server. Metrics are defined once at construction time and incremented inside the handler closure:

func main() {
    ctx := context.Background()

    a, _ := app.New(ctx)

    m, _ := metrics.New()
    a.Register(m)

    // Define metrics before Start is called.
    requestsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
        Name: m.BuildName("http", "requests_total"),
        Help: "Total HTTP requests partitioned by endpoint and status.",
    }, []string{metrics.LabelEndpointID, metrics.LabelStatus})

    requestDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{
        Name:    m.BuildName("http", "request_duration_seconds"),
        Help:    "HTTP request latency.",
        Buckets: metrics.DurationBuckets,
    }, []string{metrics.LabelEndpointID})

    m.MustRegister(requestsTotal, requestDuration)

    srv := httpserver.NewWithConfig(&httpserver.Config{
        Addr:   ":8080",
        Tracer: a.Tracer(),
    })

    srv.Router().Get("/api/projects/{id}", func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        // ... handler logic ...

        requestsTotal.WithLabelValues("GET /api/projects/{id}", "2xx").Inc()
        requestDuration.WithLabelValues("GET /api/projects/{id}").Observe(time.Since(start).Seconds())

        w.WriteHeader(http.StatusOK)
    })

    // Expose metrics at /-/metrics alongside /-/liveness and /-/readiness.
    srv.Router().Get("/-/metrics", m.Handler())

    a.Register(srv)
    a.Run(ctx)
}

Note: for high-traffic services consider using prometheus.NewHistogramVec with NativeHistogramBucketFactor instead of fixed Buckets to reduce cardinality and scrape payload size.

Configuration

m, err := metrics.NewWithConfig(&metrics.Config{
    Name:              "primary-metrics", // component name in logs   (default: "metrics")
    Namespace:         "myservice",       // metric name prefix       (default: "gitlab")
    EnableOpenMetrics: true,              // serve OpenMetrics/exemplars (default: false)

    DisableMemoryCollector: true,         // skip gitlab_memory_*      (default: false)
})

When writing tests, pass a fresh prometheus.Registry to isolate the metric state completely (see Testing below).

Sharing a registry

Pass an existing registry via Registry to serve LabKit metrics from an endpoint another library already exposes. If that library also registers Go runtime and process collectors, set DisableRuntimeCollectors so Start leaves them alone.

OpenMetrics and exemplars

By default Handler serves only the classic Prometheus text format. Set EnableOpenMetrics to also serve the OpenMetrics format to scrapers that negotiate it (Accept: application/openmetrics-text). This is required for exemplars: a histogram sample tagged with the active trace_id lets an operator jump from a latency spike straight to the distributed trace behind it. Scrapers that do not request OpenMetrics still receive the classic text format.

m, err := metrics.NewWithConfig(&metrics.Config{EnableOpenMetrics: true})
if err != nil {
    log.Fatal(err)
}

latency := prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    m.BuildName("http", "request_duration_seconds"),
    Buckets: metrics.DurationBuckets,
})
m.MustRegister(latency)

// Attach the current trace_id to the observation as an exemplar.
latency.(prometheus.ExemplarObserver).ObserveWithExemplar(
    time.Since(start).Seconds(),
    prometheus.Labels{"trace_id": traceID},
)

Injecting into sub-components

Pass only the prometheus.Registerer interface to components that should register their own collectors but not control the full Metrics instance. This limits the API surface and makes dependencies explicit:

type Worker struct {
    jobsTotal prometheus.Counter
}

func NewWorker(reg prometheus.Registerer, m *metrics.Metrics) (*Worker, error) {
    c := prometheus.NewCounter(prometheus.CounterOpts{
        Name: m.BuildName("worker", "jobs_total"),
        Help: "Total jobs processed.",
    })
    if err := reg.Register(c); err != nil {
        return nil, err
    }
    return &Worker{jobsTotal: c}, nil
}

// Wire up:
worker, err := NewWorker(m.Registerer(), m)
a.Register(worker)

Testing

Use metricstest.New for an isolated Metrics backed by a fresh registry. The component is not started — Go runtime and process collectors are absent, keeping gathered output minimal and assertions deterministic:

import "gitlab.com/gitlab-org/labkit/v2/testing/metricstest"

func TestWorker_countsJobs(t *testing.T) {
    m := metricstest.New(t)

    worker, err := NewWorker(m.Registerer(), m)
    require.NoError(t, err)
    require.NoError(t, worker.ProcessJob(ctx))

    families := metricstest.Gather(t, m)

    require.Contains(t, families, "gitlab_worker_jobs_total")
    assert.Equal(t, 1.0, families["gitlab_worker_jobs_total"].Metric[0].Counter.GetValue())
}

metricstest.Gather returns a map[string]*dto.MetricFamily keyed by fully-qualified metric name, so you can assert on counters, gauges, and histogram observations without a running Prometheus server.

Documentation

Overview

Package metrics provides an isolated Prometheus registry that integrates with the LabKit v2 component lifecycle.

All metrics are registered against a private prometheus.Registry — there is no shared global state between components or tests. The Metrics type implements [app.Component]: Metrics.Start registers the standard Go runtime and process collectors that all GitLab services are expected to export, unless Config.DisableRuntimeCollectors is set; Metrics.Handler returns an HTTP handler ready to mount at /-/metrics.

Quick start

m, err := metrics.New()
if err != nil {
    log.Fatal(err)
}
a.Register(m)

srv.Router().Get("/-/metrics", m.Handler())

Defining metrics

Use Metrics.BuildName to prefix metric names with the configured namespace (default "gitlab"), then register the collector with Metrics.MustRegister:

requestsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
    Name: m.BuildName("http", "requests_total"),
    Help: "Total number of HTTP requests.",
}, []string{metrics.LabelFeatureCategory, metrics.LabelStatus})

m.MustRegister(requestsTotal)

// Increment on every request:
requestsTotal.WithLabelValues("code_review", "2xx").Inc()

Standard labels

Use the label name constants to avoid typos and keep dashboards consistent across services:

prometheus.Labels{
    metrics.LabelComponent:       "api",
    metrics.LabelFeatureCategory: "merge_requests",
}

Standard bucket sets

DurationBuckets provides SLO-aligned histogram boundaries for HTTP and RPC latency (seconds). The five boundaries include 1 (satisfied threshold) and 10 (tolerated threshold) to match the Workhorse and Rails SLI thresholds.

latency := prometheus.NewHistogramVec(prometheus.HistogramOpts{
    Name:    m.BuildName("http", "request_duration_seconds"),
    Help:    "HTTP request latency.",
    Buckets: metrics.DurationBuckets,
}, []string{metrics.LabelEndpointID, metrics.LabelStatus})

Memory metrics

Metrics.Start also registers a cgroup memory collector, so every service reports the memory limit the kernel enforces on it alongside its current usage. Utilisation is then computable from application-scraped series alone, with no join against Kube-State-Metrics or cAdvisor and no relabelling:

gitlab_memory_usage_bytes / gitlab_memory_limit_bytes{source="cgroup_max"}

The exported metrics are:

  • gitlab_memory_limit_bytes{source}, where source is "cgroup_max" (the hard limit), "cgroup_high" (the soft throttling threshold) or "system" (MemTotal, when no hard cgroup limit applies). Always select on source: the values are different quantities and must never be summed.
  • gitlab_memory_usage_bytes, the working set, computed the same way as cAdvisor's container_memory_working_set_bytes so the two agree.
  • gitlab_memory_anon_bytes, the anonymous memory the garbage collector can act on.
  • gitlab_memory_events_total{event}, counting allocations blocked by the hard limit and throttling at the soft limit.

The limit is the minimum over the process's cgroup and all its ancestors, because a container's own cgroup routinely reads "max" while the pod cgroup carries the limit the kernel actually enforces. Usage is read from the process's own cgroup, so in a multi-container pod the limit may be the pod's while usage is one container's.

A series that is present is a measurement. A series that is absent could not be determined: zero is never published as a stand-in, so absent() alerting works and the ratio above never divides by zero. Nothing is exported on non-Linux platforms, where cgroups do not exist.

Set Config.DisableMemoryCollector to opt out.

Sharing a registry

Pass an existing registry via Config.Registry to serve LabKit metrics from an endpoint another library already exposes. If that library also registers Go runtime and process collectors, set Config.DisableRuntimeCollectors so Metrics.Start leaves them alone:

// sigs.k8s.io/controller-runtime registers its own Go and process
// collectors into this registry during package init.
m, err := metrics.NewWithConfig(&metrics.Config{
    Registry:                 reg,
    DisableRuntimeCollectors: true,
})

Without the flag Start fails: it skips a collector that is already registered, but only when it is identical. A Go collector configured with a different runtime metric set overlaps the default one without matching it, and the registry rejects it with a duplicate-descriptor error.

OpenMetrics and exemplars

By default Metrics.Handler serves only the classic Prometheus text format. Set Config.EnableOpenMetrics to also serve the OpenMetrics format to scrapers that negotiate it. This is required for exemplars: a histogram sample tagged with the active trace_id lets an operator jump from a latency spike straight to the distributed trace behind it.

m, err := metrics.NewWithConfig(&metrics.Config{EnableOpenMetrics: true})
if err != nil {
    log.Fatal(err)
}

latency := prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    m.BuildName("http", "request_duration_seconds"),
    Buckets: metrics.DurationBuckets,
})
m.MustRegister(latency)

// Attach the current trace_id to the observation as an exemplar.
latency.(prometheus.ExemplarObserver).ObserveWithExemplar(
    time.Since(start).Seconds(),
    prometheus.Labels{"trace_id": traceID},
)

Injecting into sub-components

Pass only the prometheus.Registerer interface to components that need to register their own collectors. This limits access and keeps the public API surface small:

type Worker struct { reg prometheus.Registerer }

func (w *Worker) Start(ctx context.Context) error {
    jobs := prometheus.NewCounter(...)
    return w.reg.Register(jobs)
}

worker := &Worker{reg: m.Registerer()}
a.Register(worker)

Testing

Use [metricstest.New] from gitlab.com/gitlab-org/labkit/v2/testing/metricstest to obtain an isolated Metrics instance backed by a fresh registry. [metricstest.Gather] returns all metric families keyed by name for assertion:

import "gitlab.com/gitlab-org/labkit/v2/testing/metricstest"

func TestWorker_countsJobs(t *testing.T) {
    m := metricstest.New(t)

    w := NewWorker(m.Registerer())
    require.NoError(t, w.ProcessJob(ctx))

    families := metricstest.Gather(t, m)
    require.Contains(t, families, "gitlab_worker_jobs_total")
    assert.Equal(t, 1, int(families["gitlab_worker_jobs_total"].Metric[0].Counter.GetValue()))
}
Example

Example shows a Metrics component registered with an app.App lifecycle. In production use app.Run instead of Start/Shutdown directly.

package main

import (
	"context"

	"gitlab.com/gitlab-org/labkit/v2/metrics"
)

func main() {
	ctx := context.Background()

	m, err := metrics.New()
	if err != nil {
		panic(err)
	}

	if err := m.Start(ctx); err != nil {
		panic(err)
	}
	defer m.Shutdown(ctx) //nolint:errcheck

	// Mount the handler at /-/metrics in your HTTP server.
	_ = m.Handler()
}

Index

Examples

Constants

View Source
const (
	// LabelComponent identifies the logical component within a service,
	// e.g. "gitaly_pack_objects" or "workhorse_git_http".
	LabelComponent = "component"

	// LabelFeatureCategory maps a metric to a GitLab feature category for
	// ownership attribution, error budget tracking, and SLO alerting.
	// Values should match the feature category taxonomy in the GitLab handbook.
	LabelFeatureCategory = "feature_category"

	// LabelEndpointID identifies the endpoint that handled a request using a
	// low-cardinality identifier, e.g. "GET /api/v4/projects/{id}". Prefer
	// this over separate controller/action/route labels to reduce cardinality.
	LabelEndpointID = "endpoint_id"

	// LabelStatus is the HTTP response status class, e.g. "2xx" or "5xx".
	// Always use a class rather than the exact code to keep cardinality low.
	LabelStatus = "status"
)

Standard Prometheus label names used consistently across GitLab services. Using these constants avoids typos and ensures that dashboards and alert rules written against one service work uniformly across the fleet.

Variables

View Source
var DurationBuckets = []float64{0.1, 0.5, 1, 10, 60}

DurationBuckets are histogram bucket boundaries (in seconds) for measuring HTTP request latency in SLO-oriented histograms. The boundaries are tuned to align with Workhorse and Rails SLI thresholds used across GitLab's metrics catalog: 1s is the satisfied threshold and 10s is the tolerated threshold for most services.

View Source
var ObjectStoreDurationBuckets = []float64{0.01, 0.05, 0.25, 1, 5}

ObjectStoreDurationBuckets are histogram bucket boundaries (in seconds) for object store operations, from millisecond metadata calls to multi-second transfers.

View Source
var RedisDurationBuckets = []float64{0.001, 0.005, 0.025, 0.1, 0.5}

RedisDurationBuckets are histogram bucket boundaries (in seconds) for measuring Redis command latency. The boundaries are tuned for the sub-millisecond to low-millisecond range where Redis commands typically complete

Functions

This section is empty.

Types

type Config

type Config struct {
	// Name identifies this component in logs and errors.
	// Defaults to "metrics".
	Name string

	// Namespace is prepended to every metric name built with [Metrics.BuildName],
	// following the Prometheus convention of namespace_subsystem_name.
	// Defaults to "gitlab" to match GitLab's metric naming standards.
	Namespace string

	// Registry is the Prometheus registry used for all collector registration
	// and metric gathering. When nil, a new isolated registry is created.
	//
	// Override in tests to inject a pre-populated or inspectable registry
	// without affecting the process-global default.
	Registry *prometheus.Registry

	// DisableRuntimeCollectors stops [Metrics.Start] registering the Go runtime
	// and process collectors. Defaults to false, so every service exports the
	// standard go_* and process_* metrics without configuration.
	//
	// Set this when Registry is shared with something that already registers them.
	DisableRuntimeCollectors bool

	// DisableMemoryCollector stops [Metrics.Start] registering the cgroup
	// memory collector. Defaults to false, so every service exports
	// gitlab_memory_* without configuration.
	//
	// Set this when the process already exports an equivalent metric set, or
	// when the cgroup hierarchy is deliberately not the memory boundary you
	// want reported.
	DisableMemoryCollector bool

	// 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
}

Config holds optional configuration for New / NewWithConfig.

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics is an isolated Prometheus registry that implements [app.Component].

Use Metrics.MustRegister or Metrics.Register to add application or component-specific collectors. Use Metrics.Registerer when passing the registry to sub-components that should not control the full Metrics instance. Use Metrics.Handler to expose the collected metrics over HTTP.

func New

func New() (*Metrics, error)

New returns a Metrics with default configuration.

func NewWithConfig

func NewWithConfig(cfg *Config) (*Metrics, error)

NewWithConfig returns a Metrics configured with cfg.

Example

ExampleNewWithConfig shows how to configure a custom namespace and register application-specific collectors.

package main

import (
	"github.com/prometheus/client_golang/prometheus"
	"gitlab.com/gitlab-org/labkit/v2/metrics"
)

func main() {
	m, err := metrics.NewWithConfig(&metrics.Config{
		Name:      "primary-metrics",
		Namespace: "myservice",
	})
	if err != nil {
		panic(err)
	}

	requestsTotal := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: m.BuildName("http", "requests_total"),
		Help: "Total number of HTTP requests.",
		ConstLabels: prometheus.Labels{
			metrics.LabelComponent: "api",
		},
	}, []string{metrics.LabelFeatureCategory, metrics.LabelStatus})

	m.MustRegister(requestsTotal)

	requestsTotal.WithLabelValues("code_review", "2xx").Inc()
}
Example (SharedRegistry)

ExampleNewWithConfig_sharedRegistry shows how to serve LabKit metrics from a registry another library owns — here one that already registers its own Go runtime and process collectors, as sigs.k8s.io/controller-runtime does during package init. DisableRuntimeCollectors stops Start registering a second, conflicting pair.

package main

import (
	"context"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/collectors"
	"gitlab.com/gitlab-org/labkit/v2/metrics"
)

func main() {
	registry := prometheus.NewRegistry()
	registry.MustRegister(
		collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
		collectors.NewGoCollector(collectors.WithGoCollectorRuntimeMetrics(collectors.MetricsAll)),
	)

	m, err := metrics.NewWithConfig(&metrics.Config{
		Registry:                 registry,
		DisableRuntimeCollectors: true,
	})
	if err != nil {
		panic(err)
	}

	// Start is a no-op for the runtime collectors, so the ones already in the
	// registry are left untouched.
	if err := m.Start(context.Background()); err != nil {
		panic(err)
	}
}

func (*Metrics) BuildName

func (m *Metrics) BuildName(subsystem, name string) string

BuildName returns a fully-qualified metric name by joining the configured namespace, an optional subsystem, and the metric name, separated by underscores. Pass an empty subsystem to omit it.

m.BuildName("http", "requests_total")  // → gitlab_http_requests_total
m.BuildName("", "up")                  // → gitlab_up
Example

ExampleMetrics_BuildName shows how BuildName produces fully-qualified metric names from a namespace, subsystem, and name.

package main

import (
	"gitlab.com/gitlab-org/labkit/v2/metrics"
)

func main() {
	m, _ := metrics.NewWithConfig(&metrics.Config{Namespace: "gitlab"})

	_ = m.BuildName("workhorse", "requests_total") // → gitlab_workhorse_requests_total
	_ = m.BuildName("", "up")                      // → gitlab_up
}

func (*Metrics) Gatherer

func (m *Metrics) Gatherer() prometheus.Gatherer

Gatherer returns the prometheus.Gatherer for the underlying registry. Useful when combining multiple registries into a single metrics endpoint.

func (*Metrics) Handler

func (m *Metrics) Handler() http.Handler

Handler returns an http.Handler that serves all collected metrics in the Prometheus text exposition format. Mount it at /-/metrics to follow GitLab's health endpoint conventions alongside /-/liveness and /-/readiness.

The handler itself registers a small number of internal metrics (request counts and in-flight gauges) against the same registry so they appear in the output alongside application metrics.

When Config.EnableOpenMetrics is set, the handler serves the OpenMetrics exposition format to scrapers that negotiate it (Accept: application/openmetrics-text), which is required for exemplars to be serialized; other scrapers still receive the classic text format.

Example

ExampleMetrics_Handler shows how to wire the metrics handler into an HTTP server and exercise it in a test without binding a real port.

package main

import (
	"net/http"
	"net/http/httptest"

	"github.com/prometheus/client_golang/prometheus"
	"gitlab.com/gitlab-org/labkit/v2/metrics"
)

func main() {
	m, _ := metrics.New()

	hits := prometheus.NewCounter(prometheus.CounterOpts{
		Name: m.BuildName("", "cache_hits_total"),
		Help: "Total number of cache hits.",
	})
	m.MustRegister(hits)
	hits.Add(7)

	req := httptest.NewRequest(http.MethodGet, "/-/metrics", nil)
	rec := httptest.NewRecorder()
	m.Handler().ServeHTTP(rec, req)

	_ = rec.Code // 200
}

func (*Metrics) MountOn

func (m *Metrics) MountOn(r RouteRegistrar)

MountOn registers the metrics handler at /-/metrics on r. Use this when building a custom HTTP server that does not use the httpserver package, which mounts the endpoint automatically when a Metrics instance is configured.

func (*Metrics) MustRegister

func (m *Metrics) MustRegister(cs ...prometheus.Collector)

MustRegister registers collectors with the underlying registry. It panics on any registration error, consistent with the prometheus MustRegister convention used at init time.

func (*Metrics) Name

func (m *Metrics) Name() string

Name returns the component name for use in logs and error messages.

func (*Metrics) Register

func (m *Metrics) Register(c prometheus.Collector) error

Register registers a collector with the underlying registry.

func (*Metrics) Registerer

func (m *Metrics) Registerer() prometheus.Registerer

Registerer returns the prometheus.Registerer for the underlying registry. Pass this to components that need to register their own collectors without receiving access to the full Metrics instance.

func (*Metrics) Shutdown

func (m *Metrics) Shutdown(_ context.Context) error

Shutdown is a no-op; Prometheus registries hold no closeable resources. It satisfies [app.Component] and should be called via [app.App.Shutdown].

func (*Metrics) Start

func (m *Metrics) Start(_ context.Context) error

Start registers the Go runtime and process collectors and the cgroup memory collector with the underlying registry, unless Config.DisableRuntimeCollectors or Config.DisableMemoryCollector is set. It satisfies [app.Component] and should be called via [app.App.Start].

Start never fails because the memory collector cannot resolve a cgroup. The collector reads nothing until the first scrape, and a host with no cgroups simply emits no memory series.

type RouteRegistrar

type RouteRegistrar interface {
	Handle(pattern string, handler http.Handler)
}

RouteRegistrar is a minimal interface for mounting an http.Handler at a path pattern. It is satisfied by net/http.ServeMux, the httpserver.Router interface, and chi routers, allowing MountOn to work with any HTTP framework.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL