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; 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})
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)
// Mount the handler at /-/metrics in your HTTP server.
_ = m.Handler()
}
Output:
Index ¶
- Constants
- Variables
- type Config
- type Metrics
- func (m *Metrics) BuildName(subsystem, name string) string
- func (m *Metrics) Gatherer() prometheus.Gatherer
- func (m *Metrics) Handler() http.Handler
- func (m *Metrics) MountOn(r RouteRegistrar)
- func (m *Metrics) MustRegister(cs ...prometheus.Collector)
- func (m *Metrics) Name() string
- func (m *Metrics) Register(c prometheus.Collector) error
- func (m *Metrics) Registerer() prometheus.Registerer
- func (m *Metrics) Shutdown(_ context.Context) error
- func (m *Metrics) Start(_ context.Context) error
- type RouteRegistrar
Examples ¶
Constants ¶
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 ¶
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.
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
}
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 NewWithConfig ¶
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()
}
Output:
func (*Metrics) BuildName ¶
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
}
Output:
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 ¶
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.
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
}
Output:
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) 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.
type RouteRegistrar ¶
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.