123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- package metrics
- import (
- "fmt"
- "sync"
- "github.com/prometheus/client_golang/prometheus"
- )
- type Labels map[string]string
- // NewNamespace returns a namespaces that is responsible for managing a collection of
- // metrics for a particual namespace and subsystem
- //
- // labels allows const labels to be added to all metrics created in this namespace
- // and are commonly used for data like application version and git commit
- func NewNamespace(name, subsystem string, labels Labels) *Namespace {
- if labels == nil {
- labels = make(map[string]string)
- }
- return &Namespace{
- name: name,
- subsystem: subsystem,
- labels: labels,
- }
- }
- // Namespace describes a set of metrics that share a namespace and subsystem.
- type Namespace struct {
- name string
- subsystem string
- labels Labels
- mu sync.Mutex
- metrics []prometheus.Collector
- }
- // WithConstLabels returns a namespace with the provided set of labels merged
- // with the existing constant labels on the namespace.
- //
- // Only metrics created with the returned namespace will get the new constant
- // labels. The returned namespace must be registered separately.
- func (n *Namespace) WithConstLabels(labels Labels) *Namespace {
- n.mu.Lock()
- ns := &Namespace{
- name: n.name,
- subsystem: n.subsystem,
- labels: mergeLabels(n.labels, labels),
- }
- n.mu.Unlock()
- return ns
- }
- func (n *Namespace) NewCounter(name, help string) Counter {
- c := &counter{pc: prometheus.NewCounter(n.newCounterOpts(name, help))}
- n.Add(c)
- return c
- }
- func (n *Namespace) NewLabeledCounter(name, help string, labels ...string) LabeledCounter {
- c := &labeledCounter{pc: prometheus.NewCounterVec(n.newCounterOpts(name, help), labels)}
- n.Add(c)
- return c
- }
- func (n *Namespace) newCounterOpts(name, help string) prometheus.CounterOpts {
- return prometheus.CounterOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: makeName(name, Total),
- Help: help,
- ConstLabels: prometheus.Labels(n.labels),
- }
- }
- func (n *Namespace) NewTimer(name, help string) Timer {
- t := &timer{
- m: prometheus.NewHistogram(n.newTimerOpts(name, help)),
- }
- n.Add(t)
- return t
- }
- func (n *Namespace) NewLabeledTimer(name, help string, labels ...string) LabeledTimer {
- t := &labeledTimer{
- m: prometheus.NewHistogramVec(n.newTimerOpts(name, help), labels),
- }
- n.Add(t)
- return t
- }
- func (n *Namespace) newTimerOpts(name, help string) prometheus.HistogramOpts {
- return prometheus.HistogramOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: makeName(name, Seconds),
- Help: help,
- ConstLabels: prometheus.Labels(n.labels),
- }
- }
- func (n *Namespace) NewGauge(name, help string, unit Unit) Gauge {
- g := &gauge{
- pg: prometheus.NewGauge(n.newGaugeOpts(name, help, unit)),
- }
- n.Add(g)
- return g
- }
- func (n *Namespace) NewLabeledGauge(name, help string, unit Unit, labels ...string) LabeledGauge {
- g := &labeledGauge{
- pg: prometheus.NewGaugeVec(n.newGaugeOpts(name, help, unit), labels),
- }
- n.Add(g)
- return g
- }
- func (n *Namespace) newGaugeOpts(name, help string, unit Unit) prometheus.GaugeOpts {
- return prometheus.GaugeOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: makeName(name, unit),
- Help: help,
- ConstLabels: prometheus.Labels(n.labels),
- }
- }
- func (n *Namespace) Describe(ch chan<- *prometheus.Desc) {
- n.mu.Lock()
- defer n.mu.Unlock()
- for _, metric := range n.metrics {
- metric.Describe(ch)
- }
- }
- func (n *Namespace) Collect(ch chan<- prometheus.Metric) {
- n.mu.Lock()
- defer n.mu.Unlock()
- for _, metric := range n.metrics {
- metric.Collect(ch)
- }
- }
- func (n *Namespace) Add(collector prometheus.Collector) {
- n.mu.Lock()
- n.metrics = append(n.metrics, collector)
- n.mu.Unlock()
- }
- func (n *Namespace) NewDesc(name, help string, unit Unit, labels ...string) *prometheus.Desc {
- name = makeName(name, unit)
- namespace := n.name
- if n.subsystem != "" {
- namespace = fmt.Sprintf("%s_%s", namespace, n.subsystem)
- }
- name = fmt.Sprintf("%s_%s", namespace, name)
- return prometheus.NewDesc(name, help, labels, prometheus.Labels(n.labels))
- }
- // mergeLabels merges two or more labels objects into a single map, favoring
- // the later labels.
- func mergeLabels(lbs ...Labels) Labels {
- merged := make(Labels)
- for _, target := range lbs {
- for k, v := range target {
- merged[k] = v
- }
- }
- return merged
- }
- func makeName(name string, unit Unit) string {
- if unit == "" {
- return name
- }
- return fmt.Sprintf("%s_%s", name, unit)
- }
- func (n *Namespace) NewDefaultHttpMetrics(handlerName string) []*HTTPMetric {
- return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{
- DurationBuckets: defaultDurationBuckets,
- RequestSizeBuckets: defaultResponseSizeBuckets,
- ResponseSizeBuckets: defaultResponseSizeBuckets,
- })
- }
- func (n *Namespace) NewHttpMetrics(handlerName string, durationBuckets, requestSizeBuckets, responseSizeBuckets []float64) []*HTTPMetric {
- return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{
- DurationBuckets: durationBuckets,
- RequestSizeBuckets: requestSizeBuckets,
- ResponseSizeBuckets: responseSizeBuckets,
- })
- }
- func (n *Namespace) NewHttpMetricsWithOpts(handlerName string, opts HTTPHandlerOpts) []*HTTPMetric {
- var httpMetrics []*HTTPMetric
- inFlightMetric := n.NewInFlightGaugeMetric(handlerName)
- requestTotalMetric := n.NewRequestTotalMetric(handlerName)
- requestDurationMetric := n.NewRequestDurationMetric(handlerName, opts.DurationBuckets)
- requestSizeMetric := n.NewRequestSizeMetric(handlerName, opts.RequestSizeBuckets)
- responseSizeMetric := n.NewResponseSizeMetric(handlerName, opts.ResponseSizeBuckets)
- httpMetrics = append(httpMetrics, inFlightMetric, requestDurationMetric, requestTotalMetric, requestSizeMetric, responseSizeMetric)
- return httpMetrics
- }
- func (n *Namespace) NewInFlightGaugeMetric(handlerName string) *HTTPMetric {
- labels := prometheus.Labels(n.labels)
- labels["handler"] = handlerName
- metric := prometheus.NewGauge(prometheus.GaugeOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: "in_flight_requests",
- Help: "The in-flight HTTP requests",
- ConstLabels: prometheus.Labels(labels),
- })
- httpMetric := &HTTPMetric{
- Collector: metric,
- handlerType: InstrumentHandlerInFlight,
- }
- n.Add(httpMetric)
- return httpMetric
- }
- func (n *Namespace) NewRequestTotalMetric(handlerName string) *HTTPMetric {
- labels := prometheus.Labels(n.labels)
- labels["handler"] = handlerName
- metric := prometheus.NewCounterVec(
- prometheus.CounterOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: "requests_total",
- Help: "Total number of HTTP requests made.",
- ConstLabels: prometheus.Labels(labels),
- },
- []string{"code", "method"},
- )
- httpMetric := &HTTPMetric{
- Collector: metric,
- handlerType: InstrumentHandlerCounter,
- }
- n.Add(httpMetric)
- return httpMetric
- }
- func (n *Namespace) NewRequestDurationMetric(handlerName string, buckets []float64) *HTTPMetric {
- if len(buckets) == 0 {
- panic("DurationBuckets must be provided")
- }
- labels := prometheus.Labels(n.labels)
- labels["handler"] = handlerName
- opts := prometheus.HistogramOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: "request_duration_seconds",
- Help: "The HTTP request latencies in seconds.",
- Buckets: buckets,
- ConstLabels: prometheus.Labels(labels),
- }
- metric := prometheus.NewHistogramVec(opts, []string{"method"})
- httpMetric := &HTTPMetric{
- Collector: metric,
- handlerType: InstrumentHandlerDuration,
- }
- n.Add(httpMetric)
- return httpMetric
- }
- func (n *Namespace) NewRequestSizeMetric(handlerName string, buckets []float64) *HTTPMetric {
- if len(buckets) == 0 {
- panic("RequestSizeBuckets must be provided")
- }
- labels := prometheus.Labels(n.labels)
- labels["handler"] = handlerName
- opts := prometheus.HistogramOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: "request_size_bytes",
- Help: "The HTTP request sizes in bytes.",
- Buckets: buckets,
- ConstLabels: prometheus.Labels(labels),
- }
- metric := prometheus.NewHistogramVec(opts, []string{})
- httpMetric := &HTTPMetric{
- Collector: metric,
- handlerType: InstrumentHandlerRequestSize,
- }
- n.Add(httpMetric)
- return httpMetric
- }
- func (n *Namespace) NewResponseSizeMetric(handlerName string, buckets []float64) *HTTPMetric {
- if len(buckets) == 0 {
- panic("ResponseSizeBuckets must be provided")
- }
- labels := prometheus.Labels(n.labels)
- labels["handler"] = handlerName
- opts := prometheus.HistogramOpts{
- Namespace: n.name,
- Subsystem: n.subsystem,
- Name: "response_size_bytes",
- Help: "The HTTP response sizes in bytes.",
- Buckets: buckets,
- ConstLabels: prometheus.Labels(labels),
- }
- metrics := prometheus.NewHistogramVec(opts, []string{})
- httpMetric := &HTTPMetric{
- Collector: metrics,
- handlerType: InstrumentHandlerResponseSize,
- }
- n.Add(httpMetric)
- return httpMetric
- }
|