...

Source file src/github.com/docker/go-metrics/namespace.go

Documentation: github.com/docker/go-metrics

     1  package metrics
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/prometheus/client_golang/prometheus"
     8  )
     9  
    10  type Labels map[string]string
    11  
    12  // NewNamespace returns a namespaces that is responsible for managing a collection of
    13  // metrics for a particual namespace and subsystem
    14  //
    15  // labels allows const labels to be added to all metrics created in this namespace
    16  // and are commonly used for data like application version and git commit
    17  func NewNamespace(name, subsystem string, labels Labels) *Namespace {
    18  	if labels == nil {
    19  		labels = make(map[string]string)
    20  	}
    21  	return &Namespace{
    22  		name:      name,
    23  		subsystem: subsystem,
    24  		labels:    labels,
    25  	}
    26  }
    27  
    28  // Namespace describes a set of metrics that share a namespace and subsystem.
    29  type Namespace struct {
    30  	name      string
    31  	subsystem string
    32  	labels    Labels
    33  	mu        sync.Mutex
    34  	metrics   []prometheus.Collector
    35  }
    36  
    37  // WithConstLabels returns a namespace with the provided set of labels merged
    38  // with the existing constant labels on the namespace.
    39  //
    40  //  Only metrics created with the returned namespace will get the new constant
    41  //  labels.  The returned namespace must be registered separately.
    42  func (n *Namespace) WithConstLabels(labels Labels) *Namespace {
    43  	n.mu.Lock()
    44  	ns := &Namespace{
    45  		name:      n.name,
    46  		subsystem: n.subsystem,
    47  		labels:    mergeLabels(n.labels, labels),
    48  	}
    49  	n.mu.Unlock()
    50  	return ns
    51  }
    52  
    53  func (n *Namespace) NewCounter(name, help string) Counter {
    54  	c := &counter{pc: prometheus.NewCounter(n.newCounterOpts(name, help))}
    55  	n.Add(c)
    56  	return c
    57  }
    58  
    59  func (n *Namespace) NewLabeledCounter(name, help string, labels ...string) LabeledCounter {
    60  	c := &labeledCounter{pc: prometheus.NewCounterVec(n.newCounterOpts(name, help), labels)}
    61  	n.Add(c)
    62  	return c
    63  }
    64  
    65  func (n *Namespace) newCounterOpts(name, help string) prometheus.CounterOpts {
    66  	return prometheus.CounterOpts{
    67  		Namespace:   n.name,
    68  		Subsystem:   n.subsystem,
    69  		Name:        makeName(name, Total),
    70  		Help:        help,
    71  		ConstLabels: prometheus.Labels(n.labels),
    72  	}
    73  }
    74  
    75  func (n *Namespace) NewTimer(name, help string) Timer {
    76  	t := &timer{
    77  		m: prometheus.NewHistogram(n.newTimerOpts(name, help)),
    78  	}
    79  	n.Add(t)
    80  	return t
    81  }
    82  
    83  func (n *Namespace) NewLabeledTimer(name, help string, labels ...string) LabeledTimer {
    84  	t := &labeledTimer{
    85  		m: prometheus.NewHistogramVec(n.newTimerOpts(name, help), labels),
    86  	}
    87  	n.Add(t)
    88  	return t
    89  }
    90  
    91  func (n *Namespace) newTimerOpts(name, help string) prometheus.HistogramOpts {
    92  	return prometheus.HistogramOpts{
    93  		Namespace:   n.name,
    94  		Subsystem:   n.subsystem,
    95  		Name:        makeName(name, Seconds),
    96  		Help:        help,
    97  		ConstLabels: prometheus.Labels(n.labels),
    98  	}
    99  }
   100  
   101  func (n *Namespace) NewGauge(name, help string, unit Unit) Gauge {
   102  	g := &gauge{
   103  		pg: prometheus.NewGauge(n.newGaugeOpts(name, help, unit)),
   104  	}
   105  	n.Add(g)
   106  	return g
   107  }
   108  
   109  func (n *Namespace) NewLabeledGauge(name, help string, unit Unit, labels ...string) LabeledGauge {
   110  	g := &labeledGauge{
   111  		pg: prometheus.NewGaugeVec(n.newGaugeOpts(name, help, unit), labels),
   112  	}
   113  	n.Add(g)
   114  	return g
   115  }
   116  
   117  func (n *Namespace) newGaugeOpts(name, help string, unit Unit) prometheus.GaugeOpts {
   118  	return prometheus.GaugeOpts{
   119  		Namespace:   n.name,
   120  		Subsystem:   n.subsystem,
   121  		Name:        makeName(name, unit),
   122  		Help:        help,
   123  		ConstLabels: prometheus.Labels(n.labels),
   124  	}
   125  }
   126  
   127  func (n *Namespace) Describe(ch chan<- *prometheus.Desc) {
   128  	n.mu.Lock()
   129  	defer n.mu.Unlock()
   130  
   131  	for _, metric := range n.metrics {
   132  		metric.Describe(ch)
   133  	}
   134  }
   135  
   136  func (n *Namespace) Collect(ch chan<- prometheus.Metric) {
   137  	n.mu.Lock()
   138  	defer n.mu.Unlock()
   139  
   140  	for _, metric := range n.metrics {
   141  		metric.Collect(ch)
   142  	}
   143  }
   144  
   145  func (n *Namespace) Add(collector prometheus.Collector) {
   146  	n.mu.Lock()
   147  	n.metrics = append(n.metrics, collector)
   148  	n.mu.Unlock()
   149  }
   150  
   151  func (n *Namespace) NewDesc(name, help string, unit Unit, labels ...string) *prometheus.Desc {
   152  	name = makeName(name, unit)
   153  	namespace := n.name
   154  	if n.subsystem != "" {
   155  		namespace = fmt.Sprintf("%s_%s", namespace, n.subsystem)
   156  	}
   157  	name = fmt.Sprintf("%s_%s", namespace, name)
   158  	return prometheus.NewDesc(name, help, labels, prometheus.Labels(n.labels))
   159  }
   160  
   161  // mergeLabels merges two or more labels objects into a single map, favoring
   162  // the later labels.
   163  func mergeLabels(lbs ...Labels) Labels {
   164  	merged := make(Labels)
   165  
   166  	for _, target := range lbs {
   167  		for k, v := range target {
   168  			merged[k] = v
   169  		}
   170  	}
   171  
   172  	return merged
   173  }
   174  
   175  func makeName(name string, unit Unit) string {
   176  	if unit == "" {
   177  		return name
   178  	}
   179  
   180  	return fmt.Sprintf("%s_%s", name, unit)
   181  }
   182  
   183  func (n *Namespace) NewDefaultHttpMetrics(handlerName string) []*HTTPMetric {
   184  	return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{
   185  		DurationBuckets:     defaultDurationBuckets,
   186  		RequestSizeBuckets:  defaultResponseSizeBuckets,
   187  		ResponseSizeBuckets: defaultResponseSizeBuckets,
   188  	})
   189  }
   190  
   191  func (n *Namespace) NewHttpMetrics(handlerName string, durationBuckets, requestSizeBuckets, responseSizeBuckets []float64) []*HTTPMetric {
   192  	return n.NewHttpMetricsWithOpts(handlerName, HTTPHandlerOpts{
   193  		DurationBuckets:     durationBuckets,
   194  		RequestSizeBuckets:  requestSizeBuckets,
   195  		ResponseSizeBuckets: responseSizeBuckets,
   196  	})
   197  }
   198  
   199  func (n *Namespace) NewHttpMetricsWithOpts(handlerName string, opts HTTPHandlerOpts) []*HTTPMetric {
   200  	var httpMetrics []*HTTPMetric
   201  	inFlightMetric := n.NewInFlightGaugeMetric(handlerName)
   202  	requestTotalMetric := n.NewRequestTotalMetric(handlerName)
   203  	requestDurationMetric := n.NewRequestDurationMetric(handlerName, opts.DurationBuckets)
   204  	requestSizeMetric := n.NewRequestSizeMetric(handlerName, opts.RequestSizeBuckets)
   205  	responseSizeMetric := n.NewResponseSizeMetric(handlerName, opts.ResponseSizeBuckets)
   206  	httpMetrics = append(httpMetrics, inFlightMetric, requestDurationMetric, requestTotalMetric, requestSizeMetric, responseSizeMetric)
   207  	return httpMetrics
   208  }
   209  
   210  func (n *Namespace) NewInFlightGaugeMetric(handlerName string) *HTTPMetric {
   211  	labels := prometheus.Labels(n.labels)
   212  	labels["handler"] = handlerName
   213  	metric := prometheus.NewGauge(prometheus.GaugeOpts{
   214  		Namespace:   n.name,
   215  		Subsystem:   n.subsystem,
   216  		Name:        "in_flight_requests",
   217  		Help:        "The in-flight HTTP requests",
   218  		ConstLabels: prometheus.Labels(labels),
   219  	})
   220  	httpMetric := &HTTPMetric{
   221  		Collector:   metric,
   222  		handlerType: InstrumentHandlerInFlight,
   223  	}
   224  	n.Add(httpMetric)
   225  	return httpMetric
   226  }
   227  
   228  func (n *Namespace) NewRequestTotalMetric(handlerName string) *HTTPMetric {
   229  	labels := prometheus.Labels(n.labels)
   230  	labels["handler"] = handlerName
   231  	metric := prometheus.NewCounterVec(
   232  		prometheus.CounterOpts{
   233  			Namespace:   n.name,
   234  			Subsystem:   n.subsystem,
   235  			Name:        "requests_total",
   236  			Help:        "Total number of HTTP requests made.",
   237  			ConstLabels: prometheus.Labels(labels),
   238  		},
   239  		[]string{"code", "method"},
   240  	)
   241  	httpMetric := &HTTPMetric{
   242  		Collector:   metric,
   243  		handlerType: InstrumentHandlerCounter,
   244  	}
   245  	n.Add(httpMetric)
   246  	return httpMetric
   247  }
   248  func (n *Namespace) NewRequestDurationMetric(handlerName string, buckets []float64) *HTTPMetric {
   249  	if len(buckets) == 0 {
   250  		panic("DurationBuckets must be provided")
   251  	}
   252  	labels := prometheus.Labels(n.labels)
   253  	labels["handler"] = handlerName
   254  	opts := prometheus.HistogramOpts{
   255  		Namespace:   n.name,
   256  		Subsystem:   n.subsystem,
   257  		Name:        "request_duration_seconds",
   258  		Help:        "The HTTP request latencies in seconds.",
   259  		Buckets:     buckets,
   260  		ConstLabels: prometheus.Labels(labels),
   261  	}
   262  	metric := prometheus.NewHistogramVec(opts, []string{"method"})
   263  	httpMetric := &HTTPMetric{
   264  		Collector:   metric,
   265  		handlerType: InstrumentHandlerDuration,
   266  	}
   267  	n.Add(httpMetric)
   268  	return httpMetric
   269  }
   270  
   271  func (n *Namespace) NewRequestSizeMetric(handlerName string, buckets []float64) *HTTPMetric {
   272  	if len(buckets) == 0 {
   273  		panic("RequestSizeBuckets must be provided")
   274  	}
   275  	labels := prometheus.Labels(n.labels)
   276  	labels["handler"] = handlerName
   277  	opts := prometheus.HistogramOpts{
   278  		Namespace:   n.name,
   279  		Subsystem:   n.subsystem,
   280  		Name:        "request_size_bytes",
   281  		Help:        "The HTTP request sizes in bytes.",
   282  		Buckets:     buckets,
   283  		ConstLabels: prometheus.Labels(labels),
   284  	}
   285  	metric := prometheus.NewHistogramVec(opts, []string{})
   286  	httpMetric := &HTTPMetric{
   287  		Collector:   metric,
   288  		handlerType: InstrumentHandlerRequestSize,
   289  	}
   290  	n.Add(httpMetric)
   291  	return httpMetric
   292  }
   293  
   294  func (n *Namespace) NewResponseSizeMetric(handlerName string, buckets []float64) *HTTPMetric {
   295  	if len(buckets) == 0 {
   296  		panic("ResponseSizeBuckets must be provided")
   297  	}
   298  	labels := prometheus.Labels(n.labels)
   299  	labels["handler"] = handlerName
   300  	opts := prometheus.HistogramOpts{
   301  		Namespace:   n.name,
   302  		Subsystem:   n.subsystem,
   303  		Name:        "response_size_bytes",
   304  		Help:        "The HTTP response sizes in bytes.",
   305  		Buckets:     buckets,
   306  		ConstLabels: prometheus.Labels(labels),
   307  	}
   308  	metrics := prometheus.NewHistogramVec(opts, []string{})
   309  	httpMetric := &HTTPMetric{
   310  		Collector:   metrics,
   311  		handlerType: InstrumentHandlerResponseSize,
   312  	}
   313  	n.Add(httpMetric)
   314  	return httpMetric
   315  }
   316  

View as plain text