Merge pull request #29554 from cpuguy83/keep_running_count_of_states

Use counter for tracking container states
Upstream-commit: 669f4ba37ef6eb5419306e6af4f6efdce92501a0
Component: engine
This commit is contained in:
Brian Goff
2017-05-05 16:15:22 -04:00
committed by GitHub
9 changed files with 176 additions and 31 deletions
+60 -3
View File
@@ -2,10 +2,67 @@
This package is small wrapper around the prometheus go client to help enforce convention and best practices for metrics collection in Docker projects.
## Status
## Best Practices
This project is a work in progress.
It is under heavy development and not intended to be used.
This packages is meant to be used for collecting metrics in Docker projects.
It is not meant to be used as a replacement for the prometheus client but to help enforce consistent naming across metrics collected.
If you have not already read the prometheus best practices around naming and labels you can read the page [here](https://prometheus.io/docs/practices/naming/).
The following are a few Docker specific rules that will help you name and work with metrics in your project.
1. Namespace and Subsystem
This package provides you with a namespace type that allows you to specify the same namespace and subsystem for your metrics.
```go
ns := metrics.NewNamespace("engine", "daemon", metrics.Labels{
"version": dockerversion.Version,
"commit": dockerversion.GitCommit,
})
```
In the example above we are creating metrics for the Docker engine's daemon package.
`engine` would be the namespace in this example where `daemon` is the subsystem or package where we are collecting the metrics.
A namespace also allows you to attach constant labels to the metrics such as the git commit and version that it is collecting.
2. Declaring your Metrics
Try to keep all your metric declarations in one file.
This makes it easy for others to see what constant labels are defined on the namespace and what labels are defined on the metrics when they are created.
3. Use labels instead of multiple metrics
Labels allow you to define one metric such as the time it takes to perform a certain action on an object.
If we wanted to collect timings on various container actions such as create, start, and delete then we can define one metric called `container_actions` and use labels to specify the type of action.
```go
containerActions = ns.NewLabeledTimer("container_actions", "The number of milliseconds it takes to process each container action", "action")
```
The last parameter is the label name or key.
When adding a data point to the metric you will use the `WithValues` function to specify the `action` that you are collecting for.
```go
containerActions.WithValues("create").UpdateSince(start)
```
4. Always use a unit
The metric name should describe what you are measuring but you also need to provide the unit that it is being measured with.
For a timer, the standard unit is seconds and a counter's standard unit is a total.
For gauges you must provide the unit.
This package provides a standard set of units for use within the Docker projects.
```go
Nanoseconds Unit = "nanoseconds"
Seconds Unit = "seconds"
Bytes Unit = "bytes"
Total Unit = "total"
```
If you need to use a unit but it is not defined in the package please open a PR to add it but first try to see if one of the already created units will work for your metric, i.e. seconds or nanoseconds vs adding milliseconds.
## Docs
+27 -11
View File
@@ -40,21 +40,25 @@ type Namespace struct {
// 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 {
ns := *n
ns.metrics = nil // blank this out
ns.labels = mergeLabels(ns.labels, labels)
return &ns
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.addMetric(c)
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.addMetric(c)
n.Add(c)
return c
}
@@ -72,7 +76,7 @@ func (n *Namespace) NewTimer(name, help string) Timer {
t := &timer{
m: prometheus.NewHistogram(n.newTimerOpts(name, help)),
}
n.addMetric(t)
n.Add(t)
return t
}
@@ -80,7 +84,7 @@ func (n *Namespace) NewLabeledTimer(name, help string, labels ...string) Labeled
t := &labeledTimer{
m: prometheus.NewHistogramVec(n.newTimerOpts(name, help), labels),
}
n.addMetric(t)
n.Add(t)
return t
}
@@ -98,7 +102,7 @@ func (n *Namespace) NewGauge(name, help string, unit Unit) Gauge {
g := &gauge{
pg: prometheus.NewGauge(n.newGaugeOpts(name, help, unit)),
}
n.addMetric(g)
n.Add(g)
return g
}
@@ -106,7 +110,7 @@ func (n *Namespace) NewLabeledGauge(name, help string, unit Unit, labels ...stri
g := &labeledGauge{
pg: prometheus.NewGaugeVec(n.newGaugeOpts(name, help, unit), labels),
}
n.addMetric(g)
n.Add(g)
return g
}
@@ -138,12 +142,24 @@ func (n *Namespace) Collect(ch chan<- prometheus.Metric) {
}
}
func (n *Namespace) addMetric(collector prometheus.Collector) {
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 {
if string(unit) != "" {
name = fmt.Sprintf("%s_%s", 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 {