 04506b87d6
			
		
	
	04506b87d6
	
	
	
		
			
			full diff: 4ea375f775...v0.0.1
- docker/go-metrics#15 Add functions that instruments http handler using promhttp
- docker/go-metrics#20 Rename LICENSE.code → LICENSE
- docker/go-metrics#22 Support Go Modules
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
	
		
			
				
	
	
		
			316 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 |