mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-04-26 21:44:44 +00:00
Updating Prometheus client
This commit is contained in:
parent
6ac50dc9ff
commit
1e4d96d481
59 changed files with 7092 additions and 1939 deletions
73
vendor/github.com/prometheus/client_golang/prometheus/collector.go
generated
vendored
73
vendor/github.com/prometheus/client_golang/prometheus/collector.go
generated
vendored
|
@ -29,27 +29,72 @@ type Collector interface {
|
||||||
// collected by this Collector to the provided channel and returns once
|
// collected by this Collector to the provided channel and returns once
|
||||||
// the last descriptor has been sent. The sent descriptors fulfill the
|
// the last descriptor has been sent. The sent descriptors fulfill the
|
||||||
// consistency and uniqueness requirements described in the Desc
|
// consistency and uniqueness requirements described in the Desc
|
||||||
// documentation. (It is valid if one and the same Collector sends
|
// documentation.
|
||||||
// duplicate descriptors. Those duplicates are simply ignored. However,
|
//
|
||||||
// two different Collectors must not send duplicate descriptors.) This
|
// It is valid if one and the same Collector sends duplicate
|
||||||
// method idempotently sends the same descriptors throughout the
|
// descriptors. Those duplicates are simply ignored. However, two
|
||||||
// lifetime of the Collector. If a Collector encounters an error while
|
// different Collectors must not send duplicate descriptors.
|
||||||
// executing this method, it must send an invalid descriptor (created
|
//
|
||||||
// with NewInvalidDesc) to signal the error to the registry.
|
// Sending no descriptor at all marks the Collector as “unchecked”,
|
||||||
|
// i.e. no checks will be performed at registration time, and the
|
||||||
|
// Collector may yield any Metric it sees fit in its Collect method.
|
||||||
|
//
|
||||||
|
// This method idempotently sends the same descriptors throughout the
|
||||||
|
// lifetime of the Collector. It may be called concurrently and
|
||||||
|
// therefore must be implemented in a concurrency safe way.
|
||||||
|
//
|
||||||
|
// If a Collector encounters an error while executing this method, it
|
||||||
|
// must send an invalid descriptor (created with NewInvalidDesc) to
|
||||||
|
// signal the error to the registry.
|
||||||
Describe(chan<- *Desc)
|
Describe(chan<- *Desc)
|
||||||
// Collect is called by the Prometheus registry when collecting
|
// Collect is called by the Prometheus registry when collecting
|
||||||
// metrics. The implementation sends each collected metric via the
|
// metrics. The implementation sends each collected metric via the
|
||||||
// provided channel and returns once the last metric has been sent. The
|
// provided channel and returns once the last metric has been sent. The
|
||||||
// descriptor of each sent metric is one of those returned by
|
// descriptor of each sent metric is one of those returned by Describe
|
||||||
// Describe. Returned metrics that share the same descriptor must differ
|
// (unless the Collector is unchecked, see above). Returned metrics that
|
||||||
// in their variable label values. This method may be called
|
// share the same descriptor must differ in their variable label
|
||||||
// concurrently and must therefore be implemented in a concurrency safe
|
// values.
|
||||||
// way. Blocking occurs at the expense of total performance of rendering
|
//
|
||||||
// all registered metrics. Ideally, Collector implementations support
|
// This method may be called concurrently and must therefore be
|
||||||
// concurrent readers.
|
// implemented in a concurrency safe way. Blocking occurs at the expense
|
||||||
|
// of total performance of rendering all registered metrics. Ideally,
|
||||||
|
// Collector implementations support concurrent readers.
|
||||||
Collect(chan<- Metric)
|
Collect(chan<- Metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DescribeByCollect is a helper to implement the Describe method of a custom
|
||||||
|
// Collector. It collects the metrics from the provided Collector and sends
|
||||||
|
// their descriptors to the provided channel.
|
||||||
|
//
|
||||||
|
// If a Collector collects the same metrics throughout its lifetime, its
|
||||||
|
// Describe method can simply be implemented as:
|
||||||
|
//
|
||||||
|
// func (c customCollector) Describe(ch chan<- *Desc) {
|
||||||
|
// DescribeByCollect(c, ch)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// However, this will not work if the metrics collected change dynamically over
|
||||||
|
// the lifetime of the Collector in a way that their combined set of descriptors
|
||||||
|
// changes as well. The shortcut implementation will then violate the contract
|
||||||
|
// of the Describe method. If a Collector sometimes collects no metrics at all
|
||||||
|
// (for example vectors like CounterVec, GaugeVec, etc., which only collect
|
||||||
|
// metrics after a metric with a fully specified label set has been accessed),
|
||||||
|
// it might even get registered as an unchecked Collector (cf. the Register
|
||||||
|
// method of the Registerer interface). Hence, only use this shortcut
|
||||||
|
// implementation of Describe if you are certain to fulfill the contract.
|
||||||
|
//
|
||||||
|
// The Collector example demonstrates a use of DescribeByCollect.
|
||||||
|
func DescribeByCollect(c Collector, descs chan<- *Desc) {
|
||||||
|
metrics := make(chan Metric)
|
||||||
|
go func() {
|
||||||
|
c.Collect(metrics)
|
||||||
|
close(metrics)
|
||||||
|
}()
|
||||||
|
for m := range metrics {
|
||||||
|
descs <- m.Desc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// selfCollector implements Collector for a single Metric so that the Metric
|
// selfCollector implements Collector for a single Metric so that the Metric
|
||||||
// collects itself. Add it as an anonymous field to a struct that implements
|
// collects itself. Add it as an anonymous field to a struct that implements
|
||||||
// Metric, and call init with the Metric itself as an argument.
|
// Metric, and call init with the Metric itself as an argument.
|
||||||
|
|
179
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
179
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
|
@ -15,6 +15,10 @@ package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Counter is a Metric that represents a single numerical value that only ever
|
// Counter is a Metric that represents a single numerical value that only ever
|
||||||
|
@ -42,6 +46,14 @@ type Counter interface {
|
||||||
type CounterOpts Opts
|
type CounterOpts Opts
|
||||||
|
|
||||||
// NewCounter creates a new Counter based on the provided CounterOpts.
|
// NewCounter creates a new Counter based on the provided CounterOpts.
|
||||||
|
//
|
||||||
|
// The returned implementation tracks the counter value in two separate
|
||||||
|
// variables, a float64 and a uint64. The latter is used to track calls of the
|
||||||
|
// Inc method and calls of the Add method with a value that can be represented
|
||||||
|
// as a uint64. This allows atomic increments of the counter with optimal
|
||||||
|
// performance. (It is common to have an Inc call in very hot execution paths.)
|
||||||
|
// Both internal tracking values are added up in the Write method. This has to
|
||||||
|
// be taken into account when it comes to precision and overflow behavior.
|
||||||
func NewCounter(opts CounterOpts) Counter {
|
func NewCounter(opts CounterOpts) Counter {
|
||||||
desc := NewDesc(
|
desc := NewDesc(
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
@ -49,20 +61,58 @@ func NewCounter(opts CounterOpts) Counter {
|
||||||
nil,
|
nil,
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
)
|
)
|
||||||
result := &counter{value: value{desc: desc, valType: CounterValue, labelPairs: desc.constLabelPairs}}
|
result := &counter{desc: desc, labelPairs: desc.constLabelPairs}
|
||||||
result.init(result) // Init self-collection.
|
result.init(result) // Init self-collection.
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
type counter struct {
|
type counter struct {
|
||||||
value
|
// valBits contains the bits of the represented float64 value, while
|
||||||
|
// valInt stores values that are exact integers. Both have to go first
|
||||||
|
// in the struct to guarantee alignment for atomic operations.
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
valBits uint64
|
||||||
|
valInt uint64
|
||||||
|
|
||||||
|
selfCollector
|
||||||
|
desc *Desc
|
||||||
|
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Desc() *Desc {
|
||||||
|
return c.desc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *counter) Add(v float64) {
|
func (c *counter) Add(v float64) {
|
||||||
if v < 0 {
|
if v < 0 {
|
||||||
panic(errors.New("counter cannot decrease in value"))
|
panic(errors.New("counter cannot decrease in value"))
|
||||||
}
|
}
|
||||||
c.value.Add(v)
|
ival := uint64(v)
|
||||||
|
if float64(ival) == v {
|
||||||
|
atomic.AddUint64(&c.valInt, ival)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&c.valBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
|
||||||
|
if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Inc() {
|
||||||
|
atomic.AddUint64(&c.valInt, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *counter) Write(out *dto.Metric) error {
|
||||||
|
fval := math.Float64frombits(atomic.LoadUint64(&c.valBits))
|
||||||
|
ival := atomic.LoadUint64(&c.valInt)
|
||||||
|
val := fval + float64(ival)
|
||||||
|
|
||||||
|
return populateMetric(CounterValue, val, c.labelPairs, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CounterVec is a Collector that bundles a set of Counters that all share the
|
// CounterVec is a Collector that bundles a set of Counters that all share the
|
||||||
|
@ -70,16 +120,12 @@ func (c *counter) Add(v float64) {
|
||||||
// if you want to count the same thing partitioned by various dimensions
|
// if you want to count the same thing partitioned by various dimensions
|
||||||
// (e.g. number of HTTP requests, partitioned by response code and
|
// (e.g. number of HTTP requests, partitioned by response code and
|
||||||
// method). Create instances with NewCounterVec.
|
// method). Create instances with NewCounterVec.
|
||||||
//
|
|
||||||
// CounterVec embeds MetricVec. See there for a full list of methods with
|
|
||||||
// detailed documentation.
|
|
||||||
type CounterVec struct {
|
type CounterVec struct {
|
||||||
*MetricVec
|
*metricVec
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
||||||
// partitioned by the given label names. At least one label name must be
|
// partitioned by the given label names.
|
||||||
// provided.
|
|
||||||
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||||
desc := NewDesc(
|
desc := NewDesc(
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
@ -88,34 +134,62 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
)
|
)
|
||||||
return &CounterVec{
|
return &CounterVec{
|
||||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
result := &counter{value: value{
|
if len(lvs) != len(desc.variableLabels) {
|
||||||
desc: desc,
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||||
valType: CounterValue,
|
}
|
||||||
labelPairs: makeLabelPairs(desc, lvs),
|
result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
|
||||||
}}
|
|
||||||
result.init(result) // Init self-collection.
|
result.init(result) // Init self-collection.
|
||||||
return result
|
return result
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWithLabelValues replaces the method of the same name in
|
// GetMetricWithLabelValues returns the Counter for the given slice of label
|
||||||
// MetricVec. The difference is that this method returns a Counter and not a
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
// Metric so that no type conversion is required.
|
// label values is accessed for the first time, a new Counter is created.
|
||||||
func (m *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
//
|
||||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
// It is possible to call this method without using the returned Counter to only
|
||||||
|
// create the new Counter but leave it at its starting value 0. See also the
|
||||||
|
// SummaryVec example.
|
||||||
|
//
|
||||||
|
// Keeping the Counter for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Counter from the CounterVec. In that case,
|
||||||
|
// the Counter will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Counter with the same label values is created later.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the GaugeVec example.
|
||||||
|
func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Counter), err
|
return metric.(Counter), err
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
// GetMetricWith returns the Counter for the given Labels map (the label names
|
||||||
// difference is that this method returns a Counter and not a Metric so that no
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
// type conversion is required.
|
// accessed for the first time, a new Counter is created. Implications of
|
||||||
func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
// creating a Counter without using it and keeping the Counter for later use are
|
||||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
// the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Counter), err
|
return metric.(Counter), err
|
||||||
}
|
}
|
||||||
|
@ -123,18 +197,57 @@ func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
// error, WithLabelValues allows shortcuts like
|
// error allows shortcuts like
|
||||||
// myVec.WithLabelValues("404", "GET").Add(42)
|
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||||
func (m *CounterVec) WithLabelValues(lvs ...string) Counter {
|
func (v *CounterVec) WithLabelValues(lvs ...string) Counter {
|
||||||
return m.MetricVec.WithLabelValues(lvs...).(Counter)
|
c, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||||
// returned an error. By not returning an error, With allows shortcuts like
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
|
||||||
func (m *CounterVec) With(labels Labels) Counter {
|
func (v *CounterVec) With(labels Labels) Counter {
|
||||||
return m.MetricVec.With(labels).(Counter)
|
c, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the CounterVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &CounterVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
}
|
}
|
||||||
|
|
||||||
// CounterFunc is a Counter whose value is determined at collect time by calling a
|
// CounterFunc is a Counter whose value is determined at collect time by calling a
|
||||||
|
|
40
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
40
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
|
@ -25,19 +25,6 @@ import (
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
|
||||||
// label names.
|
|
||||||
const reservedLabelPrefix = "__"
|
|
||||||
|
|
||||||
// Labels represents a collection of label name -> value mappings. This type is
|
|
||||||
// commonly used with the With(Labels) and GetMetricWith(Labels) methods of
|
|
||||||
// metric vector Collectors, e.g.:
|
|
||||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
|
||||||
//
|
|
||||||
// The other use-case is the specification of constant label pairs in Opts or to
|
|
||||||
// create a Desc.
|
|
||||||
type Labels map[string]string
|
|
||||||
|
|
||||||
// Desc is the descriptor used by every Prometheus Metric. It is essentially
|
// Desc is the descriptor used by every Prometheus Metric. It is essentially
|
||||||
// the immutable meta-data of a Metric. The normal Metric implementations
|
// the immutable meta-data of a Metric. The normal Metric implementations
|
||||||
// included in this package manage their Desc under the hood. Users only have to
|
// included in this package manage their Desc under the hood. Users only have to
|
||||||
|
@ -80,24 +67,19 @@ type Desc struct {
|
||||||
|
|
||||||
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
|
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
|
||||||
// and will be reported on registration time. variableLabels and constLabels can
|
// and will be reported on registration time. variableLabels and constLabels can
|
||||||
// be nil if no such labels should be set. fqName and help must not be empty.
|
// be nil if no such labels should be set. fqName must not be empty.
|
||||||
//
|
//
|
||||||
// variableLabels only contain the label names. Their label values are variable
|
// variableLabels only contain the label names. Their label values are variable
|
||||||
// and therefore not part of the Desc. (They are managed within the Metric.)
|
// and therefore not part of the Desc. (They are managed within the Metric.)
|
||||||
//
|
//
|
||||||
// For constLabels, the label values are constant. Therefore, they are fully
|
// For constLabels, the label values are constant. Therefore, they are fully
|
||||||
// specified in the Desc. See the Opts documentation for the implications of
|
// specified in the Desc. See the Collector example for a usage pattern.
|
||||||
// constant labels.
|
|
||||||
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
|
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
|
||||||
d := &Desc{
|
d := &Desc{
|
||||||
fqName: fqName,
|
fqName: fqName,
|
||||||
help: help,
|
help: help,
|
||||||
variableLabels: variableLabels,
|
variableLabels: variableLabels,
|
||||||
}
|
}
|
||||||
if help == "" {
|
|
||||||
d.err = errors.New("empty help string")
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||||
return d
|
return d
|
||||||
|
@ -111,7 +93,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||||
// First add only the const label names and sort them...
|
// First add only the const label names and sort them...
|
||||||
for labelName := range constLabels {
|
for labelName := range constLabels {
|
||||||
if !checkLabelName(labelName) {
|
if !checkLabelName(labelName) {
|
||||||
d.err = fmt.Errorf("%q is not a valid label name", labelName)
|
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
labelNames = append(labelNames, labelName)
|
labelNames = append(labelNames, labelName)
|
||||||
|
@ -122,12 +104,18 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||||
for _, labelName := range labelNames {
|
for _, labelName := range labelNames {
|
||||||
labelValues = append(labelValues, constLabels[labelName])
|
labelValues = append(labelValues, constLabels[labelName])
|
||||||
}
|
}
|
||||||
|
// Validate the const label values. They can't have a wrong cardinality, so
|
||||||
|
// use in len(labelValues) as expectedNumberOfValues.
|
||||||
|
if err := validateLabelValues(labelValues, len(labelValues)); err != nil {
|
||||||
|
d.err = err
|
||||||
|
return d
|
||||||
|
}
|
||||||
// Now add the variable label names, but prefix them with something that
|
// Now add the variable label names, but prefix them with something that
|
||||||
// cannot be in a regular label name. That prevents matching the label
|
// cannot be in a regular label name. That prevents matching the label
|
||||||
// dimension with a different mix between preset and variable labels.
|
// dimension with a different mix between preset and variable labels.
|
||||||
for _, labelName := range variableLabels {
|
for _, labelName := range variableLabels {
|
||||||
if !checkLabelName(labelName) {
|
if !checkLabelName(labelName) {
|
||||||
d.err = fmt.Errorf("%q is not a valid label name", labelName)
|
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
labelNames = append(labelNames, "$"+labelName)
|
labelNames = append(labelNames, "$"+labelName)
|
||||||
|
@ -137,6 +125,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||||
d.err = errors.New("duplicate label names")
|
d.err = errors.New("duplicate label names")
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
vh := hashNew()
|
vh := hashNew()
|
||||||
for _, val := range labelValues {
|
for _, val := range labelValues {
|
||||||
vh = hashAdd(vh, val)
|
vh = hashAdd(vh, val)
|
||||||
|
@ -163,7 +152,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||||
Value: proto.String(v),
|
Value: proto.String(v),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Sort(LabelPairSorter(d.constLabelPairs))
|
sort.Sort(labelPairSorter(d.constLabelPairs))
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +182,3 @@ func (d *Desc) String() string {
|
||||||
d.variableLabels,
|
d.variableLabels,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkLabelName(l string) bool {
|
|
||||||
return model.LabelName(l).IsValid() &&
|
|
||||||
!strings.HasPrefix(l, reservedLabelPrefix)
|
|
||||||
}
|
|
||||||
|
|
40
vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
40
vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
|
@ -11,10 +11,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package prometheus provides metrics primitives to instrument code for
|
// Package prometheus is the core instrumentation package. It provides metrics
|
||||||
// monitoring. It also offers a registry for metrics. Sub-packages allow to
|
// primitives to instrument code for monitoring. It also offers a registry for
|
||||||
// expose the registered metrics via HTTP (package promhttp) or push them to a
|
// metrics. Sub-packages allow to expose the registered metrics via HTTP
|
||||||
// Pushgateway (package push).
|
// (package promhttp) or push them to a Pushgateway (package push). There is
|
||||||
|
// also a sub-package promauto, which provides metrics constructors with
|
||||||
|
// automatic registration.
|
||||||
//
|
//
|
||||||
// All exported functions and methods are safe to be used concurrently unless
|
// All exported functions and methods are safe to be used concurrently unless
|
||||||
// specified otherwise.
|
// specified otherwise.
|
||||||
|
@ -26,6 +28,7 @@
|
||||||
// package main
|
// package main
|
||||||
//
|
//
|
||||||
// import (
|
// import (
|
||||||
|
// "log"
|
||||||
// "net/http"
|
// "net/http"
|
||||||
//
|
//
|
||||||
// "github.com/prometheus/client_golang/prometheus"
|
// "github.com/prometheus/client_golang/prometheus"
|
||||||
|
@ -59,7 +62,7 @@
|
||||||
// // The Handler function provides a default handler to expose metrics
|
// // The Handler function provides a default handler to expose metrics
|
||||||
// // via an HTTP server. "/metrics" is the usual endpoint for that.
|
// // via an HTTP server. "/metrics" is the usual endpoint for that.
|
||||||
// http.Handle("/metrics", promhttp.Handler())
|
// http.Handle("/metrics", promhttp.Handler())
|
||||||
// log.Fatal(http.ListenAndServe(":8080", nil))
|
// log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -71,7 +74,10 @@
|
||||||
// The number of exported identifiers in this package might appear a bit
|
// The number of exported identifiers in this package might appear a bit
|
||||||
// overwhelming. However, in addition to the basic plumbing shown in the example
|
// overwhelming. However, in addition to the basic plumbing shown in the example
|
||||||
// above, you only need to understand the different metric types and their
|
// above, you only need to understand the different metric types and their
|
||||||
// vector versions for basic usage.
|
// vector versions for basic usage. Furthermore, if you are not concerned with
|
||||||
|
// fine-grained control of when and how to register metrics with the registry,
|
||||||
|
// have a look at the promauto package, which will effectively allow you to
|
||||||
|
// ignore registration altogether in simple cases.
|
||||||
//
|
//
|
||||||
// Above, you have already touched the Counter and the Gauge. There are two more
|
// Above, you have already touched the Counter and the Gauge. There are two more
|
||||||
// advanced metric types: the Summary and Histogram. A more thorough description
|
// advanced metric types: the Summary and Histogram. A more thorough description
|
||||||
|
@ -115,7 +121,17 @@
|
||||||
// NewConstSummary (and their respective Must… versions). That will happen in
|
// NewConstSummary (and their respective Must… versions). That will happen in
|
||||||
// the Collect method. The Describe method has to return separate Desc
|
// the Collect method. The Describe method has to return separate Desc
|
||||||
// instances, representative of the “throw-away” metrics to be created later.
|
// instances, representative of the “throw-away” metrics to be created later.
|
||||||
// NewDesc comes in handy to create those Desc instances.
|
// NewDesc comes in handy to create those Desc instances. Alternatively, you
|
||||||
|
// could return no Desc at all, which will mark the Collector “unchecked”. No
|
||||||
|
// checks are performed at registration time, but metric consistency will still
|
||||||
|
// be ensured at scrape time, i.e. any inconsistencies will lead to scrape
|
||||||
|
// errors. Thus, with unchecked Collectors, the responsibility to not collect
|
||||||
|
// metrics that lead to inconsistencies in the total scrape result lies with the
|
||||||
|
// implementer of the Collector. While this is not a desirable state, it is
|
||||||
|
// sometimes necessary. The typical use case is a situation where the exact
|
||||||
|
// metrics to be returned by a Collector cannot be predicted at registration
|
||||||
|
// time, but the implementer has sufficient knowledge of the whole system to
|
||||||
|
// guarantee metric consistency.
|
||||||
//
|
//
|
||||||
// The Collector example illustrates the use case. You can also look at the
|
// The Collector example illustrates the use case. You can also look at the
|
||||||
// source code of the processCollector (mirroring process metrics), the
|
// source code of the processCollector (mirroring process metrics), the
|
||||||
|
@ -144,7 +160,7 @@
|
||||||
// registry.
|
// registry.
|
||||||
//
|
//
|
||||||
// So far, everything we did operated on the so-called default registry, as it
|
// So far, everything we did operated on the so-called default registry, as it
|
||||||
// can be found in the global DefaultRegistry variable. With NewRegistry, you
|
// can be found in the global DefaultRegisterer variable. With NewRegistry, you
|
||||||
// can create a custom registry, or you can even implement the Registerer or
|
// can create a custom registry, or you can even implement the Registerer or
|
||||||
// Gatherer interfaces yourself. The methods Register and Unregister work in the
|
// Gatherer interfaces yourself. The methods Register and Unregister work in the
|
||||||
// same way on a custom registry as the global functions Register and Unregister
|
// same way on a custom registry as the global functions Register and Unregister
|
||||||
|
@ -152,11 +168,11 @@
|
||||||
//
|
//
|
||||||
// There are a number of uses for custom registries: You can use registries with
|
// There are a number of uses for custom registries: You can use registries with
|
||||||
// special properties, see NewPedanticRegistry. You can avoid global state, as
|
// special properties, see NewPedanticRegistry. You can avoid global state, as
|
||||||
// it is imposed by the DefaultRegistry. You can use multiple registries at the
|
// it is imposed by the DefaultRegisterer. You can use multiple registries at
|
||||||
// same time to expose different metrics in different ways. You can use separate
|
// the same time to expose different metrics in different ways. You can use
|
||||||
// registries for testing purposes.
|
// separate registries for testing purposes.
|
||||||
//
|
//
|
||||||
// Also note that the DefaultRegistry comes registered with a Collector for Go
|
// Also note that the DefaultRegisterer comes registered with a Collector for Go
|
||||||
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
|
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
|
||||||
// NewProcessCollector). With a custom registry, you are in control and decide
|
// NewProcessCollector). With a custom registry, you are in control and decide
|
||||||
// yourself about the Collectors to register.
|
// yourself about the Collectors to register.
|
||||||
|
|
13
vendor/github.com/prometheus/client_golang/prometheus/fnv.go
generated
vendored
13
vendor/github.com/prometheus/client_golang/prometheus/fnv.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
// Inline and byte-free variant of hash/fnv's fnv64a.
|
// Inline and byte-free variant of hash/fnv's fnv64a.
|
||||||
|
|
191
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
191
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
|
@ -13,6 +13,14 @@
|
||||||
|
|
||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
// Gauge is a Metric that represents a single numerical value that can
|
// Gauge is a Metric that represents a single numerical value that can
|
||||||
// arbitrarily go up and down.
|
// arbitrarily go up and down.
|
||||||
//
|
//
|
||||||
|
@ -48,13 +56,74 @@ type Gauge interface {
|
||||||
type GaugeOpts Opts
|
type GaugeOpts Opts
|
||||||
|
|
||||||
// NewGauge creates a new Gauge based on the provided GaugeOpts.
|
// NewGauge creates a new Gauge based on the provided GaugeOpts.
|
||||||
|
//
|
||||||
|
// The returned implementation is optimized for a fast Set method. If you have a
|
||||||
|
// choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick
|
||||||
|
// the former. For example, the Inc method of the returned Gauge is slower than
|
||||||
|
// the Inc method of a Counter returned by NewCounter. This matches the typical
|
||||||
|
// scenarios for Gauges and Counters, where the former tends to be Set-heavy and
|
||||||
|
// the latter Inc-heavy.
|
||||||
func NewGauge(opts GaugeOpts) Gauge {
|
func NewGauge(opts GaugeOpts) Gauge {
|
||||||
return newValue(NewDesc(
|
desc := NewDesc(
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
opts.Help,
|
opts.Help,
|
||||||
nil,
|
nil,
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
), GaugeValue, 0)
|
)
|
||||||
|
result := &gauge{desc: desc, labelPairs: desc.constLabelPairs}
|
||||||
|
result.init(result) // Init self-collection.
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type gauge struct {
|
||||||
|
// valBits contains the bits of the represented float64 value. It has
|
||||||
|
// to go first in the struct to guarantee alignment for atomic
|
||||||
|
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
valBits uint64
|
||||||
|
|
||||||
|
selfCollector
|
||||||
|
|
||||||
|
desc *Desc
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Desc() *Desc {
|
||||||
|
return g.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Set(val float64) {
|
||||||
|
atomic.StoreUint64(&g.valBits, math.Float64bits(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) SetToCurrentTime() {
|
||||||
|
g.Set(float64(time.Now().UnixNano()) / 1e9)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Inc() {
|
||||||
|
g.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Dec() {
|
||||||
|
g.Add(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Add(val float64) {
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&g.valBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
|
||||||
|
if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Sub(val float64) {
|
||||||
|
g.Add(val * -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gauge) Write(out *dto.Metric) error {
|
||||||
|
val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
|
||||||
|
return populateMetric(GaugeValue, val, g.labelPairs, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
||||||
|
@ -63,12 +132,11 @@ func NewGauge(opts GaugeOpts) Gauge {
|
||||||
// (e.g. number of operations queued, partitioned by user and operation
|
// (e.g. number of operations queued, partitioned by user and operation
|
||||||
// type). Create instances with NewGaugeVec.
|
// type). Create instances with NewGaugeVec.
|
||||||
type GaugeVec struct {
|
type GaugeVec struct {
|
||||||
*MetricVec
|
*metricVec
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
|
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
|
||||||
// partitioned by the given label names. At least one label name must be
|
// partitioned by the given label names.
|
||||||
// provided.
|
|
||||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||||
desc := NewDesc(
|
desc := NewDesc(
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
@ -77,28 +145,62 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
)
|
)
|
||||||
return &GaugeVec{
|
return &GaugeVec{
|
||||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
return newValue(desc, GaugeValue, 0, lvs...)
|
if len(lvs) != len(desc.variableLabels) {
|
||||||
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||||
|
}
|
||||||
|
result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
|
||||||
|
result.init(result) // Init self-collection.
|
||||||
|
return result
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWithLabelValues replaces the method of the same name in
|
// GetMetricWithLabelValues returns the Gauge for the given slice of label
|
||||||
// MetricVec. The difference is that this method returns a Gauge and not a
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
// Metric so that no type conversion is required.
|
// label values is accessed for the first time, a new Gauge is created.
|
||||||
func (m *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
//
|
||||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
// It is possible to call this method without using the returned Gauge to only
|
||||||
|
// create the new Gauge but leave it at its starting value 0. See also the
|
||||||
|
// SummaryVec example.
|
||||||
|
//
|
||||||
|
// Keeping the Gauge for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Gauge from the GaugeVec. In that case, the
|
||||||
|
// Gauge will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Gauge with the same label values is created later. See also the CounterVec
|
||||||
|
// example.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Gauge), err
|
return metric.(Gauge), err
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
// GetMetricWith returns the Gauge for the given Labels map (the label names
|
||||||
// difference is that this method returns a Gauge and not a Metric so that no
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
// type conversion is required.
|
// accessed for the first time, a new Gauge is created. Implications of
|
||||||
func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
// creating a Gauge without using it and keeping the Gauge for later use are
|
||||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
// the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Gauge), err
|
return metric.(Gauge), err
|
||||||
}
|
}
|
||||||
|
@ -106,18 +208,57 @@ func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
// error, WithLabelValues allows shortcuts like
|
// error allows shortcuts like
|
||||||
// myVec.WithLabelValues("404", "GET").Add(42)
|
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||||
func (m *GaugeVec) WithLabelValues(lvs ...string) Gauge {
|
func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge {
|
||||||
return m.MetricVec.WithLabelValues(lvs...).(Gauge)
|
g, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||||
// returned an error. By not returning an error, With allows shortcuts like
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
|
||||||
func (m *GaugeVec) With(labels Labels) Gauge {
|
func (v *GaugeVec) With(labels Labels) Gauge {
|
||||||
return m.MetricVec.With(labels).(Gauge)
|
g, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the GaugeVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &GaugeVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
}
|
}
|
||||||
|
|
||||||
// GaugeFunc is a Gauge whose value is determined at collect time by calling a
|
// GaugeFunc is a Gauge whose value is determined at collect time by calling a
|
||||||
|
|
116
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
116
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
|
@ -1,9 +1,22 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,13 +24,43 @@ type goCollector struct {
|
||||||
goroutinesDesc *Desc
|
goroutinesDesc *Desc
|
||||||
threadsDesc *Desc
|
threadsDesc *Desc
|
||||||
gcDesc *Desc
|
gcDesc *Desc
|
||||||
|
goInfoDesc *Desc
|
||||||
|
|
||||||
// metrics to describe and collect
|
// ms... are memstats related.
|
||||||
metrics memStatsMetrics
|
msLast *runtime.MemStats // Previously collected memstats.
|
||||||
|
msLastTimestamp time.Time
|
||||||
|
msMtx sync.Mutex // Protects msLast and msLastTimestamp.
|
||||||
|
msMetrics memStatsMetrics
|
||||||
|
msRead func(*runtime.MemStats) // For mocking in tests.
|
||||||
|
msMaxWait time.Duration // Wait time for fresh memstats.
|
||||||
|
msMaxAge time.Duration // Maximum allowed age of old memstats.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGoCollector returns a collector which exports metrics about the current
|
// NewGoCollector returns a collector which exports metrics about the current Go
|
||||||
// go process.
|
// process. This includes memory stats. To collect those, runtime.ReadMemStats
|
||||||
|
// is called. This requires to “stop the world”, which usually only happens for
|
||||||
|
// garbage collection (GC). Take the following implications into account when
|
||||||
|
// deciding whether to use the Go collector:
|
||||||
|
//
|
||||||
|
// 1. The performance impact of stopping the world is the more relevant the more
|
||||||
|
// frequently metrics are collected. However, with Go1.9 or later the
|
||||||
|
// stop-the-world time per metrics collection is very short (~25µs) so that the
|
||||||
|
// performance impact will only matter in rare cases. However, with older Go
|
||||||
|
// versions, the stop-the-world duration depends on the heap size and can be
|
||||||
|
// quite significant (~1.7 ms/GiB as per
|
||||||
|
// https://go-review.googlesource.com/c/go/+/34937).
|
||||||
|
//
|
||||||
|
// 2. During an ongoing GC, nothing else can stop the world. Therefore, if the
|
||||||
|
// metrics collection happens to coincide with GC, it will only complete after
|
||||||
|
// GC has finished. Usually, GC is fast enough to not cause problems. However,
|
||||||
|
// with a very large heap, GC might take multiple seconds, which is enough to
|
||||||
|
// cause scrape timeouts in common setups. To avoid this problem, the Go
|
||||||
|
// collector will use the memstats from a previous collection if
|
||||||
|
// runtime.ReadMemStats takes more than 1s. However, if there are no previously
|
||||||
|
// collected memstats, or their collection is more than 5m ago, the collection
|
||||||
|
// will block until runtime.ReadMemStats succeeds. (The problem might be solved
|
||||||
|
// in Go1.13, see https://github.com/golang/go/issues/19812 for the related Go
|
||||||
|
// issue.)
|
||||||
func NewGoCollector() Collector {
|
func NewGoCollector() Collector {
|
||||||
return &goCollector{
|
return &goCollector{
|
||||||
goroutinesDesc: NewDesc(
|
goroutinesDesc: NewDesc(
|
||||||
|
@ -26,13 +69,21 @@ func NewGoCollector() Collector {
|
||||||
nil, nil),
|
nil, nil),
|
||||||
threadsDesc: NewDesc(
|
threadsDesc: NewDesc(
|
||||||
"go_threads",
|
"go_threads",
|
||||||
"Number of OS threads created",
|
"Number of OS threads created.",
|
||||||
nil, nil),
|
nil, nil),
|
||||||
gcDesc: NewDesc(
|
gcDesc: NewDesc(
|
||||||
"go_gc_duration_seconds",
|
"go_gc_duration_seconds",
|
||||||
"A summary of the GC invocation durations.",
|
"A summary of the GC invocation durations.",
|
||||||
nil, nil),
|
nil, nil),
|
||||||
metrics: memStatsMetrics{
|
goInfoDesc: NewDesc(
|
||||||
|
"go_info",
|
||||||
|
"Information about the Go environment.",
|
||||||
|
nil, Labels{"version": runtime.Version()}),
|
||||||
|
msLast: &runtime.MemStats{},
|
||||||
|
msRead: runtime.ReadMemStats,
|
||||||
|
msMaxWait: time.Second,
|
||||||
|
msMaxAge: 5 * time.Minute,
|
||||||
|
msMetrics: memStatsMetrics{
|
||||||
{
|
{
|
||||||
desc: NewDesc(
|
desc: NewDesc(
|
||||||
memstatNamespace("alloc_bytes"),
|
memstatNamespace("alloc_bytes"),
|
||||||
|
@ -231,7 +282,7 @@ func NewGoCollector() Collector {
|
||||||
}
|
}
|
||||||
|
|
||||||
func memstatNamespace(s string) string {
|
func memstatNamespace(s string) string {
|
||||||
return fmt.Sprintf("go_memstats_%s", s)
|
return "go_memstats_" + s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe returns all descriptions of the collector.
|
// Describe returns all descriptions of the collector.
|
||||||
|
@ -239,13 +290,28 @@ func (c *goCollector) Describe(ch chan<- *Desc) {
|
||||||
ch <- c.goroutinesDesc
|
ch <- c.goroutinesDesc
|
||||||
ch <- c.threadsDesc
|
ch <- c.threadsDesc
|
||||||
ch <- c.gcDesc
|
ch <- c.gcDesc
|
||||||
for _, i := range c.metrics {
|
ch <- c.goInfoDesc
|
||||||
|
for _, i := range c.msMetrics {
|
||||||
ch <- i.desc
|
ch <- i.desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect returns the current state of all metrics of the collector.
|
// Collect returns the current state of all metrics of the collector.
|
||||||
func (c *goCollector) Collect(ch chan<- Metric) {
|
func (c *goCollector) Collect(ch chan<- Metric) {
|
||||||
|
var (
|
||||||
|
ms = &runtime.MemStats{}
|
||||||
|
done = make(chan struct{})
|
||||||
|
)
|
||||||
|
// Start reading memstats first as it might take a while.
|
||||||
|
go func() {
|
||||||
|
c.msRead(ms)
|
||||||
|
c.msMtx.Lock()
|
||||||
|
c.msLast = ms
|
||||||
|
c.msLastTimestamp = time.Now()
|
||||||
|
c.msMtx.Unlock()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
|
ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
|
||||||
n, _ := runtime.ThreadCreateProfile(nil)
|
n, _ := runtime.ThreadCreateProfile(nil)
|
||||||
ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
|
ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
|
||||||
|
@ -259,11 +325,35 @@ func (c *goCollector) Collect(ch chan<- Metric) {
|
||||||
quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
|
quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
|
||||||
}
|
}
|
||||||
quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
|
quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
|
||||||
ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles)
|
ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), stats.PauseTotal.Seconds(), quantiles)
|
||||||
|
|
||||||
ms := &runtime.MemStats{}
|
ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1)
|
||||||
runtime.ReadMemStats(ms)
|
|
||||||
for _, i := range c.metrics {
|
timer := time.NewTimer(c.msMaxWait)
|
||||||
|
select {
|
||||||
|
case <-done: // Our own ReadMemStats succeeded in time. Use it.
|
||||||
|
timer.Stop() // Important for high collection frequencies to not pile up timers.
|
||||||
|
c.msCollect(ch, ms)
|
||||||
|
return
|
||||||
|
case <-timer.C: // Time out, use last memstats if possible. Continue below.
|
||||||
|
}
|
||||||
|
c.msMtx.Lock()
|
||||||
|
if time.Since(c.msLastTimestamp) < c.msMaxAge {
|
||||||
|
// Last memstats are recent enough. Collect from them under the lock.
|
||||||
|
c.msCollect(ch, c.msLast)
|
||||||
|
c.msMtx.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If we are here, the last memstats are too old or don't exist. We have
|
||||||
|
// to wait until our own ReadMemStats finally completes. For that to
|
||||||
|
// happen, we have to release the lock.
|
||||||
|
c.msMtx.Unlock()
|
||||||
|
<-done
|
||||||
|
c.msCollect(ch, ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) {
|
||||||
|
for _, i := range c.msMetrics {
|
||||||
ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
|
ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
278
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
278
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
|
@ -16,7 +16,9 @@ package prometheus
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
@ -108,8 +110,9 @@ func ExponentialBuckets(start, factor float64, count int) []float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistogramOpts bundles the options for creating a Histogram metric. It is
|
// HistogramOpts bundles the options for creating a Histogram metric. It is
|
||||||
// mandatory to set Name and Help to a non-empty string. All other fields are
|
// mandatory to set Name to a non-empty string. All other fields are optional
|
||||||
// optional and can safely be left at their zero value.
|
// and can safely be left at their zero value, although it is strongly
|
||||||
|
// encouraged to set a Help string.
|
||||||
type HistogramOpts struct {
|
type HistogramOpts struct {
|
||||||
// Namespace, Subsystem, and Name are components of the fully-qualified
|
// Namespace, Subsystem, and Name are components of the fully-qualified
|
||||||
// name of the Histogram (created by joining these components with
|
// name of the Histogram (created by joining these components with
|
||||||
|
@ -120,29 +123,22 @@ type HistogramOpts struct {
|
||||||
Subsystem string
|
Subsystem string
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// Help provides information about this Histogram. Mandatory!
|
// Help provides information about this Histogram.
|
||||||
//
|
//
|
||||||
// Metrics with the same fully-qualified name must have the same Help
|
// Metrics with the same fully-qualified name must have the same Help
|
||||||
// string.
|
// string.
|
||||||
Help string
|
Help string
|
||||||
|
|
||||||
// ConstLabels are used to attach fixed labels to this
|
// ConstLabels are used to attach fixed labels to this metric. Metrics
|
||||||
// Histogram. Histograms with the same fully-qualified name must have the
|
// with the same fully-qualified name must have the same label names in
|
||||||
// same label names in their ConstLabels.
|
// their ConstLabels.
|
||||||
//
|
//
|
||||||
// Note that in most cases, labels have a value that varies during the
|
// ConstLabels are only used rarely. In particular, do not use them to
|
||||||
// lifetime of a process. Those labels are usually managed with a
|
// attach the same labels to all your metrics. Those use cases are
|
||||||
// HistogramVec. ConstLabels serve only special purposes. One is for the
|
// better covered by target labels set by the scraping Prometheus
|
||||||
// special case where the value of a label does not change during the
|
// server, or by one specific metric (e.g. a build_info or a
|
||||||
// lifetime of a process, e.g. if the revision of the running binary is
|
// machine_role metric). See also
|
||||||
// put into a label. Another, more advanced purpose is if more than one
|
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||||
// Collector needs to collect Histograms with the same fully-qualified
|
|
||||||
// name. In that case, those Summaries must differ in the values of
|
|
||||||
// their ConstLabels. See the Collector examples.
|
|
||||||
//
|
|
||||||
// If the value of a label never changes (not even between binaries),
|
|
||||||
// that label most likely should not be a label at all (but part of the
|
|
||||||
// metric name).
|
|
||||||
ConstLabels Labels
|
ConstLabels Labels
|
||||||
|
|
||||||
// Buckets defines the buckets into which observations are counted. Each
|
// Buckets defines the buckets into which observations are counted. Each
|
||||||
|
@ -169,7 +165,7 @@ func NewHistogram(opts HistogramOpts) Histogram {
|
||||||
|
|
||||||
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if len(desc.variableLabels) != len(labelValues) {
|
||||||
panic(errInconsistentCardinality)
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range desc.variableLabels {
|
for _, n := range desc.variableLabels {
|
||||||
|
@ -191,6 +187,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||||
desc: desc,
|
desc: desc,
|
||||||
upperBounds: opts.Buckets,
|
upperBounds: opts.Buckets,
|
||||||
labelPairs: makeLabelPairs(desc, labelValues),
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
counts: [2]*histogramCounts{&histogramCounts{}, &histogramCounts{}},
|
||||||
}
|
}
|
||||||
for i, upperBound := range h.upperBounds {
|
for i, upperBound := range h.upperBounds {
|
||||||
if i < len(h.upperBounds)-1 {
|
if i < len(h.upperBounds)-1 {
|
||||||
|
@ -207,30 +204,56 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finally we know the final length of h.upperBounds and can make counts.
|
// Finally we know the final length of h.upperBounds and can make buckets
|
||||||
h.counts = make([]uint64, len(h.upperBounds))
|
// for both counts:
|
||||||
|
h.counts[0].buckets = make([]uint64, len(h.upperBounds))
|
||||||
|
h.counts[1].buckets = make([]uint64, len(h.upperBounds))
|
||||||
|
|
||||||
h.init(h) // Init self-collection.
|
h.init(h) // Init self-collection.
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
type histogram struct {
|
type histogramCounts struct {
|
||||||
// sumBits contains the bits of the float64 representing the sum of all
|
// sumBits contains the bits of the float64 representing the sum of all
|
||||||
// observations. sumBits and count have to go first in the struct to
|
// observations. sumBits and count have to go first in the struct to
|
||||||
// guarantee alignment for atomic operations.
|
// guarantee alignment for atomic operations.
|
||||||
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
sumBits uint64
|
sumBits uint64
|
||||||
count uint64
|
count uint64
|
||||||
|
buckets []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type histogram struct {
|
||||||
|
// countAndHotIdx enables lock-free writes with use of atomic updates.
|
||||||
|
// The most significant bit is the hot index [0 or 1] of the count field
|
||||||
|
// below. Observe calls update the hot one. All remaining bits count the
|
||||||
|
// number of Observe calls. Observe starts by incrementing this counter,
|
||||||
|
// and finish by incrementing the count field in the respective
|
||||||
|
// histogramCounts, as a marker for completion.
|
||||||
|
//
|
||||||
|
// Calls of the Write method (which are non-mutating reads from the
|
||||||
|
// perspective of the histogram) swap the hot–cold under the writeMtx
|
||||||
|
// lock. A cooldown is awaited (while locked) by comparing the number of
|
||||||
|
// observations with the initiation count. Once they match, then the
|
||||||
|
// last observation on the now cool one has completed. All cool fields must
|
||||||
|
// be merged into the new hot before releasing writeMtx.
|
||||||
|
//
|
||||||
|
// Fields with atomic access first! See alignment constraint:
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
countAndHotIdx uint64
|
||||||
|
|
||||||
selfCollector
|
selfCollector
|
||||||
// Note that there is no mutex required.
|
desc *Desc
|
||||||
|
writeMtx sync.Mutex // Only used in the Write method.
|
||||||
|
|
||||||
desc *Desc
|
// Two counts, one is "hot" for lock-free observations, the other is
|
||||||
|
// "cold" for writing out a dto.Metric. It has to be an array of
|
||||||
|
// pointers to guarantee 64bit alignment of the histogramCounts, see
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
|
||||||
|
counts [2]*histogramCounts
|
||||||
|
|
||||||
upperBounds []float64
|
upperBounds []float64
|
||||||
counts []uint64
|
labelPairs []*dto.LabelPair
|
||||||
|
|
||||||
labelPairs []*dto.LabelPair
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *histogram) Desc() *Desc {
|
func (h *histogram) Desc() *Desc {
|
||||||
|
@ -248,36 +271,84 @@ func (h *histogram) Observe(v float64) {
|
||||||
// 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
|
// 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
|
||||||
// 300 buckets: 154 ns/op linear - binary 61.6 ns/op
|
// 300 buckets: 154 ns/op linear - binary 61.6 ns/op
|
||||||
i := sort.SearchFloat64s(h.upperBounds, v)
|
i := sort.SearchFloat64s(h.upperBounds, v)
|
||||||
if i < len(h.counts) {
|
|
||||||
atomic.AddUint64(&h.counts[i], 1)
|
// We increment h.countAndHotIdx so that the counter in the lower
|
||||||
|
// 63 bits gets incremented. At the same time, we get the new value
|
||||||
|
// back, which we can use to find the currently-hot counts.
|
||||||
|
n := atomic.AddUint64(&h.countAndHotIdx, 1)
|
||||||
|
hotCounts := h.counts[n>>63]
|
||||||
|
|
||||||
|
if i < len(h.upperBounds) {
|
||||||
|
atomic.AddUint64(&hotCounts.buckets[i], 1)
|
||||||
}
|
}
|
||||||
atomic.AddUint64(&h.count, 1)
|
|
||||||
for {
|
for {
|
||||||
oldBits := atomic.LoadUint64(&h.sumBits)
|
oldBits := atomic.LoadUint64(&hotCounts.sumBits)
|
||||||
newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
|
||||||
if atomic.CompareAndSwapUint64(&h.sumBits, oldBits, newBits) {
|
if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Increment count last as we take it as a signal that the observation
|
||||||
|
// is complete.
|
||||||
|
atomic.AddUint64(&hotCounts.count, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *histogram) Write(out *dto.Metric) error {
|
func (h *histogram) Write(out *dto.Metric) error {
|
||||||
his := &dto.Histogram{}
|
// For simplicity, we protect this whole method by a mutex. It is not in
|
||||||
buckets := make([]*dto.Bucket, len(h.upperBounds))
|
// the hot path, i.e. Observe is called much more often than Write. The
|
||||||
|
// complication of making Write lock-free isn't worth it, if possible at
|
||||||
|
// all.
|
||||||
|
h.writeMtx.Lock()
|
||||||
|
defer h.writeMtx.Unlock()
|
||||||
|
|
||||||
his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&h.sumBits)))
|
// Adding 1<<63 switches the hot index (from 0 to 1 or from 1 to 0)
|
||||||
his.SampleCount = proto.Uint64(atomic.LoadUint64(&h.count))
|
// without touching the count bits. See the struct comments for a full
|
||||||
var count uint64
|
// description of the algorithm.
|
||||||
|
n := atomic.AddUint64(&h.countAndHotIdx, 1<<63)
|
||||||
|
// count is contained unchanged in the lower 63 bits.
|
||||||
|
count := n & ((1 << 63) - 1)
|
||||||
|
// The most significant bit tells us which counts is hot. The complement
|
||||||
|
// is thus the cold one.
|
||||||
|
hotCounts := h.counts[n>>63]
|
||||||
|
coldCounts := h.counts[(^n)>>63]
|
||||||
|
|
||||||
|
// Await cooldown.
|
||||||
|
for count != atomic.LoadUint64(&coldCounts.count) {
|
||||||
|
runtime.Gosched() // Let observations get work done.
|
||||||
|
}
|
||||||
|
|
||||||
|
his := &dto.Histogram{
|
||||||
|
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
|
||||||
|
SampleCount: proto.Uint64(count),
|
||||||
|
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||||
|
}
|
||||||
|
var cumCount uint64
|
||||||
for i, upperBound := range h.upperBounds {
|
for i, upperBound := range h.upperBounds {
|
||||||
count += atomic.LoadUint64(&h.counts[i])
|
cumCount += atomic.LoadUint64(&coldCounts.buckets[i])
|
||||||
buckets[i] = &dto.Bucket{
|
his.Bucket[i] = &dto.Bucket{
|
||||||
CumulativeCount: proto.Uint64(count),
|
CumulativeCount: proto.Uint64(cumCount),
|
||||||
UpperBound: proto.Float64(upperBound),
|
UpperBound: proto.Float64(upperBound),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
his.Bucket = buckets
|
|
||||||
out.Histogram = his
|
out.Histogram = his
|
||||||
out.Label = h.labelPairs
|
out.Label = h.labelPairs
|
||||||
|
|
||||||
|
// Finally add all the cold counts to the new hot counts and reset the cold counts.
|
||||||
|
atomic.AddUint64(&hotCounts.count, count)
|
||||||
|
atomic.StoreUint64(&coldCounts.count, 0)
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&hotCounts.sumBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + his.GetSampleSum())
|
||||||
|
if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
|
||||||
|
atomic.StoreUint64(&coldCounts.sumBits, 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range h.upperBounds {
|
||||||
|
atomic.AddUint64(&hotCounts.buckets[i], atomic.LoadUint64(&coldCounts.buckets[i]))
|
||||||
|
atomic.StoreUint64(&coldCounts.buckets[i], 0)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,12 +358,11 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||||
// instances with NewHistogramVec.
|
// instances with NewHistogramVec.
|
||||||
type HistogramVec struct {
|
type HistogramVec struct {
|
||||||
*MetricVec
|
*metricVec
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
|
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
|
||||||
// partitioned by the given label names. At least one label name must be
|
// partitioned by the given label names.
|
||||||
// provided.
|
|
||||||
func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||||
desc := NewDesc(
|
desc := NewDesc(
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
|
@ -301,28 +371,58 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
)
|
)
|
||||||
return &HistogramVec{
|
return &HistogramVec{
|
||||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
return newHistogram(desc, opts, lvs...)
|
return newHistogram(desc, opts, lvs...)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWithLabelValues replaces the method of the same name in
|
// GetMetricWithLabelValues returns the Histogram for the given slice of label
|
||||||
// MetricVec. The difference is that this method returns an Observer and not a
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
// Metric so that no type conversion to an Observer is required.
|
// label values is accessed for the first time, a new Histogram is created.
|
||||||
func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
//
|
||||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
// It is possible to call this method without using the returned Histogram to only
|
||||||
|
// create the new Histogram but leave it at its starting value, a Histogram without
|
||||||
|
// any observations.
|
||||||
|
//
|
||||||
|
// Keeping the Histogram for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Histogram from the HistogramVec. In that case, the
|
||||||
|
// Histogram will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Histogram with the same label values is created later. See also the CounterVec
|
||||||
|
// example.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the GaugeVec example.
|
||||||
|
func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Observer), err
|
return metric.(Observer), err
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
// GetMetricWith returns the Histogram for the given Labels map (the label names
|
||||||
// difference is that this method returns an Observer and not a Metric so that no
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
// type conversion to an Observer is required.
|
// accessed for the first time, a new Histogram is created. Implications of
|
||||||
func (m *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
// creating a Histogram without using it and keeping the Histogram for later use
|
||||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
// are the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Observer), err
|
return metric.(Observer), err
|
||||||
}
|
}
|
||||||
|
@ -330,18 +430,57 @@ func (m *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
// error, WithLabelValues allows shortcuts like
|
// error allows shortcuts like
|
||||||
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
||||||
func (m *HistogramVec) WithLabelValues(lvs ...string) Observer {
|
func (v *HistogramVec) WithLabelValues(lvs ...string) Observer {
|
||||||
return m.MetricVec.WithLabelValues(lvs...).(Observer)
|
h, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
// With works as GetMetricWith but panics where GetMetricWithLabels would have
|
||||||
// returned an error. By not returning an error, With allows shortcuts like
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
||||||
func (m *HistogramVec) With(labels Labels) Observer {
|
func (v *HistogramVec) With(labels Labels) Observer {
|
||||||
return m.MetricVec.With(labels).(Observer)
|
h, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the HistogramVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &HistogramVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
}
|
}
|
||||||
|
|
||||||
type constHistogram struct {
|
type constHistogram struct {
|
||||||
|
@ -393,7 +532,7 @@ func (h *constHistogram) Write(out *dto.Metric) error {
|
||||||
// bucket.
|
// bucket.
|
||||||
//
|
//
|
||||||
// NewConstHistogram returns an error if the length of labelValues is not
|
// NewConstHistogram returns an error if the length of labelValues is not
|
||||||
// consistent with the variable labels in Desc.
|
// consistent with the variable labels in Desc or if Desc is invalid.
|
||||||
func NewConstHistogram(
|
func NewConstHistogram(
|
||||||
desc *Desc,
|
desc *Desc,
|
||||||
count uint64,
|
count uint64,
|
||||||
|
@ -401,8 +540,11 @@ func NewConstHistogram(
|
||||||
buckets map[float64]uint64,
|
buckets map[float64]uint64,
|
||||||
labelValues ...string,
|
labelValues ...string,
|
||||||
) (Metric, error) {
|
) (Metric, error) {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if desc.err != nil {
|
||||||
return nil, errInconsistentCardinality
|
return nil, desc.err
|
||||||
|
}
|
||||||
|
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return &constHistogram{
|
return &constHistogram{
|
||||||
desc: desc,
|
desc: desc,
|
||||||
|
|
167
vendor/github.com/prometheus/client_golang/prometheus/http.go
generated
vendored
167
vendor/github.com/prometheus/client_golang/prometheus/http.go
generated
vendored
|
@ -15,9 +15,7 @@ package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -36,24 +34,14 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
contentTypeHeader = "Content-Type"
|
contentTypeHeader = "Content-Type"
|
||||||
contentLengthHeader = "Content-Length"
|
|
||||||
contentEncodingHeader = "Content-Encoding"
|
contentEncodingHeader = "Content-Encoding"
|
||||||
acceptEncodingHeader = "Accept-Encoding"
|
acceptEncodingHeader = "Accept-Encoding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bufPool sync.Pool
|
var gzipPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
func getBuf() *bytes.Buffer {
|
return gzip.NewWriter(nil)
|
||||||
buf := bufPool.Get()
|
},
|
||||||
if buf == nil {
|
|
||||||
return &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
return buf.(*bytes.Buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func giveBuf(buf *bytes.Buffer) {
|
|
||||||
buf.Reset()
|
|
||||||
bufPool.Put(buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler returns an HTTP handler for the DefaultGatherer. It is
|
// Handler returns an HTTP handler for the DefaultGatherer. It is
|
||||||
|
@ -61,68 +49,50 @@ func giveBuf(buf *bytes.Buffer) {
|
||||||
// name).
|
// name).
|
||||||
//
|
//
|
||||||
// Deprecated: Please note the issues described in the doc comment of
|
// Deprecated: Please note the issues described in the doc comment of
|
||||||
// InstrumentHandler. You might want to consider using promhttp.Handler instead
|
// InstrumentHandler. You might want to consider using promhttp.Handler instead.
|
||||||
// (which is not instrumented).
|
|
||||||
func Handler() http.Handler {
|
func Handler() http.Handler {
|
||||||
return InstrumentHandler("prometheus", UninstrumentedHandler())
|
return InstrumentHandler("prometheus", UninstrumentedHandler())
|
||||||
}
|
}
|
||||||
|
|
||||||
// UninstrumentedHandler returns an HTTP handler for the DefaultGatherer.
|
// UninstrumentedHandler returns an HTTP handler for the DefaultGatherer.
|
||||||
//
|
//
|
||||||
// Deprecated: Use promhttp.Handler instead. See there for further documentation.
|
// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
|
||||||
|
// instead. See there for further documentation.
|
||||||
func UninstrumentedHandler() http.Handler {
|
func UninstrumentedHandler() http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
|
||||||
mfs, err := DefaultGatherer.Gather()
|
mfs, err := DefaultGatherer.Gather()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError)
|
httpError(rsp, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType := expfmt.Negotiate(req.Header)
|
contentType := expfmt.Negotiate(req.Header)
|
||||||
buf := getBuf()
|
header := rsp.Header()
|
||||||
defer giveBuf(buf)
|
header.Set(contentTypeHeader, string(contentType))
|
||||||
writer, encoding := decorateWriter(req, buf)
|
|
||||||
enc := expfmt.NewEncoder(writer, contentType)
|
w := io.Writer(rsp)
|
||||||
var lastErr error
|
if gzipAccepted(req.Header) {
|
||||||
|
header.Set(contentEncodingHeader, "gzip")
|
||||||
|
gz := gzipPool.Get().(*gzip.Writer)
|
||||||
|
defer gzipPool.Put(gz)
|
||||||
|
|
||||||
|
gz.Reset(w)
|
||||||
|
defer gz.Close()
|
||||||
|
|
||||||
|
w = gz
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := expfmt.NewEncoder(w, contentType)
|
||||||
|
|
||||||
for _, mf := range mfs {
|
for _, mf := range mfs {
|
||||||
if err := enc.Encode(mf); err != nil {
|
if err := enc.Encode(mf); err != nil {
|
||||||
lastErr = err
|
httpError(rsp, err)
|
||||||
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if closer, ok := writer.(io.Closer); ok {
|
|
||||||
closer.Close()
|
|
||||||
}
|
|
||||||
if lastErr != nil && buf.Len() == 0 {
|
|
||||||
http.Error(w, "No metrics encoded, last error:\n\n"+err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
header := w.Header()
|
|
||||||
header.Set(contentTypeHeader, string(contentType))
|
|
||||||
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
|
||||||
if encoding != "" {
|
|
||||||
header.Set(contentEncodingHeader, encoding)
|
|
||||||
}
|
|
||||||
w.Write(buf.Bytes())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// decorateWriter wraps a writer to handle gzip compression if requested. It
|
|
||||||
// returns the decorated writer and the appropriate "Content-Encoding" header
|
|
||||||
// (which is empty if no compression is enabled).
|
|
||||||
func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) {
|
|
||||||
header := request.Header.Get(acceptEncodingHeader)
|
|
||||||
parts := strings.Split(header, ",")
|
|
||||||
for _, part := range parts {
|
|
||||||
part := strings.TrimSpace(part)
|
|
||||||
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
|
||||||
return gzip.NewWriter(writer), "gzip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writer, ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var instLabels = []string{"method", "code"}
|
var instLabels = []string{"method", "code"}
|
||||||
|
|
||||||
type nower interface {
|
type nower interface {
|
||||||
|
@ -139,16 +109,6 @@ var now nower = nowFunc(func() time.Time {
|
||||||
return time.Now()
|
return time.Now()
|
||||||
})
|
})
|
||||||
|
|
||||||
func nowSeries(t ...time.Time) nower {
|
|
||||||
return nowFunc(func() time.Time {
|
|
||||||
defer func() {
|
|
||||||
t = t[1:]
|
|
||||||
}()
|
|
||||||
|
|
||||||
return t[0]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstrumentHandler wraps the given HTTP handler for instrumentation. It
|
// InstrumentHandler wraps the given HTTP handler for instrumentation. It
|
||||||
// registers four metric collectors (if not already done) and reports HTTP
|
// registers four metric collectors (if not already done) and reports HTTP
|
||||||
// metrics to the (newly or already) registered collectors: http_requests_total
|
// metrics to the (newly or already) registered collectors: http_requests_total
|
||||||
|
@ -158,26 +118,16 @@ func nowSeries(t ...time.Time) nower {
|
||||||
// value. http_requests_total is a metric vector partitioned by HTTP method
|
// value. http_requests_total is a metric vector partitioned by HTTP method
|
||||||
// (label name "method") and HTTP status code (label name "code").
|
// (label name "method") and HTTP status code (label name "code").
|
||||||
//
|
//
|
||||||
// Deprecated: InstrumentHandler has several issues:
|
// Deprecated: InstrumentHandler has several issues. Use the tooling provided in
|
||||||
//
|
// package promhttp instead. The issues are the following: (1) It uses Summaries
|
||||||
// - It uses Summaries rather than Histograms. Summaries are not useful if
|
// rather than Histograms. Summaries are not useful if aggregation across
|
||||||
// aggregation across multiple instances is required.
|
// multiple instances is required. (2) It uses microseconds as unit, which is
|
||||||
//
|
// deprecated and should be replaced by seconds. (3) The size of the request is
|
||||||
// - It uses microseconds as unit, which is deprecated and should be replaced by
|
// calculated in a separate goroutine. Since this calculator requires access to
|
||||||
// seconds.
|
// the request header, it creates a race with any writes to the header performed
|
||||||
//
|
// during request handling. httputil.ReverseProxy is a prominent example for a
|
||||||
// - The size of the request is calculated in a separate goroutine. Since this
|
// handler performing such writes. (4) It has additional issues with HTTP/2, cf.
|
||||||
// calculator requires access to the request header, it creates a race with
|
|
||||||
// any writes to the header performed during request handling.
|
|
||||||
// httputil.ReverseProxy is a prominent example for a handler
|
|
||||||
// performing such writes.
|
|
||||||
//
|
|
||||||
// - It has additional issues with HTTP/2, cf.
|
|
||||||
// https://github.com/prometheus/client_golang/issues/272.
|
// https://github.com/prometheus/client_golang/issues/272.
|
||||||
//
|
|
||||||
// Upcoming versions of this package will provide ways of instrumenting HTTP
|
|
||||||
// handlers that are more flexible and have fewer issues. Please prefer direct
|
|
||||||
// instrumentation in the meantime.
|
|
||||||
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
|
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
|
||||||
return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
|
return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +137,7 @@ func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFun
|
||||||
// issues).
|
// issues).
|
||||||
//
|
//
|
||||||
// Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
|
// Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
|
||||||
// InstrumentHandler is.
|
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||||
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||||
return InstrumentHandlerFuncWithOpts(
|
return InstrumentHandlerFuncWithOpts(
|
||||||
SummaryOpts{
|
SummaryOpts{
|
||||||
|
@ -226,7 +176,7 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri
|
||||||
// SummaryOpts.
|
// SummaryOpts.
|
||||||
//
|
//
|
||||||
// Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
|
// Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
|
||||||
// InstrumentHandler is.
|
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||||
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
|
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
|
||||||
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
|
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +187,7 @@ func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.Hand
|
||||||
// SummaryOpts are used.
|
// SummaryOpts are used.
|
||||||
//
|
//
|
||||||
// Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
|
// Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
|
||||||
// as InstrumentHandler is.
|
// as InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||||
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||||
reqCnt := NewCounterVec(
|
reqCnt := NewCounterVec(
|
||||||
CounterOpts{
|
CounterOpts{
|
||||||
|
@ -320,7 +270,7 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeApproximateRequestSize(r *http.Request) <-chan int {
|
func computeApproximateRequestSize(r *http.Request) <-chan int {
|
||||||
// Get URL length in current go routine for avoiding a race condition.
|
// Get URL length in current goroutine for avoiding a race condition.
|
||||||
// HandlerFunc that runs in parallel may modify the URL.
|
// HandlerFunc that runs in parallel may modify the URL.
|
||||||
s := 0
|
s := 0
|
||||||
if r.URL != nil {
|
if r.URL != nil {
|
||||||
|
@ -355,10 +305,9 @@ func computeApproximateRequestSize(r *http.Request) <-chan int {
|
||||||
type responseWriterDelegator struct {
|
type responseWriterDelegator struct {
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
|
|
||||||
handler, method string
|
status int
|
||||||
status int
|
written int64
|
||||||
written int64
|
wroteHeader bool
|
||||||
wroteHeader bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *responseWriterDelegator) WriteHeader(code int) {
|
func (r *responseWriterDelegator) WriteHeader(code int) {
|
||||||
|
@ -381,6 +330,8 @@ type fancyResponseWriterDelegator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
|
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
|
||||||
|
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||||
|
//remove support from client_golang yet.
|
||||||
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,3 +475,31 @@ func sanitizeCode(s int) string {
|
||||||
return strconv.Itoa(s)
|
return strconv.Itoa(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gzipAccepted returns whether the client will accept gzip-encoded content.
|
||||||
|
func gzipAccepted(header http.Header) bool {
|
||||||
|
a := header.Get(acceptEncodingHeader)
|
||||||
|
parts := strings.Split(a, ",")
|
||||||
|
for _, part := range parts {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpError removes any content-encoding header and then calls http.Error with
|
||||||
|
// the provided error and http.StatusInternalServerErrer. Error contents is
|
||||||
|
// supposed to be uncompressed plain text. However, same as with a plain
|
||||||
|
// http.Error, any header settings will be void if the header has already been
|
||||||
|
// sent. The error message will still be written to the writer, but it will
|
||||||
|
// probably be of limited use.
|
||||||
|
func httpError(rsp http.ResponseWriter, err error) {
|
||||||
|
rsp.Header().Del(contentEncodingHeader)
|
||||||
|
http.Error(
|
||||||
|
rsp,
|
||||||
|
"An error has occurred while serving metrics:\n\n"+err.Error(),
|
||||||
|
http.StatusInternalServerError,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
85
vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go
generated
vendored
Normal file
85
vendor/github.com/prometheus/client_golang/prometheus/internal/metric.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// metricSorter is a sortable slice of *dto.Metric.
|
||||||
|
type metricSorter []*dto.Metric
|
||||||
|
|
||||||
|
func (s metricSorter) Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s metricSorter) Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s metricSorter) Less(i, j int) bool {
|
||||||
|
if len(s[i].Label) != len(s[j].Label) {
|
||||||
|
// This should not happen. The metrics are
|
||||||
|
// inconsistent. However, we have to deal with the fact, as
|
||||||
|
// people might use custom collectors or metric family injection
|
||||||
|
// to create inconsistent metrics. So let's simply compare the
|
||||||
|
// number of labels in this case. That will still yield
|
||||||
|
// reproducible sorting.
|
||||||
|
return len(s[i].Label) < len(s[j].Label)
|
||||||
|
}
|
||||||
|
for n, lp := range s[i].Label {
|
||||||
|
vi := lp.GetValue()
|
||||||
|
vj := s[j].Label[n].GetValue()
|
||||||
|
if vi != vj {
|
||||||
|
return vi < vj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should never arrive here. Multiple metrics with the same
|
||||||
|
// label set in the same scrape will lead to undefined ingestion
|
||||||
|
// behavior. However, as above, we have to provide stable sorting
|
||||||
|
// here, even for inconsistent metrics. So sort equal metrics
|
||||||
|
// by their timestamp, with missing timestamps (implying "now")
|
||||||
|
// coming last.
|
||||||
|
if s[i].TimestampMs == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if s[j].TimestampMs == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return s[i].GetTimestampMs() < s[j].GetTimestampMs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeMetricFamilies returns a MetricFamily slice with empty
|
||||||
|
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
|
||||||
|
// the slice, with the contained Metrics sorted within each MetricFamily.
|
||||||
|
func NormalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily {
|
||||||
|
for _, mf := range metricFamiliesByName {
|
||||||
|
sort.Sort(metricSorter(mf.Metric))
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(metricFamiliesByName))
|
||||||
|
for name, mf := range metricFamiliesByName {
|
||||||
|
if len(mf.Metric) > 0 {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
result := make([]*dto.MetricFamily, 0, len(names))
|
||||||
|
for _, name := range names {
|
||||||
|
result = append(result, metricFamiliesByName[name])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
87
vendor/github.com/prometheus/client_golang/prometheus/labels.go
generated
vendored
Normal file
87
vendor/github.com/prometheus/client_golang/prometheus/labels.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Labels represents a collection of label name -> value mappings. This type is
|
||||||
|
// commonly used with the With(Labels) and GetMetricWith(Labels) methods of
|
||||||
|
// metric vector Collectors, e.g.:
|
||||||
|
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
||||||
|
//
|
||||||
|
// The other use-case is the specification of constant label pairs in Opts or to
|
||||||
|
// create a Desc.
|
||||||
|
type Labels map[string]string
|
||||||
|
|
||||||
|
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||||
|
// label names.
|
||||||
|
const reservedLabelPrefix = "__"
|
||||||
|
|
||||||
|
var errInconsistentCardinality = errors.New("inconsistent label cardinality")
|
||||||
|
|
||||||
|
func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: %q has %d variable labels named %q but %d values %q were provided",
|
||||||
|
errInconsistentCardinality, fqName,
|
||||||
|
len(labels), labels,
|
||||||
|
len(labelValues), labelValues,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
|
||||||
|
if len(labels) != expectedNumberOfValues {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: expected %d label values but got %d in %#v",
|
||||||
|
errInconsistentCardinality, expectedNumberOfValues,
|
||||||
|
len(labels), labels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, val := range labels {
|
||||||
|
if !utf8.ValidString(val) {
|
||||||
|
return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLabelValues(vals []string, expectedNumberOfValues int) error {
|
||||||
|
if len(vals) != expectedNumberOfValues {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%s: expected %d label values but got %d in %#v",
|
||||||
|
errInconsistentCardinality, expectedNumberOfValues,
|
||||||
|
len(vals), vals,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, val := range vals {
|
||||||
|
if !utf8.ValidString(val) {
|
||||||
|
return fmt.Errorf("label value %q is not valid UTF-8", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLabelName(l string) bool {
|
||||||
|
return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix)
|
||||||
|
}
|
90
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
90
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
|
@ -15,6 +15,9 @@ package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
@ -43,9 +46,8 @@ type Metric interface {
|
||||||
// While populating dto.Metric, it is the responsibility of the
|
// While populating dto.Metric, it is the responsibility of the
|
||||||
// implementation to ensure validity of the Metric protobuf (like valid
|
// implementation to ensure validity of the Metric protobuf (like valid
|
||||||
// UTF-8 strings or syntactically valid metric and label names). It is
|
// UTF-8 strings or syntactically valid metric and label names). It is
|
||||||
// recommended to sort labels lexicographically. (Implementers may find
|
// recommended to sort labels lexicographically. Callers of Write should
|
||||||
// LabelPairSorter useful for that.) Callers of Write should still make
|
// still make sure of sorting if they depend on it.
|
||||||
// sure of sorting if they depend on it.
|
|
||||||
Write(*dto.Metric) error
|
Write(*dto.Metric) error
|
||||||
// TODO(beorn7): The original rationale of passing in a pre-allocated
|
// TODO(beorn7): The original rationale of passing in a pre-allocated
|
||||||
// dto.Metric protobuf to save allocations has disappeared. The
|
// dto.Metric protobuf to save allocations has disappeared. The
|
||||||
|
@ -57,8 +59,9 @@ type Metric interface {
|
||||||
// implementation XXX has its own XXXOpts type, but in most cases, it is just be
|
// implementation XXX has its own XXXOpts type, but in most cases, it is just be
|
||||||
// an alias of this type (which might change when the requirement arises.)
|
// an alias of this type (which might change when the requirement arises.)
|
||||||
//
|
//
|
||||||
// It is mandatory to set Name and Help to a non-empty string. All other fields
|
// It is mandatory to set Name to a non-empty string. All other fields are
|
||||||
// are optional and can safely be left at their zero value.
|
// optional and can safely be left at their zero value, although it is strongly
|
||||||
|
// encouraged to set a Help string.
|
||||||
type Opts struct {
|
type Opts struct {
|
||||||
// Namespace, Subsystem, and Name are components of the fully-qualified
|
// Namespace, Subsystem, and Name are components of the fully-qualified
|
||||||
// name of the Metric (created by joining these components with
|
// name of the Metric (created by joining these components with
|
||||||
|
@ -69,7 +72,7 @@ type Opts struct {
|
||||||
Subsystem string
|
Subsystem string
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// Help provides information about this metric. Mandatory!
|
// Help provides information about this metric.
|
||||||
//
|
//
|
||||||
// Metrics with the same fully-qualified name must have the same Help
|
// Metrics with the same fully-qualified name must have the same Help
|
||||||
// string.
|
// string.
|
||||||
|
@ -79,20 +82,12 @@ type Opts struct {
|
||||||
// with the same fully-qualified name must have the same label names in
|
// with the same fully-qualified name must have the same label names in
|
||||||
// their ConstLabels.
|
// their ConstLabels.
|
||||||
//
|
//
|
||||||
// Note that in most cases, labels have a value that varies during the
|
// ConstLabels are only used rarely. In particular, do not use them to
|
||||||
// lifetime of a process. Those labels are usually managed with a metric
|
// attach the same labels to all your metrics. Those use cases are
|
||||||
// vector collector (like CounterVec, GaugeVec, UntypedVec). ConstLabels
|
// better covered by target labels set by the scraping Prometheus
|
||||||
// serve only special purposes. One is for the special case where the
|
// server, or by one specific metric (e.g. a build_info or a
|
||||||
// value of a label does not change during the lifetime of a process,
|
// machine_role metric). See also
|
||||||
// e.g. if the revision of the running binary is put into a
|
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||||
// label. Another, more advanced purpose is if more than one Collector
|
|
||||||
// needs to collect Metrics with the same fully-qualified name. In that
|
|
||||||
// case, those Metrics must differ in the values of their
|
|
||||||
// ConstLabels. See the Collector examples.
|
|
||||||
//
|
|
||||||
// If the value of a label never changes (not even between binaries),
|
|
||||||
// that label most likely should not be a label at all (but part of the
|
|
||||||
// metric name).
|
|
||||||
ConstLabels Labels
|
ConstLabels Labels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,37 +113,22 @@ func BuildFQName(namespace, subsystem, name string) string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
// LabelPairSorter implements sort.Interface. It is used to sort a slice of
|
// labelPairSorter implements sort.Interface. It is used to sort a slice of
|
||||||
// dto.LabelPair pointers. This is useful for implementing the Write method of
|
// dto.LabelPair pointers.
|
||||||
// custom metrics.
|
type labelPairSorter []*dto.LabelPair
|
||||||
type LabelPairSorter []*dto.LabelPair
|
|
||||||
|
|
||||||
func (s LabelPairSorter) Len() int {
|
func (s labelPairSorter) Len() int {
|
||||||
return len(s)
|
return len(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s LabelPairSorter) Swap(i, j int) {
|
func (s labelPairSorter) Swap(i, j int) {
|
||||||
s[i], s[j] = s[j], s[i]
|
s[i], s[j] = s[j], s[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s LabelPairSorter) Less(i, j int) bool {
|
func (s labelPairSorter) Less(i, j int) bool {
|
||||||
return s[i].GetName() < s[j].GetName()
|
return s[i].GetName() < s[j].GetName()
|
||||||
}
|
}
|
||||||
|
|
||||||
type hashSorter []uint64
|
|
||||||
|
|
||||||
func (s hashSorter) Len() int {
|
|
||||||
return len(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s hashSorter) Swap(i, j int) {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s hashSorter) Less(i, j int) bool {
|
|
||||||
return s[i] < s[j]
|
|
||||||
}
|
|
||||||
|
|
||||||
type invalidMetric struct {
|
type invalidMetric struct {
|
||||||
desc *Desc
|
desc *Desc
|
||||||
err error
|
err error
|
||||||
|
@ -164,3 +144,31 @@ func NewInvalidMetric(desc *Desc, err error) Metric {
|
||||||
func (m *invalidMetric) Desc() *Desc { return m.desc }
|
func (m *invalidMetric) Desc() *Desc { return m.desc }
|
||||||
|
|
||||||
func (m *invalidMetric) Write(*dto.Metric) error { return m.err }
|
func (m *invalidMetric) Write(*dto.Metric) error { return m.err }
|
||||||
|
|
||||||
|
type timestampedMetric struct {
|
||||||
|
Metric
|
||||||
|
t time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m timestampedMetric) Write(pb *dto.Metric) error {
|
||||||
|
e := m.Metric.Write(pb)
|
||||||
|
pb.TimestampMs = proto.Int64(m.t.Unix()*1000 + int64(m.t.Nanosecond()/1000000))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetricWithTimestamp returns a new Metric wrapping the provided Metric in a
|
||||||
|
// way that it has an explicit timestamp set to the provided Time. This is only
|
||||||
|
// useful in rare cases as the timestamp of a Prometheus metric should usually
|
||||||
|
// be set by the Prometheus server during scraping. Exceptions include mirroring
|
||||||
|
// metrics with given timestamps from other metric
|
||||||
|
// sources.
|
||||||
|
//
|
||||||
|
// NewMetricWithTimestamp works best with MustNewConstMetric,
|
||||||
|
// MustNewConstHistogram, and MustNewConstSummary, see example.
|
||||||
|
//
|
||||||
|
// Currently, the exposition formats used by Prometheus are limited to
|
||||||
|
// millisecond resolution. Thus, the provided time will be rounded down to the
|
||||||
|
// next full millisecond value.
|
||||||
|
func NewMetricWithTimestamp(t time.Time, m Metric) Metric {
|
||||||
|
return timestampedMetric{Metric: m, t: t}
|
||||||
|
}
|
||||||
|
|
2
vendor/github.com/prometheus/client_golang/prometheus/observer.go
generated
vendored
2
vendor/github.com/prometheus/client_golang/prometheus/observer.go
generated
vendored
|
@ -45,6 +45,8 @@ type ObserverVec interface {
|
||||||
GetMetricWithLabelValues(lvs ...string) (Observer, error)
|
GetMetricWithLabelValues(lvs ...string) (Observer, error)
|
||||||
With(Labels) Observer
|
With(Labels) Observer
|
||||||
WithLabelValues(...string) Observer
|
WithLabelValues(...string) Observer
|
||||||
|
CurryWith(Labels) (ObserverVec, error)
|
||||||
|
MustCurryWith(Labels) ObserverVec
|
||||||
|
|
||||||
Collector
|
Collector
|
||||||
}
|
}
|
||||||
|
|
122
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
122
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
|
@ -13,46 +13,74 @@
|
||||||
|
|
||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
import "github.com/prometheus/procfs"
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/prometheus/procfs"
|
||||||
|
)
|
||||||
|
|
||||||
type processCollector struct {
|
type processCollector struct {
|
||||||
pid int
|
|
||||||
collectFn func(chan<- Metric)
|
collectFn func(chan<- Metric)
|
||||||
pidFn func() (int, error)
|
pidFn func() (int, error)
|
||||||
|
reportErrors bool
|
||||||
cpuTotal *Desc
|
cpuTotal *Desc
|
||||||
openFDs, maxFDs *Desc
|
openFDs, maxFDs *Desc
|
||||||
vsize, rss *Desc
|
vsize, maxVsize *Desc
|
||||||
|
rss *Desc
|
||||||
startTime *Desc
|
startTime *Desc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProcessCollector returns a collector which exports the current state of
|
// ProcessCollectorOpts defines the behavior of a process metrics collector
|
||||||
// process metrics including cpu, memory and file descriptor usage as well as
|
// created with NewProcessCollector.
|
||||||
// the process start time for the given process id under the given namespace.
|
type ProcessCollectorOpts struct {
|
||||||
func NewProcessCollector(pid int, namespace string) Collector {
|
// PidFn returns the PID of the process the collector collects metrics
|
||||||
return NewProcessCollectorPIDFn(
|
// for. It is called upon each collection. By default, the PID of the
|
||||||
func() (int, error) { return pid, nil },
|
// current process is used, as determined on construction time by
|
||||||
namespace,
|
// calling os.Getpid().
|
||||||
)
|
PidFn func() (int, error)
|
||||||
|
// If non-empty, each of the collected metrics is prefixed by the
|
||||||
|
// provided string and an underscore ("_").
|
||||||
|
Namespace string
|
||||||
|
// If true, any error encountered during collection is reported as an
|
||||||
|
// invalid metric (see NewInvalidMetric). Otherwise, errors are ignored
|
||||||
|
// and the collected metrics will be incomplete. (Possibly, no metrics
|
||||||
|
// will be collected at all.) While that's usually not desired, it is
|
||||||
|
// appropriate for the common "mix-in" of process metrics, where process
|
||||||
|
// metrics are nice to have, but failing to collect them should not
|
||||||
|
// disrupt the collection of the remaining metrics.
|
||||||
|
ReportErrors bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProcessCollectorPIDFn returns a collector which exports the current state
|
// NewProcessCollector returns a collector which exports the current state of
|
||||||
// of process metrics including cpu, memory and file descriptor usage as well
|
// process metrics including CPU, memory and file descriptor usage as well as
|
||||||
// as the process start time under the given namespace. The given pidFn is
|
// the process start time. The detailed behavior is defined by the provided
|
||||||
// called on each collect and is used to determine the process to export
|
// ProcessCollectorOpts. The zero value of ProcessCollectorOpts creates a
|
||||||
// metrics for.
|
// collector for the current process with an empty namespace string and no error
|
||||||
func NewProcessCollectorPIDFn(
|
// reporting.
|
||||||
pidFn func() (int, error),
|
//
|
||||||
namespace string,
|
// Currently, the collector depends on a Linux-style proc filesystem and
|
||||||
) Collector {
|
// therefore only exports metrics for Linux.
|
||||||
|
//
|
||||||
|
// Note: An older version of this function had the following signature:
|
||||||
|
//
|
||||||
|
// NewProcessCollector(pid int, namespace string) Collector
|
||||||
|
//
|
||||||
|
// Most commonly, it was called as
|
||||||
|
//
|
||||||
|
// NewProcessCollector(os.Getpid(), "")
|
||||||
|
//
|
||||||
|
// The following call of the current version is equivalent to the above:
|
||||||
|
//
|
||||||
|
// NewProcessCollector(ProcessCollectorOpts{})
|
||||||
|
func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
||||||
ns := ""
|
ns := ""
|
||||||
if len(namespace) > 0 {
|
if len(opts.Namespace) > 0 {
|
||||||
ns = namespace + "_"
|
ns = opts.Namespace + "_"
|
||||||
}
|
}
|
||||||
|
|
||||||
c := processCollector{
|
c := &processCollector{
|
||||||
pidFn: pidFn,
|
reportErrors: opts.ReportErrors,
|
||||||
collectFn: func(chan<- Metric) {},
|
|
||||||
|
|
||||||
cpuTotal: NewDesc(
|
cpuTotal: NewDesc(
|
||||||
ns+"process_cpu_seconds_total",
|
ns+"process_cpu_seconds_total",
|
||||||
"Total user and system CPU time spent in seconds.",
|
"Total user and system CPU time spent in seconds.",
|
||||||
|
@ -73,6 +101,11 @@ func NewProcessCollectorPIDFn(
|
||||||
"Virtual memory size in bytes.",
|
"Virtual memory size in bytes.",
|
||||||
nil, nil,
|
nil, nil,
|
||||||
),
|
),
|
||||||
|
maxVsize: NewDesc(
|
||||||
|
ns+"process_virtual_memory_max_bytes",
|
||||||
|
"Maximum amount of virtual memory available in bytes.",
|
||||||
|
nil, nil,
|
||||||
|
),
|
||||||
rss: NewDesc(
|
rss: NewDesc(
|
||||||
ns+"process_resident_memory_bytes",
|
ns+"process_resident_memory_bytes",
|
||||||
"Resident memory size in bytes.",
|
"Resident memory size in bytes.",
|
||||||
|
@ -85,12 +118,23 @@ func NewProcessCollectorPIDFn(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.PidFn == nil {
|
||||||
|
pid := os.Getpid()
|
||||||
|
c.pidFn = func() (int, error) { return pid, nil }
|
||||||
|
} else {
|
||||||
|
c.pidFn = opts.PidFn
|
||||||
|
}
|
||||||
|
|
||||||
// Set up process metric collection if supported by the runtime.
|
// Set up process metric collection if supported by the runtime.
|
||||||
if _, err := procfs.NewStat(); err == nil {
|
if _, err := procfs.NewStat(); err == nil {
|
||||||
c.collectFn = c.processCollect
|
c.collectFn = c.processCollect
|
||||||
|
} else {
|
||||||
|
c.collectFn = func(ch chan<- Metric) {
|
||||||
|
c.reportError(ch, nil, errors.New("process metrics not supported on this platform"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe returns all descriptions of the collector.
|
// Describe returns all descriptions of the collector.
|
||||||
|
@ -99,6 +143,7 @@ func (c *processCollector) Describe(ch chan<- *Desc) {
|
||||||
ch <- c.openFDs
|
ch <- c.openFDs
|
||||||
ch <- c.maxFDs
|
ch <- c.maxFDs
|
||||||
ch <- c.vsize
|
ch <- c.vsize
|
||||||
|
ch <- c.maxVsize
|
||||||
ch <- c.rss
|
ch <- c.rss
|
||||||
ch <- c.startTime
|
ch <- c.startTime
|
||||||
}
|
}
|
||||||
|
@ -108,16 +153,16 @@ func (c *processCollector) Collect(ch chan<- Metric) {
|
||||||
c.collectFn(ch)
|
c.collectFn(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ts): Bring back error reporting by reverting 7faf9e7 as soon as the
|
|
||||||
// client allows users to configure the error behavior.
|
|
||||||
func (c *processCollector) processCollect(ch chan<- Metric) {
|
func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||||
pid, err := c.pidFn()
|
pid, err := c.pidFn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.reportError(ch, nil, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := procfs.NewProc(pid)
|
p, err := procfs.NewProc(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.reportError(ch, nil, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,14 +172,33 @@ func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
|
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
|
||||||
if startTime, err := stat.StartTime(); err == nil {
|
if startTime, err := stat.StartTime(); err == nil {
|
||||||
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
|
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
|
||||||
|
} else {
|
||||||
|
c.reportError(ch, c.startTime, err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
c.reportError(ch, nil, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fds, err := p.FileDescriptorsLen(); err == nil {
|
if fds, err := p.FileDescriptorsLen(); err == nil {
|
||||||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
|
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
|
||||||
|
} else {
|
||||||
|
c.reportError(ch, c.openFDs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if limits, err := p.NewLimits(); err == nil {
|
if limits, err := p.NewLimits(); err == nil {
|
||||||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
|
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
|
||||||
|
ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace))
|
||||||
|
} else {
|
||||||
|
c.reportError(ch, nil, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
|
||||||
|
if !c.reportErrors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if desc == nil {
|
||||||
|
desc = NewInvalidDesc(err)
|
||||||
|
}
|
||||||
|
ch <- NewInvalidMetric(desc, err)
|
||||||
|
}
|
||||||
|
|
678
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
678
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
|
@ -15,15 +15,22 @@ package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,13 +42,14 @@ const (
|
||||||
// DefaultRegisterer and DefaultGatherer are the implementations of the
|
// DefaultRegisterer and DefaultGatherer are the implementations of the
|
||||||
// Registerer and Gatherer interface a number of convenience functions in this
|
// Registerer and Gatherer interface a number of convenience functions in this
|
||||||
// package act on. Initially, both variables point to the same Registry, which
|
// package act on. Initially, both variables point to the same Registry, which
|
||||||
// has a process collector (see NewProcessCollector) and a Go collector (see
|
// has a process collector (currently on Linux only, see NewProcessCollector)
|
||||||
// NewGoCollector) already registered. This approach to keep default instances
|
// and a Go collector (see NewGoCollector, in particular the note about
|
||||||
// as global state mirrors the approach of other packages in the Go standard
|
// stop-the-world implication with Go versions older than 1.9) already
|
||||||
// library. Note that there are caveats. Change the variables with caution and
|
// registered. This approach to keep default instances as global state mirrors
|
||||||
// only if you understand the consequences. Users who want to avoid global state
|
// the approach of other packages in the Go standard library. Note that there
|
||||||
// altogether should not use the convenience function and act on custom
|
// are caveats. Change the variables with caution and only if you understand the
|
||||||
// instances instead.
|
// consequences. Users who want to avoid global state altogether should not use
|
||||||
|
// the convenience functions and act on custom instances instead.
|
||||||
var (
|
var (
|
||||||
defaultRegistry = NewRegistry()
|
defaultRegistry = NewRegistry()
|
||||||
DefaultRegisterer Registerer = defaultRegistry
|
DefaultRegisterer Registerer = defaultRegistry
|
||||||
|
@ -49,7 +57,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
MustRegister(NewProcessCollector(os.Getpid(), ""))
|
MustRegister(NewProcessCollector(ProcessCollectorOpts{}))
|
||||||
MustRegister(NewGoCollector())
|
MustRegister(NewGoCollector())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +73,8 @@ func NewRegistry() *Registry {
|
||||||
|
|
||||||
// NewPedanticRegistry returns a registry that checks during collection if each
|
// NewPedanticRegistry returns a registry that checks during collection if each
|
||||||
// collected Metric is consistent with its reported Desc, and if the Desc has
|
// collected Metric is consistent with its reported Desc, and if the Desc has
|
||||||
// actually been registered with the registry.
|
// actually been registered with the registry. Unchecked Collectors (those whose
|
||||||
|
// Describe methed does not yield any descriptors) are excluded from the check.
|
||||||
//
|
//
|
||||||
// Usually, a Registry will be happy as long as the union of all collected
|
// Usually, a Registry will be happy as long as the union of all collected
|
||||||
// Metrics is consistent and valid even if some metrics are not consistent with
|
// Metrics is consistent and valid even if some metrics are not consistent with
|
||||||
|
@ -80,7 +89,7 @@ func NewPedanticRegistry() *Registry {
|
||||||
|
|
||||||
// Registerer is the interface for the part of a registry in charge of
|
// Registerer is the interface for the part of a registry in charge of
|
||||||
// registering and unregistering. Users of custom registries should use
|
// registering and unregistering. Users of custom registries should use
|
||||||
// Registerer as type for registration purposes (rather then the Registry type
|
// Registerer as type for registration purposes (rather than the Registry type
|
||||||
// directly). In that way, they are free to use custom Registerer implementation
|
// directly). In that way, they are free to use custom Registerer implementation
|
||||||
// (e.g. for testing purposes).
|
// (e.g. for testing purposes).
|
||||||
type Registerer interface {
|
type Registerer interface {
|
||||||
|
@ -95,8 +104,13 @@ type Registerer interface {
|
||||||
// returned error is an instance of AlreadyRegisteredError, which
|
// returned error is an instance of AlreadyRegisteredError, which
|
||||||
// contains the previously registered Collector.
|
// contains the previously registered Collector.
|
||||||
//
|
//
|
||||||
// It is in general not safe to register the same Collector multiple
|
// A Collector whose Describe method does not yield any Desc is treated
|
||||||
// times concurrently.
|
// as unchecked. Registration will always succeed. No check for
|
||||||
|
// re-registering (see previous paragraph) is performed. Thus, the
|
||||||
|
// caller is responsible for not double-registering the same unchecked
|
||||||
|
// Collector, and for providing a Collector that will not cause
|
||||||
|
// inconsistent metrics on collection. (This would lead to scrape
|
||||||
|
// errors.)
|
||||||
Register(Collector) error
|
Register(Collector) error
|
||||||
// MustRegister works like Register but registers any number of
|
// MustRegister works like Register but registers any number of
|
||||||
// Collectors and panics upon the first registration that causes an
|
// Collectors and panics upon the first registration that causes an
|
||||||
|
@ -105,7 +119,9 @@ type Registerer interface {
|
||||||
// Unregister unregisters the Collector that equals the Collector passed
|
// Unregister unregisters the Collector that equals the Collector passed
|
||||||
// in as an argument. (Two Collectors are considered equal if their
|
// in as an argument. (Two Collectors are considered equal if their
|
||||||
// Describe method yields the same set of descriptors.) The function
|
// Describe method yields the same set of descriptors.) The function
|
||||||
// returns whether a Collector was unregistered.
|
// returns whether a Collector was unregistered. Note that an unchecked
|
||||||
|
// Collector cannot be unregistered (as its Describe method does not
|
||||||
|
// yield any descriptor).
|
||||||
//
|
//
|
||||||
// Note that even after unregistering, it will not be possible to
|
// Note that even after unregistering, it will not be possible to
|
||||||
// register a new Collector that is inconsistent with the unregistered
|
// register a new Collector that is inconsistent with the unregistered
|
||||||
|
@ -123,15 +139,23 @@ type Registerer interface {
|
||||||
type Gatherer interface {
|
type Gatherer interface {
|
||||||
// Gather calls the Collect method of the registered Collectors and then
|
// Gather calls the Collect method of the registered Collectors and then
|
||||||
// gathers the collected metrics into a lexicographically sorted slice
|
// gathers the collected metrics into a lexicographically sorted slice
|
||||||
// of MetricFamily protobufs. Even if an error occurs, Gather attempts
|
// of uniquely named MetricFamily protobufs. Gather ensures that the
|
||||||
// to gather as many metrics as possible. Hence, if a non-nil error is
|
// returned slice is valid and self-consistent so that it can be used
|
||||||
// returned, the returned MetricFamily slice could be nil (in case of a
|
// for valid exposition. As an exception to the strict consistency
|
||||||
// fatal error that prevented any meaningful metric collection) or
|
// requirements described for metric.Desc, Gather will tolerate
|
||||||
// contain a number of MetricFamily protobufs, some of which might be
|
// different sets of label names for metrics of the same metric family.
|
||||||
// incomplete, and some might be missing altogether. The returned error
|
//
|
||||||
// (which might be a MultiError) explains the details. In scenarios
|
// Even if an error occurs, Gather attempts to gather as many metrics as
|
||||||
// where complete collection is critical, the returned MetricFamily
|
// possible. Hence, if a non-nil error is returned, the returned
|
||||||
// protobufs should be disregarded if the returned error is non-nil.
|
// MetricFamily slice could be nil (in case of a fatal error that
|
||||||
|
// prevented any meaningful metric collection) or contain a number of
|
||||||
|
// MetricFamily protobufs, some of which might be incomplete, and some
|
||||||
|
// might be missing altogether. The returned error (which might be a
|
||||||
|
// MultiError) explains the details. Note that this is mostly useful for
|
||||||
|
// debugging purposes. If the gathered protobufs are to be used for
|
||||||
|
// exposition in actual monitoring, it is almost always better to not
|
||||||
|
// expose an incomplete result and instead disregard the returned
|
||||||
|
// MetricFamily protobufs in case the returned error is non-nil.
|
||||||
Gather() ([]*dto.MetricFamily, error)
|
Gather() ([]*dto.MetricFamily, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +225,13 @@ func (errs MultiError) Error() string {
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append appends the provided error if it is not nil.
|
||||||
|
func (errs *MultiError) Append(err error) {
|
||||||
|
if err != nil {
|
||||||
|
*errs = append(*errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
|
// MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
|
||||||
// contained error as error if len(errs is 1). In all other cases, it returns
|
// contained error as error if len(errs is 1). In all other cases, it returns
|
||||||
// the MultiError directly. This is helpful for returning a MultiError in a way
|
// the MultiError directly. This is helpful for returning a MultiError in a way
|
||||||
|
@ -225,6 +256,7 @@ type Registry struct {
|
||||||
collectorsByID map[uint64]Collector // ID is a hash of the descIDs.
|
collectorsByID map[uint64]Collector // ID is a hash of the descIDs.
|
||||||
descIDs map[uint64]struct{}
|
descIDs map[uint64]struct{}
|
||||||
dimHashesByName map[string]uint64
|
dimHashesByName map[string]uint64
|
||||||
|
uncheckedCollectors []Collector
|
||||||
pedanticChecksEnabled bool
|
pedanticChecksEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +274,12 @@ func (r *Registry) Register(c Collector) error {
|
||||||
close(descChan)
|
close(descChan)
|
||||||
}()
|
}()
|
||||||
r.mtx.Lock()
|
r.mtx.Lock()
|
||||||
defer r.mtx.Unlock()
|
defer func() {
|
||||||
|
// Drain channel in case of premature return to not leak a goroutine.
|
||||||
|
for range descChan {
|
||||||
|
}
|
||||||
|
r.mtx.Unlock()
|
||||||
|
}()
|
||||||
// Conduct various tests...
|
// Conduct various tests...
|
||||||
for desc := range descChan {
|
for desc := range descChan {
|
||||||
|
|
||||||
|
@ -282,9 +319,10 @@ func (r *Registry) Register(c Collector) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Did anything happen at all?
|
// A Collector yielding no Desc at all is considered unchecked.
|
||||||
if len(newDescIDs) == 0 {
|
if len(newDescIDs) == 0 {
|
||||||
return errors.New("collector has no descriptors")
|
r.uncheckedCollectors = append(r.uncheckedCollectors, c)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if existing, exists := r.collectorsByID[collectorID]; exists {
|
if existing, exists := r.collectorsByID[collectorID]; exists {
|
||||||
return AlreadyRegisteredError{
|
return AlreadyRegisteredError{
|
||||||
|
@ -358,31 +396,25 @@ func (r *Registry) MustRegister(cs ...Collector) {
|
||||||
// Gather implements Gatherer.
|
// Gather implements Gatherer.
|
||||||
func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||||
var (
|
var (
|
||||||
metricChan = make(chan Metric, capMetricChan)
|
checkedMetricChan = make(chan Metric, capMetricChan)
|
||||||
metricHashes = map[uint64]struct{}{}
|
uncheckedMetricChan = make(chan Metric, capMetricChan)
|
||||||
dimHashes = map[string]uint64{}
|
metricHashes = map[uint64]struct{}{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
errs MultiError // The collected errors to return in the end.
|
errs MultiError // The collected errors to return in the end.
|
||||||
registeredDescIDs map[uint64]struct{} // Only used for pedantic checks
|
registeredDescIDs map[uint64]struct{} // Only used for pedantic checks
|
||||||
)
|
)
|
||||||
|
|
||||||
r.mtx.RLock()
|
r.mtx.RLock()
|
||||||
|
goroutineBudget := len(r.collectorsByID) + len(r.uncheckedCollectors)
|
||||||
metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
|
metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
|
||||||
|
checkedCollectors := make(chan Collector, len(r.collectorsByID))
|
||||||
// Scatter.
|
uncheckedCollectors := make(chan Collector, len(r.uncheckedCollectors))
|
||||||
// (Collectors could be complex and slow, so we call them all at once.)
|
|
||||||
wg.Add(len(r.collectorsByID))
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(metricChan)
|
|
||||||
}()
|
|
||||||
for _, collector := range r.collectorsByID {
|
for _, collector := range r.collectorsByID {
|
||||||
go func(collector Collector) {
|
checkedCollectors <- collector
|
||||||
defer wg.Done()
|
}
|
||||||
collector.Collect(metricChan)
|
for _, collector := range r.uncheckedCollectors {
|
||||||
}(collector)
|
uncheckedCollectors <- collector
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case pedantic checks are enabled, we have to copy the map before
|
// In case pedantic checks are enabled, we have to copy the map before
|
||||||
// giving up the RLock.
|
// giving up the RLock.
|
||||||
if r.pedanticChecksEnabled {
|
if r.pedanticChecksEnabled {
|
||||||
|
@ -391,133 +423,264 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||||
registeredDescIDs[id] = struct{}{}
|
registeredDescIDs[id] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.mtx.RUnlock()
|
r.mtx.RUnlock()
|
||||||
|
|
||||||
// Drain metricChan in case of premature return.
|
wg.Add(goroutineBudget)
|
||||||
|
|
||||||
|
collectWorker := func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case collector := <-checkedCollectors:
|
||||||
|
collector.Collect(checkedMetricChan)
|
||||||
|
case collector := <-uncheckedCollectors:
|
||||||
|
collector.Collect(uncheckedMetricChan)
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the first worker now to make sure at least one is running.
|
||||||
|
go collectWorker()
|
||||||
|
goroutineBudget--
|
||||||
|
|
||||||
|
// Close checkedMetricChan and uncheckedMetricChan once all collectors
|
||||||
|
// are collected.
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(checkedMetricChan)
|
||||||
|
close(uncheckedMetricChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Drain checkedMetricChan and uncheckedMetricChan in case of premature return.
|
||||||
defer func() {
|
defer func() {
|
||||||
for range metricChan {
|
if checkedMetricChan != nil {
|
||||||
|
for range checkedMetricChan {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if uncheckedMetricChan != nil {
|
||||||
|
for range uncheckedMetricChan {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Gather.
|
// Copy the channel references so we can nil them out later to remove
|
||||||
for metric := range metricChan {
|
// them from the select statements below.
|
||||||
// This could be done concurrently, too, but it required locking
|
cmc := checkedMetricChan
|
||||||
// of metricFamiliesByName (and of metricHashes if checks are
|
umc := uncheckedMetricChan
|
||||||
// enabled). Most likely not worth it.
|
|
||||||
desc := metric.Desc()
|
for {
|
||||||
dtoMetric := &dto.Metric{}
|
select {
|
||||||
if err := metric.Write(dtoMetric); err != nil {
|
case metric, ok := <-cmc:
|
||||||
errs = append(errs, fmt.Errorf(
|
if !ok {
|
||||||
"error collecting metric %v: %s", desc, err,
|
cmc = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
errs.Append(processMetric(
|
||||||
|
metric, metricFamiliesByName,
|
||||||
|
metricHashes,
|
||||||
|
registeredDescIDs,
|
||||||
))
|
))
|
||||||
continue
|
case metric, ok := <-umc:
|
||||||
|
if !ok {
|
||||||
|
umc = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
errs.Append(processMetric(
|
||||||
|
metric, metricFamiliesByName,
|
||||||
|
metricHashes,
|
||||||
|
nil,
|
||||||
|
))
|
||||||
|
default:
|
||||||
|
if goroutineBudget <= 0 || len(checkedCollectors)+len(uncheckedCollectors) == 0 {
|
||||||
|
// All collectors are already being worked on or
|
||||||
|
// we have already as many goroutines started as
|
||||||
|
// there are collectors. Do the same as above,
|
||||||
|
// just without the default.
|
||||||
|
select {
|
||||||
|
case metric, ok := <-cmc:
|
||||||
|
if !ok {
|
||||||
|
cmc = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
errs.Append(processMetric(
|
||||||
|
metric, metricFamiliesByName,
|
||||||
|
metricHashes,
|
||||||
|
registeredDescIDs,
|
||||||
|
))
|
||||||
|
case metric, ok := <-umc:
|
||||||
|
if !ok {
|
||||||
|
umc = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
errs.Append(processMetric(
|
||||||
|
metric, metricFamiliesByName,
|
||||||
|
metricHashes,
|
||||||
|
nil,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Start more workers.
|
||||||
|
go collectWorker()
|
||||||
|
goroutineBudget--
|
||||||
|
runtime.Gosched()
|
||||||
}
|
}
|
||||||
metricFamily, ok := metricFamiliesByName[desc.fqName]
|
// Once both checkedMetricChan and uncheckdMetricChan are closed
|
||||||
if ok {
|
// and drained, the contraption above will nil out cmc and umc,
|
||||||
if metricFamily.GetHelp() != desc.help {
|
// and then we can leave the collect loop here.
|
||||||
errs = append(errs, fmt.Errorf(
|
if cmc == nil && umc == nil {
|
||||||
"collected metric %s %s has help %q but should have %q",
|
break
|
||||||
desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// TODO(beorn7): Simplify switch once Desc has type.
|
|
||||||
switch metricFamily.GetType() {
|
|
||||||
case dto.MetricType_COUNTER:
|
|
||||||
if dtoMetric.Counter == nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"collected metric %s %s should be a Counter",
|
|
||||||
desc.fqName, dtoMetric,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case dto.MetricType_GAUGE:
|
|
||||||
if dtoMetric.Gauge == nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"collected metric %s %s should be a Gauge",
|
|
||||||
desc.fqName, dtoMetric,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case dto.MetricType_SUMMARY:
|
|
||||||
if dtoMetric.Summary == nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"collected metric %s %s should be a Summary",
|
|
||||||
desc.fqName, dtoMetric,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case dto.MetricType_UNTYPED:
|
|
||||||
if dtoMetric.Untyped == nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"collected metric %s %s should be Untyped",
|
|
||||||
desc.fqName, dtoMetric,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case dto.MetricType_HISTOGRAM:
|
|
||||||
if dtoMetric.Histogram == nil {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"collected metric %s %s should be a Histogram",
|
|
||||||
desc.fqName, dtoMetric,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("encountered MetricFamily with invalid type")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
metricFamily = &dto.MetricFamily{}
|
|
||||||
metricFamily.Name = proto.String(desc.fqName)
|
|
||||||
metricFamily.Help = proto.String(desc.help)
|
|
||||||
// TODO(beorn7): Simplify switch once Desc has type.
|
|
||||||
switch {
|
|
||||||
case dtoMetric.Gauge != nil:
|
|
||||||
metricFamily.Type = dto.MetricType_GAUGE.Enum()
|
|
||||||
case dtoMetric.Counter != nil:
|
|
||||||
metricFamily.Type = dto.MetricType_COUNTER.Enum()
|
|
||||||
case dtoMetric.Summary != nil:
|
|
||||||
metricFamily.Type = dto.MetricType_SUMMARY.Enum()
|
|
||||||
case dtoMetric.Untyped != nil:
|
|
||||||
metricFamily.Type = dto.MetricType_UNTYPED.Enum()
|
|
||||||
case dtoMetric.Histogram != nil:
|
|
||||||
metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
|
|
||||||
default:
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"empty metric collected: %s", dtoMetric,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
metricFamiliesByName[desc.fqName] = metricFamily
|
|
||||||
}
|
}
|
||||||
if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.pedanticChecksEnabled {
|
|
||||||
// Is the desc registered at all?
|
|
||||||
if _, exist := registeredDescIDs[desc.id]; !exist {
|
|
||||||
errs = append(errs, fmt.Errorf(
|
|
||||||
"collected metric %s %s with unregistered descriptor %s",
|
|
||||||
metricFamily.GetName(), dtoMetric, desc,
|
|
||||||
))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
|
|
||||||
}
|
}
|
||||||
return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the
|
||||||
|
// Prometheus text format, and writes it to a temporary file. Upon success, the
|
||||||
|
// temporary file is renamed to the provided filename.
|
||||||
|
//
|
||||||
|
// This is intended for use with the textfile collector of the node exporter.
|
||||||
|
// Note that the node exporter expects the filename to be suffixed with ".prom".
|
||||||
|
func WriteToTextfile(filename string, g Gatherer) error {
|
||||||
|
tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.Remove(tmp.Name())
|
||||||
|
|
||||||
|
mfs, err := g.Gather()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, mf := range mfs {
|
||||||
|
if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := tmp.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chmod(tmp.Name(), 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(tmp.Name(), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// processMetric is an internal helper method only used by the Gather method.
|
||||||
|
func processMetric(
|
||||||
|
metric Metric,
|
||||||
|
metricFamiliesByName map[string]*dto.MetricFamily,
|
||||||
|
metricHashes map[uint64]struct{},
|
||||||
|
registeredDescIDs map[uint64]struct{},
|
||||||
|
) error {
|
||||||
|
desc := metric.Desc()
|
||||||
|
// Wrapped metrics collected by an unchecked Collector can have an
|
||||||
|
// invalid Desc.
|
||||||
|
if desc.err != nil {
|
||||||
|
return desc.err
|
||||||
|
}
|
||||||
|
dtoMetric := &dto.Metric{}
|
||||||
|
if err := metric.Write(dtoMetric); err != nil {
|
||||||
|
return fmt.Errorf("error collecting metric %v: %s", desc, err)
|
||||||
|
}
|
||||||
|
metricFamily, ok := metricFamiliesByName[desc.fqName]
|
||||||
|
if ok { // Existing name.
|
||||||
|
if metricFamily.GetHelp() != desc.help {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s has help %q but should have %q",
|
||||||
|
desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// TODO(beorn7): Simplify switch once Desc has type.
|
||||||
|
switch metricFamily.GetType() {
|
||||||
|
case dto.MetricType_COUNTER:
|
||||||
|
if dtoMetric.Counter == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Counter",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_GAUGE:
|
||||||
|
if dtoMetric.Gauge == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Gauge",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_SUMMARY:
|
||||||
|
if dtoMetric.Summary == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Summary",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_UNTYPED:
|
||||||
|
if dtoMetric.Untyped == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be Untyped",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
if dtoMetric.Histogram == nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s should be a Histogram",
|
||||||
|
desc.fqName, dtoMetric,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("encountered MetricFamily with invalid type")
|
||||||
|
}
|
||||||
|
} else { // New name.
|
||||||
|
metricFamily = &dto.MetricFamily{}
|
||||||
|
metricFamily.Name = proto.String(desc.fqName)
|
||||||
|
metricFamily.Help = proto.String(desc.help)
|
||||||
|
// TODO(beorn7): Simplify switch once Desc has type.
|
||||||
|
switch {
|
||||||
|
case dtoMetric.Gauge != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_GAUGE.Enum()
|
||||||
|
case dtoMetric.Counter != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_COUNTER.Enum()
|
||||||
|
case dtoMetric.Summary != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_SUMMARY.Enum()
|
||||||
|
case dtoMetric.Untyped != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_UNTYPED.Enum()
|
||||||
|
case dtoMetric.Histogram != nil:
|
||||||
|
metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("empty metric collected: %s", dtoMetric)
|
||||||
|
}
|
||||||
|
if err := checkSuffixCollisions(metricFamily, metricFamiliesByName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metricFamiliesByName[desc.fqName] = metricFamily
|
||||||
|
}
|
||||||
|
if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if registeredDescIDs != nil {
|
||||||
|
// Is the desc registered at all?
|
||||||
|
if _, exist := registeredDescIDs[desc.id]; !exist {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %s %s with unregistered descriptor %s",
|
||||||
|
metricFamily.GetName(), dtoMetric, desc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gatherers is a slice of Gatherer instances that implements the Gatherer
|
// Gatherers is a slice of Gatherer instances that implements the Gatherer
|
||||||
// interface itself. Its Gather method calls Gather on all Gatherers in the
|
// interface itself. Its Gather method calls Gather on all Gatherers in the
|
||||||
// slice in order and returns the merged results. Errors returned from the
|
// slice in order and returns the merged results. Errors returned from the
|
||||||
// Gather calles are all returned in a flattened MultiError. Duplicate and
|
// Gather calls are all returned in a flattened MultiError. Duplicate and
|
||||||
// inconsistent Metrics are skipped (first occurrence in slice order wins) and
|
// inconsistent Metrics are skipped (first occurrence in slice order wins) and
|
||||||
// reported in the returned error.
|
// reported in the returned error.
|
||||||
//
|
//
|
||||||
|
@ -537,7 +700,6 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
|
||||||
var (
|
var (
|
||||||
metricFamiliesByName = map[string]*dto.MetricFamily{}
|
metricFamiliesByName = map[string]*dto.MetricFamily{}
|
||||||
metricHashes = map[uint64]struct{}{}
|
metricHashes = map[uint64]struct{}{}
|
||||||
dimHashes = map[string]uint64{}
|
|
||||||
errs MultiError // The collected errors to return in the end.
|
errs MultiError // The collected errors to return in the end.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -574,10 +736,14 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
|
||||||
existingMF.Name = mf.Name
|
existingMF.Name = mf.Name
|
||||||
existingMF.Help = mf.Help
|
existingMF.Help = mf.Help
|
||||||
existingMF.Type = mf.Type
|
existingMF.Type = mf.Type
|
||||||
|
if err := checkSuffixCollisions(existingMF, metricFamiliesByName); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
metricFamiliesByName[mf.GetName()] = existingMF
|
metricFamiliesByName[mf.GetName()] = existingMF
|
||||||
}
|
}
|
||||||
for _, m := range mf.Metric {
|
for _, m := range mf.Metric {
|
||||||
if err := checkMetricConsistency(existingMF, m, metricHashes, dimHashes); err != nil {
|
if err := checkMetricConsistency(existingMF, m, metricHashes); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -585,88 +751,80 @@ func (gs Gatherers) Gather() ([]*dto.MetricFamily, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// metricSorter is a sortable slice of *dto.Metric.
|
// checkSuffixCollisions checks for collisions with the “magic” suffixes the
|
||||||
type metricSorter []*dto.Metric
|
// Prometheus text format and the internal metric representation of the
|
||||||
|
// Prometheus server add while flattening Summaries and Histograms.
|
||||||
func (s metricSorter) Len() int {
|
func checkSuffixCollisions(mf *dto.MetricFamily, mfs map[string]*dto.MetricFamily) error {
|
||||||
return len(s)
|
var (
|
||||||
}
|
newName = mf.GetName()
|
||||||
|
newType = mf.GetType()
|
||||||
func (s metricSorter) Swap(i, j int) {
|
newNameWithoutSuffix = ""
|
||||||
s[i], s[j] = s[j], s[i]
|
)
|
||||||
}
|
switch {
|
||||||
|
case strings.HasSuffix(newName, "_count"):
|
||||||
func (s metricSorter) Less(i, j int) bool {
|
newNameWithoutSuffix = newName[:len(newName)-6]
|
||||||
if len(s[i].Label) != len(s[j].Label) {
|
case strings.HasSuffix(newName, "_sum"):
|
||||||
// This should not happen. The metrics are
|
newNameWithoutSuffix = newName[:len(newName)-4]
|
||||||
// inconsistent. However, we have to deal with the fact, as
|
case strings.HasSuffix(newName, "_bucket"):
|
||||||
// people might use custom collectors or metric family injection
|
newNameWithoutSuffix = newName[:len(newName)-7]
|
||||||
// to create inconsistent metrics. So let's simply compare the
|
|
||||||
// number of labels in this case. That will still yield
|
|
||||||
// reproducible sorting.
|
|
||||||
return len(s[i].Label) < len(s[j].Label)
|
|
||||||
}
|
}
|
||||||
for n, lp := range s[i].Label {
|
if newNameWithoutSuffix != "" {
|
||||||
vi := lp.GetValue()
|
if existingMF, ok := mfs[newNameWithoutSuffix]; ok {
|
||||||
vj := s[j].Label[n].GetValue()
|
switch existingMF.GetType() {
|
||||||
if vi != vj {
|
case dto.MetricType_SUMMARY:
|
||||||
return vi < vj
|
if !strings.HasSuffix(newName, "_bucket") {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric named %q collides with previously collected summary named %q",
|
||||||
|
newName, newNameWithoutSuffix,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric named %q collides with previously collected histogram named %q",
|
||||||
|
newName, newNameWithoutSuffix,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if newType == dto.MetricType_SUMMARY || newType == dto.MetricType_HISTOGRAM {
|
||||||
// We should never arrive here. Multiple metrics with the same
|
if _, ok := mfs[newName+"_count"]; ok {
|
||||||
// label set in the same scrape will lead to undefined ingestion
|
return fmt.Errorf(
|
||||||
// behavior. However, as above, we have to provide stable sorting
|
"collected histogram or summary named %q collides with previously collected metric named %q",
|
||||||
// here, even for inconsistent metrics. So sort equal metrics
|
newName, newName+"_count",
|
||||||
// by their timestamp, with missing timestamps (implying "now")
|
)
|
||||||
// coming last.
|
}
|
||||||
if s[i].TimestampMs == nil {
|
if _, ok := mfs[newName+"_sum"]; ok {
|
||||||
return false
|
return fmt.Errorf(
|
||||||
}
|
"collected histogram or summary named %q collides with previously collected metric named %q",
|
||||||
if s[j].TimestampMs == nil {
|
newName, newName+"_sum",
|
||||||
return true
|
)
|
||||||
}
|
|
||||||
return s[i].GetTimestampMs() < s[j].GetTimestampMs()
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalizeMetricFamilies returns a MetricFamily slice with empty
|
|
||||||
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
|
|
||||||
// the slice, with the contained Metrics sorted within each MetricFamily.
|
|
||||||
func normalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily {
|
|
||||||
for _, mf := range metricFamiliesByName {
|
|
||||||
sort.Sort(metricSorter(mf.Metric))
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(metricFamiliesByName))
|
|
||||||
for name, mf := range metricFamiliesByName {
|
|
||||||
if len(mf.Metric) > 0 {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
if newType == dto.MetricType_HISTOGRAM {
|
||||||
result := make([]*dto.MetricFamily, 0, len(names))
|
if _, ok := mfs[newName+"_bucket"]; ok {
|
||||||
for _, name := range names {
|
return fmt.Errorf(
|
||||||
result = append(result, metricFamiliesByName[name])
|
"collected histogram named %q collides with previously collected metric named %q",
|
||||||
|
newName, newName+"_bucket",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkMetricConsistency checks if the provided Metric is consistent with the
|
// checkMetricConsistency checks if the provided Metric is consistent with the
|
||||||
// provided MetricFamily. It also hashed the Metric labels and the MetricFamily
|
// provided MetricFamily. It also hashes the Metric labels and the MetricFamily
|
||||||
// name. If the resulting hash is alread in the provided metricHashes, an error
|
// name. If the resulting hash is already in the provided metricHashes, an error
|
||||||
// is returned. If not, it is added to metricHashes. The provided dimHashes maps
|
// is returned. If not, it is added to metricHashes.
|
||||||
// MetricFamily names to their dimHash (hashed sorted label names). If dimHashes
|
|
||||||
// doesn't yet contain a hash for the provided MetricFamily, it is
|
|
||||||
// added. Otherwise, an error is returned if the existing dimHashes in not equal
|
|
||||||
// the calculated dimHash.
|
|
||||||
func checkMetricConsistency(
|
func checkMetricConsistency(
|
||||||
metricFamily *dto.MetricFamily,
|
metricFamily *dto.MetricFamily,
|
||||||
dtoMetric *dto.Metric,
|
dtoMetric *dto.Metric,
|
||||||
metricHashes map[uint64]struct{},
|
metricHashes map[uint64]struct{},
|
||||||
dimHashes map[string]uint64,
|
|
||||||
) error {
|
) error {
|
||||||
|
name := metricFamily.GetName()
|
||||||
|
|
||||||
// Type consistency with metric family.
|
// Type consistency with metric family.
|
||||||
if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
|
if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
|
||||||
metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
|
metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
|
||||||
|
@ -674,41 +832,65 @@ func checkMetricConsistency(
|
||||||
metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
|
metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
|
||||||
metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
|
metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"collected metric %s %s is not a %s",
|
"collected metric %q { %s} is not a %s",
|
||||||
metricFamily.GetName(), dtoMetric, metricFamily.GetType(),
|
name, dtoMetric, metricFamily.GetType(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the metric unique (i.e. no other metric with the same name and the same label values)?
|
previousLabelName := ""
|
||||||
|
for _, labelPair := range dtoMetric.GetLabel() {
|
||||||
|
labelName := labelPair.GetName()
|
||||||
|
if labelName == previousLabelName {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %q { %s} has two or more labels with the same name: %s",
|
||||||
|
name, dtoMetric, labelName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !checkLabelName(labelName) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %q { %s} has a label with an invalid name: %s",
|
||||||
|
name, dtoMetric, labelName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if dtoMetric.Summary != nil && labelName == quantileLabel {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %q { %s} must not have an explicit %q label",
|
||||||
|
name, dtoMetric, quantileLabel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !utf8.ValidString(labelPair.GetValue()) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"collected metric %q { %s} has a label named %q whose value is not utf8: %#v",
|
||||||
|
name, dtoMetric, labelName, labelPair.GetValue())
|
||||||
|
}
|
||||||
|
previousLabelName = labelName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the metric unique (i.e. no other metric with the same name and the same labels)?
|
||||||
h := hashNew()
|
h := hashNew()
|
||||||
h = hashAdd(h, metricFamily.GetName())
|
h = hashAdd(h, name)
|
||||||
h = hashAddByte(h, separatorByte)
|
h = hashAddByte(h, separatorByte)
|
||||||
dh := hashNew()
|
|
||||||
// Make sure label pairs are sorted. We depend on it for the consistency
|
// Make sure label pairs are sorted. We depend on it for the consistency
|
||||||
// check.
|
// check.
|
||||||
sort.Sort(LabelPairSorter(dtoMetric.Label))
|
if !sort.IsSorted(labelPairSorter(dtoMetric.Label)) {
|
||||||
|
// We cannot sort dtoMetric.Label in place as it is immutable by contract.
|
||||||
|
copiedLabels := make([]*dto.LabelPair, len(dtoMetric.Label))
|
||||||
|
copy(copiedLabels, dtoMetric.Label)
|
||||||
|
sort.Sort(labelPairSorter(copiedLabels))
|
||||||
|
dtoMetric.Label = copiedLabels
|
||||||
|
}
|
||||||
for _, lp := range dtoMetric.Label {
|
for _, lp := range dtoMetric.Label {
|
||||||
|
h = hashAdd(h, lp.GetName())
|
||||||
|
h = hashAddByte(h, separatorByte)
|
||||||
h = hashAdd(h, lp.GetValue())
|
h = hashAdd(h, lp.GetValue())
|
||||||
h = hashAddByte(h, separatorByte)
|
h = hashAddByte(h, separatorByte)
|
||||||
dh = hashAdd(dh, lp.GetName())
|
|
||||||
dh = hashAddByte(dh, separatorByte)
|
|
||||||
}
|
}
|
||||||
if _, exists := metricHashes[h]; exists {
|
if _, exists := metricHashes[h]; exists {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"collected metric %s %s was collected before with the same name and label values",
|
"collected metric %q { %s} was collected before with the same name and label values",
|
||||||
metricFamily.GetName(), dtoMetric,
|
name, dtoMetric,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if dimHash, ok := dimHashes[metricFamily.GetName()]; ok {
|
|
||||||
if dimHash != dh {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"collected metric %s %s has label dimensions inconsistent with previously collected metrics in the same metric family",
|
|
||||||
metricFamily.GetName(), dtoMetric,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dimHashes[metricFamily.GetName()] = dh
|
|
||||||
}
|
|
||||||
metricHashes[h] = struct{}{}
|
metricHashes[h] = struct{}{}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -727,8 +909,8 @@ func checkDescConsistency(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the desc consistent with the content of the metric?
|
// Is the desc consistent with the content of the metric?
|
||||||
lpsFromDesc := make([]*dto.LabelPair, 0, len(dtoMetric.Label))
|
lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
|
||||||
lpsFromDesc = append(lpsFromDesc, desc.constLabelPairs...)
|
copy(lpsFromDesc, desc.constLabelPairs)
|
||||||
for _, l := range desc.variableLabels {
|
for _, l := range desc.variableLabels {
|
||||||
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
||||||
Name: proto.String(l),
|
Name: proto.String(l),
|
||||||
|
@ -740,7 +922,7 @@ func checkDescConsistency(
|
||||||
metricFamily.GetName(), dtoMetric, desc,
|
metricFamily.GetName(), dtoMetric, desc,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
sort.Sort(LabelPairSorter(lpsFromDesc))
|
sort.Sort(labelPairSorter(lpsFromDesc))
|
||||||
for i, lpFromDesc := range lpsFromDesc {
|
for i, lpFromDesc := range lpsFromDesc {
|
||||||
lpFromMetric := dtoMetric.Label[i]
|
lpFromMetric := dtoMetric.Label[i]
|
||||||
if lpFromDesc.GetName() != lpFromMetric.GetName() ||
|
if lpFromDesc.GetName() != lpFromMetric.GetName() ||
|
||||||
|
|
307
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
307
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
|
@ -16,8 +16,10 @@ package prometheus
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/beorn7/perks/quantile"
|
"github.com/beorn7/perks/quantile"
|
||||||
|
@ -36,7 +38,10 @@ const quantileLabel = "quantile"
|
||||||
//
|
//
|
||||||
// A typical use-case is the observation of request latencies. By default, a
|
// A typical use-case is the observation of request latencies. By default, a
|
||||||
// Summary provides the median, the 90th and the 99th percentile of the latency
|
// Summary provides the median, the 90th and the 99th percentile of the latency
|
||||||
// as rank estimations.
|
// as rank estimations. However, the default behavior will change in the
|
||||||
|
// upcoming v1.0.0 of the library. There will be no rank estimations at all by
|
||||||
|
// default. For a sane transition, it is recommended to set the desired rank
|
||||||
|
// estimations explicitly.
|
||||||
//
|
//
|
||||||
// Note that the rank estimations cannot be aggregated in a meaningful way with
|
// Note that the rank estimations cannot be aggregated in a meaningful way with
|
||||||
// the Prometheus query language (i.e. you cannot average or add them). If you
|
// the Prometheus query language (i.e. you cannot average or add them). If you
|
||||||
|
@ -56,7 +61,7 @@ type Summary interface {
|
||||||
// DefObjectives are the default Summary quantile values.
|
// DefObjectives are the default Summary quantile values.
|
||||||
//
|
//
|
||||||
// Deprecated: DefObjectives will not be used as the default objectives in
|
// Deprecated: DefObjectives will not be used as the default objectives in
|
||||||
// v0.10 of the library. The default Summary will have no quantiles then.
|
// v1.0.0 of the library. The default Summary will have no quantiles then.
|
||||||
var (
|
var (
|
||||||
DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
|
DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
|
||||||
|
|
||||||
|
@ -78,8 +83,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// SummaryOpts bundles the options for creating a Summary metric. It is
|
// SummaryOpts bundles the options for creating a Summary metric. It is
|
||||||
// mandatory to set Name and Help to a non-empty string. All other fields are
|
// mandatory to set Name to a non-empty string. While all other fields are
|
||||||
// optional and can safely be left at their zero value.
|
// optional and can safely be left at their zero value, it is recommended to set
|
||||||
|
// a help string and to explicitly set the Objectives field to the desired value
|
||||||
|
// as the default value will change in the upcoming v1.0.0 of the library.
|
||||||
type SummaryOpts struct {
|
type SummaryOpts struct {
|
||||||
// Namespace, Subsystem, and Name are components of the fully-qualified
|
// Namespace, Subsystem, and Name are components of the fully-qualified
|
||||||
// name of the Summary (created by joining these components with
|
// name of the Summary (created by joining these components with
|
||||||
|
@ -90,29 +97,27 @@ type SummaryOpts struct {
|
||||||
Subsystem string
|
Subsystem string
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// Help provides information about this Summary. Mandatory!
|
// Help provides information about this Summary.
|
||||||
//
|
//
|
||||||
// Metrics with the same fully-qualified name must have the same Help
|
// Metrics with the same fully-qualified name must have the same Help
|
||||||
// string.
|
// string.
|
||||||
Help string
|
Help string
|
||||||
|
|
||||||
// ConstLabels are used to attach fixed labels to this
|
// ConstLabels are used to attach fixed labels to this metric. Metrics
|
||||||
// Summary. Summaries with the same fully-qualified name must have the
|
// with the same fully-qualified name must have the same label names in
|
||||||
// same label names in their ConstLabels.
|
// their ConstLabels.
|
||||||
//
|
//
|
||||||
// Note that in most cases, labels have a value that varies during the
|
// Due to the way a Summary is represented in the Prometheus text format
|
||||||
// lifetime of a process. Those labels are usually managed with a
|
// and how it is handled by the Prometheus server internally, “quantile”
|
||||||
// SummaryVec. ConstLabels serve only special purposes. One is for the
|
// is an illegal label name. Construction of a Summary or SummaryVec
|
||||||
// special case where the value of a label does not change during the
|
// will panic if this label name is used in ConstLabels.
|
||||||
// lifetime of a process, e.g. if the revision of the running binary is
|
|
||||||
// put into a label. Another, more advanced purpose is if more than one
|
|
||||||
// Collector needs to collect Summaries with the same fully-qualified
|
|
||||||
// name. In that case, those Summaries must differ in the values of
|
|
||||||
// their ConstLabels. See the Collector examples.
|
|
||||||
//
|
//
|
||||||
// If the value of a label never changes (not even between binaries),
|
// ConstLabels are only used rarely. In particular, do not use them to
|
||||||
// that label most likely should not be a label at all (but part of the
|
// attach the same labels to all your metrics. Those use cases are
|
||||||
// metric name).
|
// better covered by target labels set by the scraping Prometheus
|
||||||
|
// server, or by one specific metric (e.g. a build_info or a
|
||||||
|
// machine_role metric). See also
|
||||||
|
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||||
ConstLabels Labels
|
ConstLabels Labels
|
||||||
|
|
||||||
// Objectives defines the quantile rank estimates with their respective
|
// Objectives defines the quantile rank estimates with their respective
|
||||||
|
@ -122,9 +127,10 @@ type SummaryOpts struct {
|
||||||
// its zero value (i.e. nil). To create a Summary without Objectives,
|
// its zero value (i.e. nil). To create a Summary without Objectives,
|
||||||
// set it to an empty map (i.e. map[float64]float64{}).
|
// set it to an empty map (i.e. map[float64]float64{}).
|
||||||
//
|
//
|
||||||
// Deprecated: Note that the current value of DefObjectives is
|
// Note that the current value of DefObjectives is deprecated. It will
|
||||||
// deprecated. It will be replaced by an empty map in v0.10 of the
|
// be replaced by an empty map in v1.0.0 of the library. Please
|
||||||
// library. Please explicitly set Objectives to the desired value.
|
// explicitly set Objectives to the desired value to avoid problems
|
||||||
|
// during the transition.
|
||||||
Objectives map[float64]float64
|
Objectives map[float64]float64
|
||||||
|
|
||||||
// MaxAge defines the duration for which an observation stays relevant
|
// MaxAge defines the duration for which an observation stays relevant
|
||||||
|
@ -148,7 +154,7 @@ type SummaryOpts struct {
|
||||||
BufCap uint32
|
BufCap uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Great fuck-up with the sliding-window decay algorithm... The Merge method of
|
// Problem with the sliding-window decay algorithm... The Merge method of
|
||||||
// perk/quantile is actually not working as advertised - and it might be
|
// perk/quantile is actually not working as advertised - and it might be
|
||||||
// unfixable, as the underlying algorithm is apparently not capable of merging
|
// unfixable, as the underlying algorithm is apparently not capable of merging
|
||||||
// summaries in the first place. To avoid using Merge, we are currently adding
|
// summaries in the first place. To avoid using Merge, we are currently adding
|
||||||
|
@ -178,7 +184,7 @@ func NewSummary(opts SummaryOpts) Summary {
|
||||||
|
|
||||||
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if len(desc.variableLabels) != len(labelValues) {
|
||||||
panic(errInconsistentCardinality)
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range desc.variableLabels {
|
for _, n := range desc.variableLabels {
|
||||||
|
@ -211,6 +217,17 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||||
opts.BufCap = DefBufCap
|
opts.BufCap = DefBufCap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(opts.Objectives) == 0 {
|
||||||
|
// Use the lock-free implementation of a Summary without objectives.
|
||||||
|
s := &noObjectivesSummary{
|
||||||
|
desc: desc,
|
||||||
|
labelPairs: makeLabelPairs(desc, labelValues),
|
||||||
|
counts: [2]*summaryCounts{&summaryCounts{}, &summaryCounts{}},
|
||||||
|
}
|
||||||
|
s.init(s) // Init self-collection.
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
s := &summary{
|
s := &summary{
|
||||||
desc: desc,
|
desc: desc,
|
||||||
|
|
||||||
|
@ -379,6 +396,116 @@ func (s *summary) swapBufs(now time.Time) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type summaryCounts struct {
|
||||||
|
// sumBits contains the bits of the float64 representing the sum of all
|
||||||
|
// observations. sumBits and count have to go first in the struct to
|
||||||
|
// guarantee alignment for atomic operations.
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
sumBits uint64
|
||||||
|
count uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type noObjectivesSummary struct {
|
||||||
|
// countAndHotIdx enables lock-free writes with use of atomic updates.
|
||||||
|
// The most significant bit is the hot index [0 or 1] of the count field
|
||||||
|
// below. Observe calls update the hot one. All remaining bits count the
|
||||||
|
// number of Observe calls. Observe starts by incrementing this counter,
|
||||||
|
// and finish by incrementing the count field in the respective
|
||||||
|
// summaryCounts, as a marker for completion.
|
||||||
|
//
|
||||||
|
// Calls of the Write method (which are non-mutating reads from the
|
||||||
|
// perspective of the summary) swap the hot–cold under the writeMtx
|
||||||
|
// lock. A cooldown is awaited (while locked) by comparing the number of
|
||||||
|
// observations with the initiation count. Once they match, then the
|
||||||
|
// last observation on the now cool one has completed. All cool fields must
|
||||||
|
// be merged into the new hot before releasing writeMtx.
|
||||||
|
|
||||||
|
// Fields with atomic access first! See alignment constraint:
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||||
|
countAndHotIdx uint64
|
||||||
|
|
||||||
|
selfCollector
|
||||||
|
desc *Desc
|
||||||
|
writeMtx sync.Mutex // Only used in the Write method.
|
||||||
|
|
||||||
|
// Two counts, one is "hot" for lock-free observations, the other is
|
||||||
|
// "cold" for writing out a dto.Metric. It has to be an array of
|
||||||
|
// pointers to guarantee 64bit alignment of the histogramCounts, see
|
||||||
|
// http://golang.org/pkg/sync/atomic/#pkg-note-BUG.
|
||||||
|
counts [2]*summaryCounts
|
||||||
|
|
||||||
|
labelPairs []*dto.LabelPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noObjectivesSummary) Desc() *Desc {
|
||||||
|
return s.desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noObjectivesSummary) Observe(v float64) {
|
||||||
|
// We increment h.countAndHotIdx so that the counter in the lower
|
||||||
|
// 63 bits gets incremented. At the same time, we get the new value
|
||||||
|
// back, which we can use to find the currently-hot counts.
|
||||||
|
n := atomic.AddUint64(&s.countAndHotIdx, 1)
|
||||||
|
hotCounts := s.counts[n>>63]
|
||||||
|
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&hotCounts.sumBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
|
||||||
|
if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Increment count last as we take it as a signal that the observation
|
||||||
|
// is complete.
|
||||||
|
atomic.AddUint64(&hotCounts.count, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noObjectivesSummary) Write(out *dto.Metric) error {
|
||||||
|
// For simplicity, we protect this whole method by a mutex. It is not in
|
||||||
|
// the hot path, i.e. Observe is called much more often than Write. The
|
||||||
|
// complication of making Write lock-free isn't worth it, if possible at
|
||||||
|
// all.
|
||||||
|
s.writeMtx.Lock()
|
||||||
|
defer s.writeMtx.Unlock()
|
||||||
|
|
||||||
|
// Adding 1<<63 switches the hot index (from 0 to 1 or from 1 to 0)
|
||||||
|
// without touching the count bits. See the struct comments for a full
|
||||||
|
// description of the algorithm.
|
||||||
|
n := atomic.AddUint64(&s.countAndHotIdx, 1<<63)
|
||||||
|
// count is contained unchanged in the lower 63 bits.
|
||||||
|
count := n & ((1 << 63) - 1)
|
||||||
|
// The most significant bit tells us which counts is hot. The complement
|
||||||
|
// is thus the cold one.
|
||||||
|
hotCounts := s.counts[n>>63]
|
||||||
|
coldCounts := s.counts[(^n)>>63]
|
||||||
|
|
||||||
|
// Await cooldown.
|
||||||
|
for count != atomic.LoadUint64(&coldCounts.count) {
|
||||||
|
runtime.Gosched() // Let observations get work done.
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := &dto.Summary{
|
||||||
|
SampleCount: proto.Uint64(count),
|
||||||
|
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Summary = sum
|
||||||
|
out.Label = s.labelPairs
|
||||||
|
|
||||||
|
// Finally add all the cold counts to the new hot counts and reset the cold counts.
|
||||||
|
atomic.AddUint64(&hotCounts.count, count)
|
||||||
|
atomic.StoreUint64(&coldCounts.count, 0)
|
||||||
|
for {
|
||||||
|
oldBits := atomic.LoadUint64(&hotCounts.sumBits)
|
||||||
|
newBits := math.Float64bits(math.Float64frombits(oldBits) + sum.GetSampleSum())
|
||||||
|
if atomic.CompareAndSwapUint64(&hotCounts.sumBits, oldBits, newBits) {
|
||||||
|
atomic.StoreUint64(&coldCounts.sumBits, 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type quantSort []*dto.Quantile
|
type quantSort []*dto.Quantile
|
||||||
|
|
||||||
func (s quantSort) Len() int {
|
func (s quantSort) Len() int {
|
||||||
|
@ -399,13 +526,21 @@ func (s quantSort) Less(i, j int) bool {
|
||||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||||
// instances with NewSummaryVec.
|
// instances with NewSummaryVec.
|
||||||
type SummaryVec struct {
|
type SummaryVec struct {
|
||||||
*MetricVec
|
*metricVec
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
|
// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
|
||||||
// partitioned by the given label names. At least one label name must be
|
// partitioned by the given label names.
|
||||||
// provided.
|
//
|
||||||
|
// Due to the way a Summary is represented in the Prometheus text format and how
|
||||||
|
// it is handled by the Prometheus server internally, “quantile” is an illegal
|
||||||
|
// label name. NewSummaryVec will panic if this label name is used.
|
||||||
func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||||
|
for _, ln := range labelNames {
|
||||||
|
if ln == quantileLabel {
|
||||||
|
panic(errQuantileLabelNotAllowed)
|
||||||
|
}
|
||||||
|
}
|
||||||
desc := NewDesc(
|
desc := NewDesc(
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||||
opts.Help,
|
opts.Help,
|
||||||
|
@ -413,28 +548,58 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||||
opts.ConstLabels,
|
opts.ConstLabels,
|
||||||
)
|
)
|
||||||
return &SummaryVec{
|
return &SummaryVec{
|
||||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||||
return newSummary(desc, opts, lvs...)
|
return newSummary(desc, opts, lvs...)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWithLabelValues replaces the method of the same name in MetricVec.
|
// GetMetricWithLabelValues returns the Summary for the given slice of label
|
||||||
// The difference is that this method returns an Observer and not a Metric so
|
// values (same order as the VariableLabels in Desc). If that combination of
|
||||||
// that no type conversion to an Observer is required.
|
// label values is accessed for the first time, a new Summary is created.
|
||||||
func (m *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
//
|
||||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
// It is possible to call this method without using the returned Summary to only
|
||||||
|
// create the new Summary but leave it at its starting value, a Summary without
|
||||||
|
// any observations.
|
||||||
|
//
|
||||||
|
// Keeping the Summary for later use is possible (and should be considered if
|
||||||
|
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||||
|
// Delete can be used to delete the Summary from the SummaryVec. In that case,
|
||||||
|
// the Summary will still exist, but it will not be exported anymore, even if a
|
||||||
|
// Summary with the same label values is created later. See also the CounterVec
|
||||||
|
// example.
|
||||||
|
//
|
||||||
|
// An error is returned if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||||
|
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the GaugeVec example.
|
||||||
|
func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Observer), err
|
return metric.(Observer), err
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
// GetMetricWith returns the Summary for the given Labels map (the label names
|
||||||
// difference is that this method returns an Observer and not a Metric so that
|
// must match those of the VariableLabels in Desc). If that label map is
|
||||||
// no type conversion to an Observer is required.
|
// accessed for the first time, a new Summary is created. Implications of
|
||||||
func (m *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
// creating a Summary without using it and keeping the Summary for later use are
|
||||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
// the same as for GetMetricWithLabelValues.
|
||||||
|
//
|
||||||
|
// An error is returned if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as
|
||||||
|
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||||
|
// methods.
|
||||||
|
func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||||
|
metric, err := v.metricVec.getMetricWith(labels)
|
||||||
if metric != nil {
|
if metric != nil {
|
||||||
return metric.(Observer), err
|
return metric.(Observer), err
|
||||||
}
|
}
|
||||||
|
@ -442,18 +607,57 @@ func (m *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
||||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
// GetMetricWithLabelValues would have returned an error. Not returning an
|
||||||
// error, WithLabelValues allows shortcuts like
|
// error allows shortcuts like
|
||||||
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
||||||
func (m *SummaryVec) WithLabelValues(lvs ...string) Observer {
|
func (v *SummaryVec) WithLabelValues(lvs ...string) Observer {
|
||||||
return m.MetricVec.WithLabelValues(lvs...).(Observer)
|
s, err := v.GetMetricWithLabelValues(lvs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||||
// returned an error. By not returning an error, With allows shortcuts like
|
// returned an error. Not returning an error allows shortcuts like
|
||||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
||||||
func (m *SummaryVec) With(labels Labels) Observer {
|
func (v *SummaryVec) With(labels Labels) Observer {
|
||||||
return m.MetricVec.With(labels).(Observer)
|
s, err := v.GetMetricWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||||
|
// returned vector has those labels pre-set for all labeled operations performed
|
||||||
|
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||||
|
// order of the remaining labels stays the same (just with the curried labels
|
||||||
|
// taken out of the sequence – which is relevant for the
|
||||||
|
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||||
|
// vector, but only with labels not yet used for currying before.
|
||||||
|
//
|
||||||
|
// The metrics contained in the SummaryVec are shared between the curried and
|
||||||
|
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||||
|
// vectors behave identically in terms of collection. Only one must be
|
||||||
|
// registered with a given registry (usually the uncurried version). The Reset
|
||||||
|
// method deletes all metrics, even if called on a curried vector.
|
||||||
|
func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||||
|
vec, err := v.curryWith(labels)
|
||||||
|
if vec != nil {
|
||||||
|
return &SummaryVec{vec}, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustCurryWith works as CurryWith but panics where CurryWith would have
|
||||||
|
// returned an error.
|
||||||
|
func (v *SummaryVec) MustCurryWith(labels Labels) ObserverVec {
|
||||||
|
vec, err := v.CurryWith(labels)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return vec
|
||||||
}
|
}
|
||||||
|
|
||||||
type constSummary struct {
|
type constSummary struct {
|
||||||
|
@ -506,7 +710,7 @@ func (s *constSummary) Write(out *dto.Metric) error {
|
||||||
// map[float64]float64{0.5: 0.23, 0.99: 0.56}
|
// map[float64]float64{0.5: 0.23, 0.99: 0.56}
|
||||||
//
|
//
|
||||||
// NewConstSummary returns an error if the length of labelValues is not
|
// NewConstSummary returns an error if the length of labelValues is not
|
||||||
// consistent with the variable labels in Desc.
|
// consistent with the variable labels in Desc or if Desc is invalid.
|
||||||
func NewConstSummary(
|
func NewConstSummary(
|
||||||
desc *Desc,
|
desc *Desc,
|
||||||
count uint64,
|
count uint64,
|
||||||
|
@ -514,8 +718,11 @@ func NewConstSummary(
|
||||||
quantiles map[float64]float64,
|
quantiles map[float64]float64,
|
||||||
labelValues ...string,
|
labelValues ...string,
|
||||||
) (Metric, error) {
|
) (Metric, error) {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if desc.err != nil {
|
||||||
return nil, errInconsistentCardinality
|
return nil, desc.err
|
||||||
|
}
|
||||||
|
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return &constSummary{
|
return &constSummary{
|
||||||
desc: desc,
|
desc: desc,
|
||||||
|
|
14
vendor/github.com/prometheus/client_golang/prometheus/timer.go
generated
vendored
14
vendor/github.com/prometheus/client_golang/prometheus/timer.go
generated
vendored
|
@ -39,10 +39,16 @@ func NewTimer(o Observer) *Timer {
|
||||||
|
|
||||||
// ObserveDuration records the duration passed since the Timer was created with
|
// ObserveDuration records the duration passed since the Timer was created with
|
||||||
// NewTimer. It calls the Observe method of the Observer provided during
|
// NewTimer. It calls the Observe method of the Observer provided during
|
||||||
// construction with the duration in seconds as an argument. ObserveDuration is
|
// construction with the duration in seconds as an argument. The observed
|
||||||
// usually called with a defer statement.
|
// duration is also returned. ObserveDuration is usually called with a defer
|
||||||
func (t *Timer) ObserveDuration() {
|
// statement.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
func (t *Timer) ObserveDuration() time.Duration {
|
||||||
|
d := time.Since(t.begin)
|
||||||
if t.observer != nil {
|
if t.observer != nil {
|
||||||
t.observer.Observe(time.Since(t.begin).Seconds())
|
t.observer.Observe(d.Seconds())
|
||||||
}
|
}
|
||||||
|
return d
|
||||||
}
|
}
|
||||||
|
|
107
vendor/github.com/prometheus/client_golang/prometheus/untyped.go
generated
vendored
107
vendor/github.com/prometheus/client_golang/prometheus/untyped.go
generated
vendored
|
@ -13,113 +13,12 @@
|
||||||
|
|
||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
// Untyped is a Metric that represents a single numerical value that can
|
|
||||||
// arbitrarily go up and down.
|
|
||||||
//
|
|
||||||
// An Untyped metric works the same as a Gauge. The only difference is that to
|
|
||||||
// no type information is implied.
|
|
||||||
//
|
|
||||||
// To create Untyped instances, use NewUntyped.
|
|
||||||
//
|
|
||||||
// Deprecated: The Untyped type is deprecated because it doesn't make sense in
|
|
||||||
// direct instrumentation. If you need to mirror an external metric of unknown
|
|
||||||
// type (usually while writing exporters), Use MustNewConstMetric to create an
|
|
||||||
// untyped metric instance on the fly.
|
|
||||||
type Untyped interface {
|
|
||||||
Metric
|
|
||||||
Collector
|
|
||||||
|
|
||||||
// Set sets the Untyped metric to an arbitrary value.
|
|
||||||
Set(float64)
|
|
||||||
// Inc increments the Untyped metric by 1.
|
|
||||||
Inc()
|
|
||||||
// Dec decrements the Untyped metric by 1.
|
|
||||||
Dec()
|
|
||||||
// Add adds the given value to the Untyped metric. (The value can be
|
|
||||||
// negative, resulting in a decrease.)
|
|
||||||
Add(float64)
|
|
||||||
// Sub subtracts the given value from the Untyped metric. (The value can
|
|
||||||
// be negative, resulting in an increase.)
|
|
||||||
Sub(float64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UntypedOpts is an alias for Opts. See there for doc comments.
|
// UntypedOpts is an alias for Opts. See there for doc comments.
|
||||||
type UntypedOpts Opts
|
type UntypedOpts Opts
|
||||||
|
|
||||||
// NewUntyped creates a new Untyped metric from the provided UntypedOpts.
|
// UntypedFunc works like GaugeFunc but the collected metric is of type
|
||||||
func NewUntyped(opts UntypedOpts) Untyped {
|
// "Untyped". UntypedFunc is useful to mirror an external metric of unknown
|
||||||
return newValue(NewDesc(
|
// type.
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
opts.Help,
|
|
||||||
nil,
|
|
||||||
opts.ConstLabels,
|
|
||||||
), UntypedValue, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UntypedVec is a Collector that bundles a set of Untyped metrics that all
|
|
||||||
// share the same Desc, but have different values for their variable
|
|
||||||
// labels. This is used if you want to count the same thing partitioned by
|
|
||||||
// various dimensions. Create instances with NewUntypedVec.
|
|
||||||
type UntypedVec struct {
|
|
||||||
*MetricVec
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUntypedVec creates a new UntypedVec based on the provided UntypedOpts and
|
|
||||||
// partitioned by the given label names. At least one label name must be
|
|
||||||
// provided.
|
|
||||||
func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec {
|
|
||||||
desc := NewDesc(
|
|
||||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
|
||||||
opts.Help,
|
|
||||||
labelNames,
|
|
||||||
opts.ConstLabels,
|
|
||||||
)
|
|
||||||
return &UntypedVec{
|
|
||||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
|
||||||
return newValue(desc, UntypedValue, 0, lvs...)
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMetricWithLabelValues replaces the method of the same name in
|
|
||||||
// MetricVec. The difference is that this method returns an Untyped and not a
|
|
||||||
// Metric so that no type conversion is required.
|
|
||||||
func (m *UntypedVec) GetMetricWithLabelValues(lvs ...string) (Untyped, error) {
|
|
||||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
|
||||||
if metric != nil {
|
|
||||||
return metric.(Untyped), err
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
|
||||||
// difference is that this method returns an Untyped and not a Metric so that no
|
|
||||||
// type conversion is required.
|
|
||||||
func (m *UntypedVec) GetMetricWith(labels Labels) (Untyped, error) {
|
|
||||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
|
||||||
if metric != nil {
|
|
||||||
return metric.(Untyped), err
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLabelValues works as GetMetricWithLabelValues, but panics where
|
|
||||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
|
||||||
// error, WithLabelValues allows shortcuts like
|
|
||||||
// myVec.WithLabelValues("404", "GET").Add(42)
|
|
||||||
func (m *UntypedVec) WithLabelValues(lvs ...string) Untyped {
|
|
||||||
return m.MetricVec.WithLabelValues(lvs...).(Untyped)
|
|
||||||
}
|
|
||||||
|
|
||||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
|
||||||
// returned an error. By not returning an error, With allows shortcuts like
|
|
||||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
|
||||||
func (m *UntypedVec) With(labels Labels) Untyped {
|
|
||||||
return m.MetricVec.With(labels).(Untyped)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UntypedFunc is an Untyped whose value is determined at collect time by
|
|
||||||
// calling a provided function.
|
|
||||||
//
|
//
|
||||||
// To create UntypedFunc instances, use NewUntypedFunc.
|
// To create UntypedFunc instances, use NewUntypedFunc.
|
||||||
type UntypedFunc interface {
|
type UntypedFunc interface {
|
||||||
|
|
99
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
99
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
|
@ -14,16 +14,12 @@
|
||||||
package prometheus
|
package prometheus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"sort"
|
"sort"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValueType is an enumeration of metric types that represent a simple value.
|
// ValueType is an enumeration of metric types that represent a simple value.
|
||||||
|
@ -37,81 +33,6 @@ const (
|
||||||
UntypedValue
|
UntypedValue
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInconsistentCardinality = errors.New("inconsistent label cardinality")
|
|
||||||
|
|
||||||
// value is a generic metric for simple values. It implements Metric, Collector,
|
|
||||||
// Counter, Gauge, and Untyped. Its effective type is determined by
|
|
||||||
// ValueType. This is a low-level building block used by the library to back the
|
|
||||||
// implementations of Counter, Gauge, and Untyped.
|
|
||||||
type value struct {
|
|
||||||
// valBits contains the bits of the represented float64 value. It has
|
|
||||||
// to go first in the struct to guarantee alignment for atomic
|
|
||||||
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
|
||||||
valBits uint64
|
|
||||||
|
|
||||||
selfCollector
|
|
||||||
|
|
||||||
desc *Desc
|
|
||||||
valType ValueType
|
|
||||||
labelPairs []*dto.LabelPair
|
|
||||||
}
|
|
||||||
|
|
||||||
// newValue returns a newly allocated value with the given Desc, ValueType,
|
|
||||||
// sample value and label values. It panics if the number of label
|
|
||||||
// values is different from the number of variable labels in Desc.
|
|
||||||
func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value {
|
|
||||||
if len(labelValues) != len(desc.variableLabels) {
|
|
||||||
panic(errInconsistentCardinality)
|
|
||||||
}
|
|
||||||
result := &value{
|
|
||||||
desc: desc,
|
|
||||||
valType: valueType,
|
|
||||||
valBits: math.Float64bits(val),
|
|
||||||
labelPairs: makeLabelPairs(desc, labelValues),
|
|
||||||
}
|
|
||||||
result.init(result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Desc() *Desc {
|
|
||||||
return v.desc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Set(val float64) {
|
|
||||||
atomic.StoreUint64(&v.valBits, math.Float64bits(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) SetToCurrentTime() {
|
|
||||||
v.Set(float64(time.Now().UnixNano()) / 1e9)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Inc() {
|
|
||||||
v.Add(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Dec() {
|
|
||||||
v.Add(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Add(val float64) {
|
|
||||||
for {
|
|
||||||
oldBits := atomic.LoadUint64(&v.valBits)
|
|
||||||
newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
|
|
||||||
if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Sub(val float64) {
|
|
||||||
v.Add(val * -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *value) Write(out *dto.Metric) error {
|
|
||||||
val := math.Float64frombits(atomic.LoadUint64(&v.valBits))
|
|
||||||
return populateMetric(v.valType, val, v.labelPairs, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// valueFunc is a generic metric for simple values retrieved on collect time
|
// valueFunc is a generic metric for simple values retrieved on collect time
|
||||||
// from a function. It implements Metric and Collector. Its effective type is
|
// from a function. It implements Metric and Collector. Its effective type is
|
||||||
// determined by ValueType. This is a low-level building block used by the
|
// determined by ValueType. This is a low-level building block used by the
|
||||||
|
@ -156,10 +77,14 @@ func (v *valueFunc) Write(out *dto.Metric) error {
|
||||||
// operations. However, when implementing custom Collectors, it is useful as a
|
// operations. However, when implementing custom Collectors, it is useful as a
|
||||||
// throw-away metric that is generated on the fly to send it to Prometheus in
|
// throw-away metric that is generated on the fly to send it to Prometheus in
|
||||||
// the Collect method. NewConstMetric returns an error if the length of
|
// the Collect method. NewConstMetric returns an error if the length of
|
||||||
// labelValues is not consistent with the variable labels in Desc.
|
// labelValues is not consistent with the variable labels in Desc or if Desc is
|
||||||
|
// invalid.
|
||||||
func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
|
func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if desc.err != nil {
|
||||||
return nil, errInconsistentCardinality
|
return nil, desc.err
|
||||||
|
}
|
||||||
|
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return &constMetric{
|
return &constMetric{
|
||||||
desc: desc,
|
desc: desc,
|
||||||
|
@ -231,9 +156,7 @@ func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||||
Value: proto.String(labelValues[i]),
|
Value: proto.String(labelValues[i]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, lp := range desc.constLabelPairs {
|
labelPairs = append(labelPairs, desc.constLabelPairs...)
|
||||||
labelPairs = append(labelPairs, lp)
|
sort.Sort(labelPairSorter(labelPairs))
|
||||||
}
|
|
||||||
sort.Sort(LabelPairSorter(labelPairs))
|
|
||||||
return labelPairs
|
return labelPairs
|
||||||
}
|
}
|
||||||
|
|
516
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
516
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
|
@ -20,33 +20,180 @@ import (
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetricVec is a Collector to bundle metrics of the same name that
|
// metricVec is a Collector to bundle metrics of the same name that differ in
|
||||||
// differ in their label values. MetricVec is usually not used directly but as a
|
// their label values. metricVec is not used directly (and therefore
|
||||||
// building block for implementations of vectors of a given metric
|
// unexported). It is used as a building block for implementations of vectors of
|
||||||
// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
|
// a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
|
||||||
// provided in this package.
|
// It also handles label currying. It uses basicMetricVec internally.
|
||||||
type MetricVec struct {
|
type metricVec struct {
|
||||||
mtx sync.RWMutex // Protects the children.
|
*metricMap
|
||||||
children map[uint64][]metricWithLabelValues
|
|
||||||
desc *Desc
|
|
||||||
|
|
||||||
newMetric func(labelValues ...string) Metric
|
curry []curriedLabelValue
|
||||||
hashAdd func(h uint64, s string) uint64 // replace hash function for testing collision handling
|
|
||||||
|
// hashAdd and hashAddByte can be replaced for testing collision handling.
|
||||||
|
hashAdd func(h uint64, s string) uint64
|
||||||
hashAddByte func(h uint64, b byte) uint64
|
hashAddByte func(h uint64, b byte) uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMetricVec returns an initialized MetricVec. The concrete value is
|
// newMetricVec returns an initialized metricVec.
|
||||||
// returned for embedding into another struct.
|
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
||||||
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
return &metricVec{
|
||||||
return &MetricVec{
|
metricMap: &metricMap{
|
||||||
children: map[uint64][]metricWithLabelValues{},
|
metrics: map[uint64][]metricWithLabelValues{},
|
||||||
desc: desc,
|
desc: desc,
|
||||||
newMetric: newMetric,
|
newMetric: newMetric,
|
||||||
|
},
|
||||||
hashAdd: hashAdd,
|
hashAdd: hashAdd,
|
||||||
hashAddByte: hashAddByte,
|
hashAddByte: hashAddByte,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteLabelValues removes the metric where the variable labels are the same
|
||||||
|
// as those passed in as labels (same order as the VariableLabels in Desc). It
|
||||||
|
// returns true if a metric was deleted.
|
||||||
|
//
|
||||||
|
// It is not an error if the number of label values is not the same as the
|
||||||
|
// number of VariableLabels in Desc. However, such inconsistent label count can
|
||||||
|
// never match an actual metric, so the method will always return false in that
|
||||||
|
// case.
|
||||||
|
//
|
||||||
|
// Note that for more than one label value, this method is prone to mistakes
|
||||||
|
// caused by an incorrect order of arguments. Consider Delete(Labels) as an
|
||||||
|
// alternative to avoid that type of mistake. For higher label numbers, the
|
||||||
|
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||||
|
// with a performance overhead (for creating and processing the Labels map).
|
||||||
|
// See also the CounterVec example.
|
||||||
|
func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
||||||
|
h, err := m.hashLabelValues(lvs)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the metric where the variable labels are the same as those
|
||||||
|
// passed in as labels. It returns true if a metric was deleted.
|
||||||
|
//
|
||||||
|
// It is not an error if the number and names of the Labels are inconsistent
|
||||||
|
// with those of the VariableLabels in Desc. However, such inconsistent Labels
|
||||||
|
// can never match an actual metric, so the method will always return false in
|
||||||
|
// that case.
|
||||||
|
//
|
||||||
|
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
||||||
|
// there for pros and cons of the two methods.
|
||||||
|
func (m *metricVec) Delete(labels Labels) bool {
|
||||||
|
h, err := m.hashLabels(labels)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
||||||
|
var (
|
||||||
|
newCurry []curriedLabelValue
|
||||||
|
oldCurry = m.curry
|
||||||
|
iCurry int
|
||||||
|
)
|
||||||
|
for i, label := range m.desc.variableLabels {
|
||||||
|
val, ok := labels[label]
|
||||||
|
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
|
||||||
|
if ok {
|
||||||
|
return nil, fmt.Errorf("label name %q is already curried", label)
|
||||||
|
}
|
||||||
|
newCurry = append(newCurry, oldCurry[iCurry])
|
||||||
|
iCurry++
|
||||||
|
} else {
|
||||||
|
if !ok {
|
||||||
|
continue // Label stays uncurried.
|
||||||
|
}
|
||||||
|
newCurry = append(newCurry, curriedLabelValue{i, val})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
|
||||||
|
return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &metricVec{
|
||||||
|
metricMap: m.metricMap,
|
||||||
|
curry: newCurry,
|
||||||
|
hashAdd: m.hashAdd,
|
||||||
|
hashAddByte: m.hashAddByte,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||||
|
h, err := m.hashLabelValues(lvs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
||||||
|
h, err := m.hashLabels(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||||
|
if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
h = hashNew()
|
||||||
|
curry = m.curry
|
||||||
|
iVals, iCurry int
|
||||||
|
)
|
||||||
|
for i := 0; i < len(m.desc.variableLabels); i++ {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
h = m.hashAdd(h, curry[iCurry].value)
|
||||||
|
iCurry++
|
||||||
|
} else {
|
||||||
|
h = m.hashAdd(h, vals[iVals])
|
||||||
|
iVals++
|
||||||
|
}
|
||||||
|
h = m.hashAddByte(h, model.SeparatorByte)
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
|
||||||
|
if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
h = hashNew()
|
||||||
|
curry = m.curry
|
||||||
|
iCurry int
|
||||||
|
)
|
||||||
|
for i, label := range m.desc.variableLabels {
|
||||||
|
val, ok := labels[label]
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
if ok {
|
||||||
|
return 0, fmt.Errorf("label name %q is already curried", label)
|
||||||
|
}
|
||||||
|
h = m.hashAdd(h, curry[iCurry].value)
|
||||||
|
iCurry++
|
||||||
|
} else {
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("label name %q missing in label map", label)
|
||||||
|
}
|
||||||
|
h = m.hashAdd(h, val)
|
||||||
|
}
|
||||||
|
h = m.hashAddByte(h, model.SeparatorByte)
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
// metricWithLabelValues provides the metric and its label values for
|
// metricWithLabelValues provides the metric and its label values for
|
||||||
// disambiguation on hash collision.
|
// disambiguation on hash collision.
|
||||||
type metricWithLabelValues struct {
|
type metricWithLabelValues struct {
|
||||||
|
@ -54,166 +201,72 @@ type metricWithLabelValues struct {
|
||||||
metric Metric
|
metric Metric
|
||||||
}
|
}
|
||||||
|
|
||||||
// Describe implements Collector. The length of the returned slice
|
// curriedLabelValue sets the curried value for a label at the given index.
|
||||||
// is always one.
|
type curriedLabelValue struct {
|
||||||
func (m *MetricVec) Describe(ch chan<- *Desc) {
|
index int
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// metricMap is a helper for metricVec and shared between differently curried
|
||||||
|
// metricVecs.
|
||||||
|
type metricMap struct {
|
||||||
|
mtx sync.RWMutex // Protects metrics.
|
||||||
|
metrics map[uint64][]metricWithLabelValues
|
||||||
|
desc *Desc
|
||||||
|
newMetric func(labelValues ...string) Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describe implements Collector. It will send exactly one Desc to the provided
|
||||||
|
// channel.
|
||||||
|
func (m *metricMap) Describe(ch chan<- *Desc) {
|
||||||
ch <- m.desc
|
ch <- m.desc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect implements Collector.
|
// Collect implements Collector.
|
||||||
func (m *MetricVec) Collect(ch chan<- Metric) {
|
func (m *metricMap) Collect(ch chan<- Metric) {
|
||||||
m.mtx.RLock()
|
m.mtx.RLock()
|
||||||
defer m.mtx.RUnlock()
|
defer m.mtx.RUnlock()
|
||||||
|
|
||||||
for _, metrics := range m.children {
|
for _, metrics := range m.metrics {
|
||||||
for _, metric := range metrics {
|
for _, metric := range metrics {
|
||||||
ch <- metric.metric
|
ch <- metric.metric
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMetricWithLabelValues returns the Metric for the given slice of label
|
// Reset deletes all metrics in this vector.
|
||||||
// values (same order as the VariableLabels in Desc). If that combination of
|
func (m *metricMap) Reset() {
|
||||||
// label values is accessed for the first time, a new Metric is created.
|
|
||||||
//
|
|
||||||
// It is possible to call this method without using the returned Metric to only
|
|
||||||
// create the new Metric but leave it at its start value (e.g. a Summary or
|
|
||||||
// Histogram without any observations). See also the SummaryVec example.
|
|
||||||
//
|
|
||||||
// Keeping the Metric for later use is possible (and should be considered if
|
|
||||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
|
||||||
// Delete can be used to delete the Metric from the MetricVec. In that case, the
|
|
||||||
// Metric will still exist, but it will not be exported anymore, even if a
|
|
||||||
// Metric with the same label values is created later. See also the CounterVec
|
|
||||||
// example.
|
|
||||||
//
|
|
||||||
// An error is returned if the number of label values is not the same as the
|
|
||||||
// number of VariableLabels in Desc.
|
|
||||||
//
|
|
||||||
// Note that for more than one label value, this method is prone to mistakes
|
|
||||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
|
||||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
|
||||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
|
||||||
// with a performance overhead (for creating and processing the Labels map).
|
|
||||||
// See also the GaugeVec example.
|
|
||||||
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
|
||||||
h, err := m.hashLabelValues(lvs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.getOrCreateMetricWithLabelValues(h, lvs), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMetricWith returns the Metric for the given Labels map (the label names
|
|
||||||
// must match those of the VariableLabels in Desc). If that label map is
|
|
||||||
// accessed for the first time, a new Metric is created. Implications of
|
|
||||||
// creating a Metric without using it and keeping the Metric for later use are
|
|
||||||
// the same as for GetMetricWithLabelValues.
|
|
||||||
//
|
|
||||||
// An error is returned if the number and names of the Labels are inconsistent
|
|
||||||
// with those of the VariableLabels in Desc.
|
|
||||||
//
|
|
||||||
// This method is used for the same purpose as
|
|
||||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
|
||||||
// methods.
|
|
||||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
|
||||||
h, err := m.hashLabels(labels)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.getOrCreateMetricWithLabels(h, labels), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
|
|
||||||
// occurs. The method allows neat syntax like:
|
|
||||||
// httpReqs.WithLabelValues("404", "POST").Inc()
|
|
||||||
func (m *MetricVec) WithLabelValues(lvs ...string) Metric {
|
|
||||||
metric, err := m.GetMetricWithLabelValues(lvs...)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return metric
|
|
||||||
}
|
|
||||||
|
|
||||||
// With works as GetMetricWith, but panics if an error occurs. The method allows
|
|
||||||
// neat syntax like:
|
|
||||||
// httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
|
|
||||||
func (m *MetricVec) With(labels Labels) Metric {
|
|
||||||
metric, err := m.GetMetricWith(labels)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return metric
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteLabelValues removes the metric where the variable labels are the same
|
|
||||||
// as those passed in as labels (same order as the VariableLabels in Desc). It
|
|
||||||
// returns true if a metric was deleted.
|
|
||||||
//
|
|
||||||
// It is not an error if the number of label values is not the same as the
|
|
||||||
// number of VariableLabels in Desc. However, such inconsistent label count can
|
|
||||||
// never match an actual Metric, so the method will always return false in that
|
|
||||||
// case.
|
|
||||||
//
|
|
||||||
// Note that for more than one label value, this method is prone to mistakes
|
|
||||||
// caused by an incorrect order of arguments. Consider Delete(Labels) as an
|
|
||||||
// alternative to avoid that type of mistake. For higher label numbers, the
|
|
||||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
|
||||||
// with a performance overhead (for creating and processing the Labels map).
|
|
||||||
// See also the CounterVec example.
|
|
||||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
|
||||||
m.mtx.Lock()
|
m.mtx.Lock()
|
||||||
defer m.mtx.Unlock()
|
defer m.mtx.Unlock()
|
||||||
|
|
||||||
h, err := m.hashLabelValues(lvs)
|
for h := range m.metrics {
|
||||||
if err != nil {
|
delete(m.metrics, h)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return m.deleteByHashWithLabelValues(h, lvs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes the metric where the variable labels are the same as those
|
|
||||||
// passed in as labels. It returns true if a metric was deleted.
|
|
||||||
//
|
|
||||||
// It is not an error if the number and names of the Labels are inconsistent
|
|
||||||
// with those of the VariableLabels in the Desc of the MetricVec. However, such
|
|
||||||
// inconsistent Labels can never match an actual Metric, so the method will
|
|
||||||
// always return false in that case.
|
|
||||||
//
|
|
||||||
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
|
||||||
// there for pros and cons of the two methods.
|
|
||||||
func (m *MetricVec) Delete(labels Labels) bool {
|
|
||||||
m.mtx.Lock()
|
|
||||||
defer m.mtx.Unlock()
|
|
||||||
|
|
||||||
h, err := m.hashLabels(labels)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.deleteByHashWithLabels(h, labels)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteByHashWithLabelValues removes the metric from the hash bucket h. If
|
// deleteByHashWithLabelValues removes the metric from the hash bucket h. If
|
||||||
// there are multiple matches in the bucket, use lvs to select a metric and
|
// there are multiple matches in the bucket, use lvs to select a metric and
|
||||||
// remove only that metric.
|
// remove only that metric.
|
||||||
func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
|
func (m *metricMap) deleteByHashWithLabelValues(
|
||||||
metrics, ok := m.children[h]
|
h uint64, lvs []string, curry []curriedLabelValue,
|
||||||
|
) bool {
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
i := m.findMetricWithLabelValues(metrics, lvs)
|
i := findMetricWithLabelValues(metrics, lvs, curry)
|
||||||
if i >= len(metrics) {
|
if i >= len(metrics) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(metrics) > 1 {
|
if len(metrics) > 1 {
|
||||||
m.children[h] = append(metrics[:i], metrics[i+1:]...)
|
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||||
} else {
|
} else {
|
||||||
delete(m.children, h)
|
delete(m.metrics, h)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -221,69 +274,38 @@ func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
|
||||||
// deleteByHashWithLabels removes the metric from the hash bucket h. If there
|
// deleteByHashWithLabels removes the metric from the hash bucket h. If there
|
||||||
// are multiple matches in the bucket, use lvs to select a metric and remove
|
// are multiple matches in the bucket, use lvs to select a metric and remove
|
||||||
// only that metric.
|
// only that metric.
|
||||||
func (m *MetricVec) deleteByHashWithLabels(h uint64, labels Labels) bool {
|
func (m *metricMap) deleteByHashWithLabels(
|
||||||
metrics, ok := m.children[h]
|
h uint64, labels Labels, curry []curriedLabelValue,
|
||||||
|
) bool {
|
||||||
|
m.mtx.Lock()
|
||||||
|
defer m.mtx.Unlock()
|
||||||
|
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
i := m.findMetricWithLabels(metrics, labels)
|
i := findMetricWithLabels(m.desc, metrics, labels, curry)
|
||||||
if i >= len(metrics) {
|
if i >= len(metrics) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(metrics) > 1 {
|
if len(metrics) > 1 {
|
||||||
m.children[h] = append(metrics[:i], metrics[i+1:]...)
|
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||||
} else {
|
} else {
|
||||||
delete(m.children, h)
|
delete(m.metrics, h)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset deletes all metrics in this vector.
|
|
||||||
func (m *MetricVec) Reset() {
|
|
||||||
m.mtx.Lock()
|
|
||||||
defer m.mtx.Unlock()
|
|
||||||
|
|
||||||
for h := range m.children {
|
|
||||||
delete(m.children, h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
|
||||||
if len(vals) != len(m.desc.variableLabels) {
|
|
||||||
return 0, errInconsistentCardinality
|
|
||||||
}
|
|
||||||
h := hashNew()
|
|
||||||
for _, val := range vals {
|
|
||||||
h = m.hashAdd(h, val)
|
|
||||||
h = m.hashAddByte(h, model.SeparatorByte)
|
|
||||||
}
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
|
||||||
if len(labels) != len(m.desc.variableLabels) {
|
|
||||||
return 0, errInconsistentCardinality
|
|
||||||
}
|
|
||||||
h := hashNew()
|
|
||||||
for _, label := range m.desc.variableLabels {
|
|
||||||
val, ok := labels[label]
|
|
||||||
if !ok {
|
|
||||||
return 0, fmt.Errorf("label name %q missing in label map", label)
|
|
||||||
}
|
|
||||||
h = m.hashAdd(h, val)
|
|
||||||
h = m.hashAddByte(h, model.SeparatorByte)
|
|
||||||
}
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
|
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
|
||||||
// or creates it and returns the new one.
|
// or creates it and returns the new one.
|
||||||
//
|
//
|
||||||
// This function holds the mutex.
|
// This function holds the mutex.
|
||||||
func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric {
|
func (m *metricMap) getOrCreateMetricWithLabelValues(
|
||||||
|
hash uint64, lvs []string, curry []curriedLabelValue,
|
||||||
|
) Metric {
|
||||||
m.mtx.RLock()
|
m.mtx.RLock()
|
||||||
metric, ok := m.getMetricWithLabelValues(hash, lvs)
|
metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry)
|
||||||
m.mtx.RUnlock()
|
m.mtx.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
return metric
|
return metric
|
||||||
|
@ -291,13 +313,11 @@ func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string)
|
||||||
|
|
||||||
m.mtx.Lock()
|
m.mtx.Lock()
|
||||||
defer m.mtx.Unlock()
|
defer m.mtx.Unlock()
|
||||||
metric, ok = m.getMetricWithLabelValues(hash, lvs)
|
metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Copy to avoid allocation in case wo don't go down this code path.
|
inlinedLVs := inlineLabelValues(lvs, curry)
|
||||||
copiedLVs := make([]string, len(lvs))
|
metric = m.newMetric(inlinedLVs...)
|
||||||
copy(copiedLVs, lvs)
|
m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric})
|
||||||
metric = m.newMetric(copiedLVs...)
|
|
||||||
m.children[hash] = append(m.children[hash], metricWithLabelValues{values: copiedLVs, metric: metric})
|
|
||||||
}
|
}
|
||||||
return metric
|
return metric
|
||||||
}
|
}
|
||||||
|
@ -306,9 +326,11 @@ func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string)
|
||||||
// or creates it and returns the new one.
|
// or creates it and returns the new one.
|
||||||
//
|
//
|
||||||
// This function holds the mutex.
|
// This function holds the mutex.
|
||||||
func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric {
|
func (m *metricMap) getOrCreateMetricWithLabels(
|
||||||
|
hash uint64, labels Labels, curry []curriedLabelValue,
|
||||||
|
) Metric {
|
||||||
m.mtx.RLock()
|
m.mtx.RLock()
|
||||||
metric, ok := m.getMetricWithLabels(hash, labels)
|
metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry)
|
||||||
m.mtx.RUnlock()
|
m.mtx.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
return metric
|
return metric
|
||||||
|
@ -316,33 +338,37 @@ func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metr
|
||||||
|
|
||||||
m.mtx.Lock()
|
m.mtx.Lock()
|
||||||
defer m.mtx.Unlock()
|
defer m.mtx.Unlock()
|
||||||
metric, ok = m.getMetricWithLabels(hash, labels)
|
metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry)
|
||||||
if !ok {
|
if !ok {
|
||||||
lvs := m.extractLabelValues(labels)
|
lvs := extractLabelValues(m.desc, labels, curry)
|
||||||
metric = m.newMetric(lvs...)
|
metric = m.newMetric(lvs...)
|
||||||
m.children[hash] = append(m.children[hash], metricWithLabelValues{values: lvs, metric: metric})
|
m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric})
|
||||||
}
|
}
|
||||||
return metric
|
return metric
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMetricWithLabelValues gets a metric while handling possible collisions in
|
// getMetricWithHashAndLabelValues gets a metric while handling possible
|
||||||
// the hash space. Must be called while holding read mutex.
|
// collisions in the hash space. Must be called while holding the read mutex.
|
||||||
func (m *MetricVec) getMetricWithLabelValues(h uint64, lvs []string) (Metric, bool) {
|
func (m *metricMap) getMetricWithHashAndLabelValues(
|
||||||
metrics, ok := m.children[h]
|
h uint64, lvs []string, curry []curriedLabelValue,
|
||||||
|
) (Metric, bool) {
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
if ok {
|
if ok {
|
||||||
if i := m.findMetricWithLabelValues(metrics, lvs); i < len(metrics) {
|
if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) {
|
||||||
return metrics[i].metric, true
|
return metrics[i].metric, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMetricWithLabels gets a metric while handling possible collisions in
|
// getMetricWithHashAndLabels gets a metric while handling possible collisions in
|
||||||
// the hash space. Must be called while holding read mutex.
|
// the hash space. Must be called while holding read mutex.
|
||||||
func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) {
|
func (m *metricMap) getMetricWithHashAndLabels(
|
||||||
metrics, ok := m.children[h]
|
h uint64, labels Labels, curry []curriedLabelValue,
|
||||||
|
) (Metric, bool) {
|
||||||
|
metrics, ok := m.metrics[h]
|
||||||
if ok {
|
if ok {
|
||||||
if i := m.findMetricWithLabels(metrics, labels); i < len(metrics) {
|
if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) {
|
||||||
return metrics[i].metric, true
|
return metrics[i].metric, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,9 +377,11 @@ func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool)
|
||||||
|
|
||||||
// findMetricWithLabelValues returns the index of the matching metric or
|
// findMetricWithLabelValues returns the index of the matching metric or
|
||||||
// len(metrics) if not found.
|
// len(metrics) if not found.
|
||||||
func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int {
|
func findMetricWithLabelValues(
|
||||||
|
metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue,
|
||||||
|
) int {
|
||||||
for i, metric := range metrics {
|
for i, metric := range metrics {
|
||||||
if m.matchLabelValues(metric.values, lvs) {
|
if matchLabelValues(metric.values, lvs, curry) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,32 +390,51 @@ func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, l
|
||||||
|
|
||||||
// findMetricWithLabels returns the index of the matching metric or len(metrics)
|
// findMetricWithLabels returns the index of the matching metric or len(metrics)
|
||||||
// if not found.
|
// if not found.
|
||||||
func (m *MetricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int {
|
func findMetricWithLabels(
|
||||||
|
desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue,
|
||||||
|
) int {
|
||||||
for i, metric := range metrics {
|
for i, metric := range metrics {
|
||||||
if m.matchLabels(metric.values, labels) {
|
if matchLabels(desc, metric.values, labels, curry) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return len(metrics)
|
return len(metrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MetricVec) matchLabelValues(values []string, lvs []string) bool {
|
func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool {
|
||||||
if len(values) != len(lvs) {
|
if len(values) != len(lvs)+len(curry) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
var iLVs, iCurry int
|
||||||
for i, v := range values {
|
for i, v := range values {
|
||||||
if v != lvs[i] {
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
if v != curry[iCurry].value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v != lvs[iLVs] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
iLVs++
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MetricVec) matchLabels(values []string, labels Labels) bool {
|
func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
|
||||||
if len(labels) != len(values) {
|
if len(values) != len(labels)+len(curry) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i, k := range m.desc.variableLabels {
|
iCurry := 0
|
||||||
|
for i, k := range desc.variableLabels {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
if values[i] != curry[iCurry].value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
if values[i] != labels[k] {
|
if values[i] != labels[k] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -395,10 +442,31 @@ func (m *MetricVec) matchLabels(values []string, labels Labels) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MetricVec) extractLabelValues(labels Labels) []string {
|
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
|
||||||
labelValues := make([]string, len(labels))
|
labelValues := make([]string, len(labels)+len(curry))
|
||||||
for i, k := range m.desc.variableLabels {
|
iCurry := 0
|
||||||
|
for i, k := range desc.variableLabels {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
labelValues[i] = curry[iCurry].value
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
labelValues[i] = labels[k]
|
labelValues[i] = labels[k]
|
||||||
}
|
}
|
||||||
return labelValues
|
return labelValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
|
||||||
|
labelValues := make([]string, len(lvs)+len(curry))
|
||||||
|
var iCurry, iLVs int
|
||||||
|
for i := range labelValues {
|
||||||
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
|
labelValues[i] = curry[iCurry].value
|
||||||
|
iCurry++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelValues[i] = lvs[iLVs]
|
||||||
|
iLVs++
|
||||||
|
}
|
||||||
|
return labelValues
|
||||||
|
}
|
||||||
|
|
179
vendor/github.com/prometheus/client_golang/prometheus/wrap.go
generated
vendored
Normal file
179
vendor/github.com/prometheus/client_golang/prometheus/wrap.go
generated
vendored
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WrapRegistererWith returns a Registerer wrapping the provided
|
||||||
|
// Registerer. Collectors registered with the returned Registerer will be
|
||||||
|
// registered with the wrapped Registerer in a modified way. The modified
|
||||||
|
// Collector adds the provided Labels to all Metrics it collects (as
|
||||||
|
// ConstLabels). The Metrics collected by the unmodified Collector must not
|
||||||
|
// duplicate any of those labels.
|
||||||
|
//
|
||||||
|
// WrapRegistererWith provides a way to add fixed labels to a subset of
|
||||||
|
// Collectors. It should not be used to add fixed labels to all metrics exposed.
|
||||||
|
//
|
||||||
|
// The Collector example demonstrates a use of WrapRegistererWith.
|
||||||
|
func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
|
||||||
|
return &wrappingRegisterer{
|
||||||
|
wrappedRegisterer: reg,
|
||||||
|
labels: labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapRegistererWithPrefix returns a Registerer wrapping the provided
|
||||||
|
// Registerer. Collectors registered with the returned Registerer will be
|
||||||
|
// registered with the wrapped Registerer in a modified way. The modified
|
||||||
|
// Collector adds the provided prefix to the name of all Metrics it collects.
|
||||||
|
//
|
||||||
|
// WrapRegistererWithPrefix is useful to have one place to prefix all metrics of
|
||||||
|
// a sub-system. To make this work, register metrics of the sub-system with the
|
||||||
|
// wrapping Registerer returned by WrapRegistererWithPrefix. It is rarely useful
|
||||||
|
// to use the same prefix for all metrics exposed. In particular, do not prefix
|
||||||
|
// metric names that are standardized across applications, as that would break
|
||||||
|
// horizontal monitoring, for example the metrics provided by the Go collector
|
||||||
|
// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
|
||||||
|
// fact, those metrics are already prefixed with “go_” or “process_”,
|
||||||
|
// respectively.)
|
||||||
|
func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
|
||||||
|
return &wrappingRegisterer{
|
||||||
|
wrappedRegisterer: reg,
|
||||||
|
prefix: prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappingRegisterer struct {
|
||||||
|
wrappedRegisterer Registerer
|
||||||
|
prefix string
|
||||||
|
labels Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wrappingRegisterer) Register(c Collector) error {
|
||||||
|
return r.wrappedRegisterer.Register(&wrappingCollector{
|
||||||
|
wrappedCollector: c,
|
||||||
|
prefix: r.prefix,
|
||||||
|
labels: r.labels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
|
||||||
|
for _, c := range cs {
|
||||||
|
if err := r.Register(c); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *wrappingRegisterer) Unregister(c Collector) bool {
|
||||||
|
return r.wrappedRegisterer.Unregister(&wrappingCollector{
|
||||||
|
wrappedCollector: c,
|
||||||
|
prefix: r.prefix,
|
||||||
|
labels: r.labels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappingCollector struct {
|
||||||
|
wrappedCollector Collector
|
||||||
|
prefix string
|
||||||
|
labels Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wrappingCollector) Collect(ch chan<- Metric) {
|
||||||
|
wrappedCh := make(chan Metric)
|
||||||
|
go func() {
|
||||||
|
c.wrappedCollector.Collect(wrappedCh)
|
||||||
|
close(wrappedCh)
|
||||||
|
}()
|
||||||
|
for m := range wrappedCh {
|
||||||
|
ch <- &wrappingMetric{
|
||||||
|
wrappedMetric: m,
|
||||||
|
prefix: c.prefix,
|
||||||
|
labels: c.labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wrappingCollector) Describe(ch chan<- *Desc) {
|
||||||
|
wrappedCh := make(chan *Desc)
|
||||||
|
go func() {
|
||||||
|
c.wrappedCollector.Describe(wrappedCh)
|
||||||
|
close(wrappedCh)
|
||||||
|
}()
|
||||||
|
for desc := range wrappedCh {
|
||||||
|
ch <- wrapDesc(desc, c.prefix, c.labels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappingMetric struct {
|
||||||
|
wrappedMetric Metric
|
||||||
|
prefix string
|
||||||
|
labels Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wrappingMetric) Desc() *Desc {
|
||||||
|
return wrapDesc(m.wrappedMetric.Desc(), m.prefix, m.labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *wrappingMetric) Write(out *dto.Metric) error {
|
||||||
|
if err := m.wrappedMetric.Write(out); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(m.labels) == 0 {
|
||||||
|
// No wrapping labels.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for ln, lv := range m.labels {
|
||||||
|
out.Label = append(out.Label, &dto.LabelPair{
|
||||||
|
Name: proto.String(ln),
|
||||||
|
Value: proto.String(lv),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Sort(labelPairSorter(out.Label))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc {
|
||||||
|
constLabels := Labels{}
|
||||||
|
for _, lp := range desc.constLabelPairs {
|
||||||
|
constLabels[*lp.Name] = *lp.Value
|
||||||
|
}
|
||||||
|
for ln, lv := range labels {
|
||||||
|
if _, alreadyUsed := constLabels[ln]; alreadyUsed {
|
||||||
|
return &Desc{
|
||||||
|
fqName: desc.fqName,
|
||||||
|
help: desc.help,
|
||||||
|
variableLabels: desc.variableLabels,
|
||||||
|
constLabelPairs: desc.constLabelPairs,
|
||||||
|
err: fmt.Errorf("attempted wrapping with already existing label name %q", ln),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constLabels[ln] = lv
|
||||||
|
}
|
||||||
|
// NewDesc will do remaining validations.
|
||||||
|
newDesc := NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels)
|
||||||
|
// Propagate errors if there was any. This will override any errer
|
||||||
|
// created by NewDesc above, i.e. earlier errors get precedence.
|
||||||
|
if desc.err != nil {
|
||||||
|
newDesc.err = desc.err
|
||||||
|
}
|
||||||
|
return newDesc
|
||||||
|
}
|
379
vendor/github.com/prometheus/client_model/go/metrics.pb.go
generated
vendored
379
vendor/github.com/prometheus/client_model/go/metrics.pb.go
generated
vendored
|
@ -1,34 +1,23 @@
|
||||||
// Code generated by protoc-gen-go.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: metrics.proto
|
// source: metrics.proto
|
||||||
// DO NOT EDIT!
|
|
||||||
|
|
||||||
/*
|
package io_prometheus_client // import "github.com/prometheus/client_model/go"
|
||||||
Package io_prometheus_client is a generated protocol buffer package.
|
|
||||||
|
|
||||||
It is generated from these files:
|
|
||||||
metrics.proto
|
|
||||||
|
|
||||||
It has these top-level messages:
|
|
||||||
LabelPair
|
|
||||||
Gauge
|
|
||||||
Counter
|
|
||||||
Quantile
|
|
||||||
Summary
|
|
||||||
Untyped
|
|
||||||
Histogram
|
|
||||||
Bucket
|
|
||||||
Metric
|
|
||||||
MetricFamily
|
|
||||||
*/
|
|
||||||
package io_prometheus_client
|
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
import math "math"
|
import math "math"
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
var _ = math.Inf
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
type MetricType int32
|
type MetricType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -70,16 +59,41 @@ func (x *MetricType) UnmarshalJSON(data []byte) error {
|
||||||
*x = MetricType(value)
|
*x = MetricType(value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (MetricType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
type LabelPair struct {
|
type LabelPair struct {
|
||||||
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
|
Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *LabelPair) Reset() { *m = LabelPair{} }
|
func (m *LabelPair) Reset() { *m = LabelPair{} }
|
||||||
func (m *LabelPair) String() string { return proto.CompactTextString(m) }
|
func (m *LabelPair) String() string { return proto.CompactTextString(m) }
|
||||||
func (*LabelPair) ProtoMessage() {}
|
func (*LabelPair) ProtoMessage() {}
|
||||||
|
func (*LabelPair) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{0}
|
||||||
|
}
|
||||||
|
func (m *LabelPair) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_LabelPair.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *LabelPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_LabelPair.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *LabelPair) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_LabelPair.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *LabelPair) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_LabelPair.Size(m)
|
||||||
|
}
|
||||||
|
func (m *LabelPair) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_LabelPair.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_LabelPair proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *LabelPair) GetName() string {
|
func (m *LabelPair) GetName() string {
|
||||||
if m != nil && m.Name != nil {
|
if m != nil && m.Name != nil {
|
||||||
|
@ -96,13 +110,35 @@ func (m *LabelPair) GetValue() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Gauge struct {
|
type Gauge struct {
|
||||||
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Gauge) Reset() { *m = Gauge{} }
|
func (m *Gauge) Reset() { *m = Gauge{} }
|
||||||
func (m *Gauge) String() string { return proto.CompactTextString(m) }
|
func (m *Gauge) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Gauge) ProtoMessage() {}
|
func (*Gauge) ProtoMessage() {}
|
||||||
|
func (*Gauge) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{1}
|
||||||
|
}
|
||||||
|
func (m *Gauge) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Gauge.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Gauge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Gauge.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Gauge) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Gauge.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Gauge) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Gauge.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Gauge) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Gauge.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Gauge proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Gauge) GetValue() float64 {
|
func (m *Gauge) GetValue() float64 {
|
||||||
if m != nil && m.Value != nil {
|
if m != nil && m.Value != nil {
|
||||||
|
@ -112,13 +148,35 @@ func (m *Gauge) GetValue() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Counter struct {
|
type Counter struct {
|
||||||
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Counter) Reset() { *m = Counter{} }
|
func (m *Counter) Reset() { *m = Counter{} }
|
||||||
func (m *Counter) String() string { return proto.CompactTextString(m) }
|
func (m *Counter) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Counter) ProtoMessage() {}
|
func (*Counter) ProtoMessage() {}
|
||||||
|
func (*Counter) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{2}
|
||||||
|
}
|
||||||
|
func (m *Counter) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Counter.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Counter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Counter.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Counter) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Counter.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Counter) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Counter.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Counter) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Counter.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Counter proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Counter) GetValue() float64 {
|
func (m *Counter) GetValue() float64 {
|
||||||
if m != nil && m.Value != nil {
|
if m != nil && m.Value != nil {
|
||||||
|
@ -128,14 +186,36 @@ func (m *Counter) GetValue() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Quantile struct {
|
type Quantile struct {
|
||||||
Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"`
|
Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"`
|
||||||
Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"`
|
Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Quantile) Reset() { *m = Quantile{} }
|
func (m *Quantile) Reset() { *m = Quantile{} }
|
||||||
func (m *Quantile) String() string { return proto.CompactTextString(m) }
|
func (m *Quantile) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Quantile) ProtoMessage() {}
|
func (*Quantile) ProtoMessage() {}
|
||||||
|
func (*Quantile) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{3}
|
||||||
|
}
|
||||||
|
func (m *Quantile) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Quantile.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Quantile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Quantile.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Quantile) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Quantile.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Quantile) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Quantile.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Quantile) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Quantile.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Quantile proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Quantile) GetQuantile() float64 {
|
func (m *Quantile) GetQuantile() float64 {
|
||||||
if m != nil && m.Quantile != nil {
|
if m != nil && m.Quantile != nil {
|
||||||
|
@ -152,15 +232,37 @@ func (m *Quantile) GetValue() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Summary struct {
|
type Summary struct {
|
||||||
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count" json:"sample_count,omitempty"`
|
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"`
|
||||||
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum" json:"sample_sum,omitempty"`
|
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
|
||||||
Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"`
|
Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Summary) Reset() { *m = Summary{} }
|
func (m *Summary) Reset() { *m = Summary{} }
|
||||||
func (m *Summary) String() string { return proto.CompactTextString(m) }
|
func (m *Summary) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Summary) ProtoMessage() {}
|
func (*Summary) ProtoMessage() {}
|
||||||
|
func (*Summary) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{4}
|
||||||
|
}
|
||||||
|
func (m *Summary) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Summary.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Summary) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Summary.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Summary) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Summary.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Summary) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Summary.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Summary) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Summary.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Summary proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Summary) GetSampleCount() uint64 {
|
func (m *Summary) GetSampleCount() uint64 {
|
||||||
if m != nil && m.SampleCount != nil {
|
if m != nil && m.SampleCount != nil {
|
||||||
|
@ -184,13 +286,35 @@ func (m *Summary) GetQuantile() []*Quantile {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Untyped struct {
|
type Untyped struct {
|
||||||
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Untyped) Reset() { *m = Untyped{} }
|
func (m *Untyped) Reset() { *m = Untyped{} }
|
||||||
func (m *Untyped) String() string { return proto.CompactTextString(m) }
|
func (m *Untyped) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Untyped) ProtoMessage() {}
|
func (*Untyped) ProtoMessage() {}
|
||||||
|
func (*Untyped) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{5}
|
||||||
|
}
|
||||||
|
func (m *Untyped) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Untyped.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Untyped) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Untyped.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Untyped) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Untyped.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Untyped) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Untyped.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Untyped) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Untyped.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Untyped proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Untyped) GetValue() float64 {
|
func (m *Untyped) GetValue() float64 {
|
||||||
if m != nil && m.Value != nil {
|
if m != nil && m.Value != nil {
|
||||||
|
@ -200,15 +324,37 @@ func (m *Untyped) GetValue() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Histogram struct {
|
type Histogram struct {
|
||||||
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count" json:"sample_count,omitempty"`
|
SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count,json=sampleCount" json:"sample_count,omitempty"`
|
||||||
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum" json:"sample_sum,omitempty"`
|
SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum,json=sampleSum" json:"sample_sum,omitempty"`
|
||||||
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"`
|
Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Histogram) Reset() { *m = Histogram{} }
|
func (m *Histogram) Reset() { *m = Histogram{} }
|
||||||
func (m *Histogram) String() string { return proto.CompactTextString(m) }
|
func (m *Histogram) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Histogram) ProtoMessage() {}
|
func (*Histogram) ProtoMessage() {}
|
||||||
|
func (*Histogram) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{6}
|
||||||
|
}
|
||||||
|
func (m *Histogram) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Histogram.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Histogram) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Histogram.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Histogram) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Histogram.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Histogram) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Histogram.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Histogram) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Histogram.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Histogram proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Histogram) GetSampleCount() uint64 {
|
func (m *Histogram) GetSampleCount() uint64 {
|
||||||
if m != nil && m.SampleCount != nil {
|
if m != nil && m.SampleCount != nil {
|
||||||
|
@ -232,14 +378,36 @@ func (m *Histogram) GetBucket() []*Bucket {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Bucket struct {
|
type Bucket struct {
|
||||||
CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count" json:"cumulative_count,omitempty"`
|
CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count,json=cumulativeCount" json:"cumulative_count,omitempty"`
|
||||||
UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound" json:"upper_bound,omitempty"`
|
UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound,json=upperBound" json:"upper_bound,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Bucket) Reset() { *m = Bucket{} }
|
func (m *Bucket) Reset() { *m = Bucket{} }
|
||||||
func (m *Bucket) String() string { return proto.CompactTextString(m) }
|
func (m *Bucket) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Bucket) ProtoMessage() {}
|
func (*Bucket) ProtoMessage() {}
|
||||||
|
func (*Bucket) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{7}
|
||||||
|
}
|
||||||
|
func (m *Bucket) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Bucket.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Bucket) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Bucket.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Bucket) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Bucket.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Bucket) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Bucket.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Bucket) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Bucket.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Bucket proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Bucket) GetCumulativeCount() uint64 {
|
func (m *Bucket) GetCumulativeCount() uint64 {
|
||||||
if m != nil && m.CumulativeCount != nil {
|
if m != nil && m.CumulativeCount != nil {
|
||||||
|
@ -256,19 +424,41 @@ func (m *Bucket) GetUpperBound() float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Metric struct {
|
type Metric struct {
|
||||||
Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"`
|
Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"`
|
||||||
Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"`
|
Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"`
|
||||||
Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"`
|
Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"`
|
||||||
Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"`
|
Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"`
|
||||||
Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"`
|
Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"`
|
||||||
Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"`
|
Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"`
|
||||||
TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms" json:"timestamp_ms,omitempty"`
|
TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms,json=timestampMs" json:"timestamp_ms,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metric) Reset() { *m = Metric{} }
|
func (m *Metric) Reset() { *m = Metric{} }
|
||||||
func (m *Metric) String() string { return proto.CompactTextString(m) }
|
func (m *Metric) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Metric) ProtoMessage() {}
|
func (*Metric) ProtoMessage() {}
|
||||||
|
func (*Metric) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{8}
|
||||||
|
}
|
||||||
|
func (m *Metric) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_Metric.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *Metric) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_Metric.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *Metric) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Metric.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *Metric) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_Metric.Size(m)
|
||||||
|
}
|
||||||
|
func (m *Metric) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Metric.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Metric proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *Metric) GetLabel() []*LabelPair {
|
func (m *Metric) GetLabel() []*LabelPair {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
|
@ -320,16 +510,38 @@ func (m *Metric) GetTimestampMs() int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetricFamily struct {
|
type MetricFamily struct {
|
||||||
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"`
|
Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"`
|
||||||
Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"`
|
Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"`
|
||||||
Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"`
|
Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MetricFamily) Reset() { *m = MetricFamily{} }
|
func (m *MetricFamily) Reset() { *m = MetricFamily{} }
|
||||||
func (m *MetricFamily) String() string { return proto.CompactTextString(m) }
|
func (m *MetricFamily) String() string { return proto.CompactTextString(m) }
|
||||||
func (*MetricFamily) ProtoMessage() {}
|
func (*MetricFamily) ProtoMessage() {}
|
||||||
|
func (*MetricFamily) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_metrics_c97c9a2b9560cb8f, []int{9}
|
||||||
|
}
|
||||||
|
func (m *MetricFamily) XXX_Unmarshal(b []byte) error {
|
||||||
|
return xxx_messageInfo_MetricFamily.Unmarshal(m, b)
|
||||||
|
}
|
||||||
|
func (m *MetricFamily) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
return xxx_messageInfo_MetricFamily.Marshal(b, m, deterministic)
|
||||||
|
}
|
||||||
|
func (dst *MetricFamily) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_MetricFamily.Merge(dst, src)
|
||||||
|
}
|
||||||
|
func (m *MetricFamily) XXX_Size() int {
|
||||||
|
return xxx_messageInfo_MetricFamily.Size(m)
|
||||||
|
}
|
||||||
|
func (m *MetricFamily) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_MetricFamily.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_MetricFamily proto.InternalMessageInfo
|
||||||
|
|
||||||
func (m *MetricFamily) GetName() string {
|
func (m *MetricFamily) GetName() string {
|
||||||
if m != nil && m.Name != nil {
|
if m != nil && m.Name != nil {
|
||||||
|
@ -360,5 +572,58 @@ func (m *MetricFamily) GetMetric() []*Metric {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
proto.RegisterType((*LabelPair)(nil), "io.prometheus.client.LabelPair")
|
||||||
|
proto.RegisterType((*Gauge)(nil), "io.prometheus.client.Gauge")
|
||||||
|
proto.RegisterType((*Counter)(nil), "io.prometheus.client.Counter")
|
||||||
|
proto.RegisterType((*Quantile)(nil), "io.prometheus.client.Quantile")
|
||||||
|
proto.RegisterType((*Summary)(nil), "io.prometheus.client.Summary")
|
||||||
|
proto.RegisterType((*Untyped)(nil), "io.prometheus.client.Untyped")
|
||||||
|
proto.RegisterType((*Histogram)(nil), "io.prometheus.client.Histogram")
|
||||||
|
proto.RegisterType((*Bucket)(nil), "io.prometheus.client.Bucket")
|
||||||
|
proto.RegisterType((*Metric)(nil), "io.prometheus.client.Metric")
|
||||||
|
proto.RegisterType((*MetricFamily)(nil), "io.prometheus.client.MetricFamily")
|
||||||
proto.RegisterEnum("io.prometheus.client.MetricType", MetricType_name, MetricType_value)
|
proto.RegisterEnum("io.prometheus.client.MetricType", MetricType_name, MetricType_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("metrics.proto", fileDescriptor_metrics_c97c9a2b9560cb8f) }
|
||||||
|
|
||||||
|
var fileDescriptor_metrics_c97c9a2b9560cb8f = []byte{
|
||||||
|
// 591 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x4f, 0x4f, 0xdb, 0x4e,
|
||||||
|
0x14, 0xfc, 0x99, 0xd8, 0x09, 0x7e, 0x86, 0x5f, 0xad, 0x15, 0x07, 0xab, 0x2d, 0x25, 0xcd, 0x89,
|
||||||
|
0xf6, 0x10, 0x54, 0x04, 0xaa, 0x44, 0xdb, 0x03, 0x50, 0x1a, 0x2a, 0xd5, 0x40, 0x37, 0xc9, 0x81,
|
||||||
|
0x5e, 0xac, 0x8d, 0x59, 0x25, 0x56, 0xbd, 0xb6, 0x6b, 0xef, 0x22, 0xe5, 0xdc, 0x43, 0xbf, 0x47,
|
||||||
|
0xbf, 0x68, 0xab, 0xfd, 0xe3, 0x18, 0x24, 0xc3, 0xa9, 0xb7, 0xb7, 0xf3, 0x66, 0xde, 0x8e, 0x77,
|
||||||
|
0xc7, 0x0b, 0x9b, 0x8c, 0xf2, 0x32, 0x89, 0xab, 0x61, 0x51, 0xe6, 0x3c, 0x47, 0x5b, 0x49, 0x2e,
|
||||||
|
0x2b, 0x46, 0xf9, 0x82, 0x8a, 0x6a, 0x18, 0xa7, 0x09, 0xcd, 0xf8, 0xe0, 0x10, 0xdc, 0x2f, 0x64,
|
||||||
|
0x46, 0xd3, 0x2b, 0x92, 0x94, 0x08, 0x81, 0x9d, 0x11, 0x46, 0x03, 0xab, 0x6f, 0xed, 0xba, 0x58,
|
||||||
|
0xd5, 0x68, 0x0b, 0x9c, 0x5b, 0x92, 0x0a, 0x1a, 0xac, 0x29, 0x50, 0x2f, 0x06, 0xdb, 0xe0, 0x8c,
|
||||||
|
0x88, 0x98, 0xdf, 0x69, 0x4b, 0x8d, 0x55, 0xb7, 0x77, 0xa0, 0x77, 0x9a, 0x8b, 0x8c, 0xd3, 0xf2,
|
||||||
|
0x01, 0xc2, 0x7b, 0x58, 0xff, 0x2a, 0x48, 0xc6, 0x93, 0x94, 0xa2, 0xa7, 0xb0, 0xfe, 0xc3, 0xd4,
|
||||||
|
0x86, 0xb4, 0x5a, 0xdf, 0xdf, 0x7d, 0xa5, 0xfe, 0x65, 0x41, 0x6f, 0x2c, 0x18, 0x23, 0xe5, 0x12,
|
||||||
|
0xbd, 0x84, 0x8d, 0x8a, 0xb0, 0x22, 0xa5, 0x51, 0x2c, 0x77, 0x54, 0x13, 0x6c, 0xec, 0x69, 0x4c,
|
||||||
|
0x99, 0x40, 0xdb, 0x00, 0x86, 0x52, 0x09, 0x66, 0x26, 0xb9, 0x1a, 0x19, 0x0b, 0x86, 0x8e, 0xee,
|
||||||
|
0xec, 0xdf, 0xe9, 0x77, 0x76, 0xbd, 0xfd, 0x17, 0xc3, 0xb6, 0xb3, 0x1a, 0xd6, 0x8e, 0x1b, 0x7f,
|
||||||
|
0xf2, 0x43, 0xa7, 0x19, 0x5f, 0x16, 0xf4, 0xe6, 0x81, 0x0f, 0xfd, 0x69, 0x81, 0x7b, 0x9e, 0x54,
|
||||||
|
0x3c, 0x9f, 0x97, 0x84, 0xfd, 0x03, 0xb3, 0x07, 0xd0, 0x9d, 0x89, 0xf8, 0x3b, 0xe5, 0xc6, 0xea,
|
||||||
|
0xf3, 0x76, 0xab, 0x27, 0x8a, 0x83, 0x0d, 0x77, 0x30, 0x81, 0xae, 0x46, 0xd0, 0x2b, 0xf0, 0x63,
|
||||||
|
0xc1, 0x44, 0x4a, 0x78, 0x72, 0x7b, 0xdf, 0xc5, 0x93, 0x06, 0xd7, 0x4e, 0x76, 0xc0, 0x13, 0x45,
|
||||||
|
0x41, 0xcb, 0x68, 0x96, 0x8b, 0xec, 0xc6, 0x58, 0x01, 0x05, 0x9d, 0x48, 0x64, 0xf0, 0x67, 0x0d,
|
||||||
|
0xba, 0xa1, 0xca, 0x18, 0x3a, 0x04, 0x27, 0x95, 0x31, 0x0a, 0x2c, 0xe5, 0x6a, 0xa7, 0xdd, 0xd5,
|
||||||
|
0x2a, 0x69, 0x58, 0xb3, 0xd1, 0x1b, 0x70, 0xe6, 0x32, 0x46, 0x6a, 0xb8, 0xb7, 0xff, 0xac, 0x5d,
|
||||||
|
0xa6, 0x92, 0x86, 0x35, 0x13, 0xbd, 0x85, 0x5e, 0xac, 0xa3, 0x15, 0x74, 0x94, 0x68, 0xbb, 0x5d,
|
||||||
|
0x64, 0xf2, 0x87, 0x6b, 0xb6, 0x14, 0x56, 0x3a, 0x33, 0x81, 0xfd, 0x98, 0xd0, 0x04, 0x0b, 0xd7,
|
||||||
|
0x6c, 0x29, 0x14, 0xfa, 0x8e, 0x03, 0xe7, 0x31, 0xa1, 0x09, 0x02, 0xae, 0xd9, 0xe8, 0x03, 0xb8,
|
||||||
|
0x8b, 0xfa, 0xea, 0x83, 0x9e, 0x92, 0x3e, 0x70, 0x30, 0xab, 0x84, 0xe0, 0x46, 0x21, 0xc3, 0xc2,
|
||||||
|
0x13, 0x46, 0x2b, 0x4e, 0x58, 0x11, 0xb1, 0x2a, 0xe8, 0xf6, 0xad, 0xdd, 0x0e, 0xf6, 0x56, 0x58,
|
||||||
|
0x58, 0x0d, 0x7e, 0x5b, 0xb0, 0xa1, 0x6f, 0xe0, 0x13, 0x61, 0x49, 0xba, 0x6c, 0xfd, 0x83, 0x11,
|
||||||
|
0xd8, 0x0b, 0x9a, 0x16, 0xe6, 0x07, 0x56, 0x35, 0x3a, 0x00, 0x5b, 0x7a, 0x54, 0x47, 0xf8, 0xff,
|
||||||
|
0x7e, 0xbf, 0xdd, 0x95, 0x9e, 0x3c, 0x59, 0x16, 0x14, 0x2b, 0xb6, 0x0c, 0x9f, 0x7e, 0x53, 0x02,
|
||||||
|
0xfb, 0xb1, 0xf0, 0x69, 0x1d, 0x36, 0xdc, 0xd7, 0x21, 0x40, 0x33, 0x09, 0x79, 0xd0, 0x3b, 0xbd,
|
||||||
|
0x9c, 0x5e, 0x4c, 0xce, 0xb0, 0xff, 0x1f, 0x72, 0xc1, 0x19, 0x1d, 0x4f, 0x47, 0x67, 0xbe, 0x25,
|
||||||
|
0xf1, 0xf1, 0x34, 0x0c, 0x8f, 0xf1, 0xb5, 0xbf, 0x26, 0x17, 0xd3, 0x8b, 0xc9, 0xf5, 0xd5, 0xd9,
|
||||||
|
0x47, 0xbf, 0x83, 0x36, 0xc1, 0x3d, 0xff, 0x3c, 0x9e, 0x5c, 0x8e, 0xf0, 0x71, 0xe8, 0xdb, 0x27,
|
||||||
|
0x18, 0x5a, 0x5f, 0xb2, 0x6f, 0x47, 0xf3, 0x84, 0x2f, 0xc4, 0x6c, 0x18, 0xe7, 0x6c, 0xaf, 0xe9,
|
||||||
|
0xee, 0xe9, 0x6e, 0xc4, 0xf2, 0x1b, 0x9a, 0xee, 0xcd, 0xf3, 0x77, 0x49, 0x1e, 0x35, 0xdd, 0x48,
|
||||||
|
0x77, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x45, 0x21, 0x7f, 0x64, 0x2b, 0x05, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
4
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
4
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
|
@ -164,9 +164,9 @@ func (sd *SampleDecoder) Decode(s *model.Vector) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractSamples builds a slice of samples from the provided metric
|
// ExtractSamples builds a slice of samples from the provided metric
|
||||||
// families. If an error occurs during sample extraction, it continues to
|
// families. If an error occurrs during sample extraction, it continues to
|
||||||
// extract from the remaining metric families. The returned error is the last
|
// extract from the remaining metric families. The returned error is the last
|
||||||
// error that has occured.
|
// error that has occurred.
|
||||||
func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
|
func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) (model.Vector, error) {
|
||||||
var (
|
var (
|
||||||
all model.Vector
|
all model.Vector
|
||||||
|
|
2
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
2
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
|
@ -26,7 +26,7 @@ const (
|
||||||
|
|
||||||
// The Content-Type values for the different wire protocols.
|
// The Content-Type values for the different wire protocols.
|
||||||
FmtUnknown Format = `<unknown>`
|
FmtUnknown Format = `<unknown>`
|
||||||
FmtText Format = `text/plain; version=` + TextVersion
|
FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
|
||||||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
||||||
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
||||||
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
||||||
|
|
385
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
385
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
|
@ -14,13 +14,45 @@
|
||||||
package expfmt
|
package expfmt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
"github.com/prometheus/common/model"
|
)
|
||||||
|
|
||||||
|
// enhancedWriter has all the enhanced write functions needed here. bytes.Buffer
|
||||||
|
// implements it.
|
||||||
|
type enhancedWriter interface {
|
||||||
|
io.Writer
|
||||||
|
WriteRune(r rune) (n int, err error)
|
||||||
|
WriteString(s string) (n int, err error)
|
||||||
|
WriteByte(c byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
initialBufSize = 512
|
||||||
|
initialNumBufSize = 24
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return bytes.NewBuffer(make([]byte, 0, initialBufSize))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
numBufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
b := make([]byte, 0, initialNumBufSize)
|
||||||
|
return &b
|
||||||
|
},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
// MetricFamilyToText converts a MetricFamily proto message into text format and
|
||||||
|
@ -32,37 +64,92 @@ import (
|
||||||
// will result in invalid text format output.
|
// will result in invalid text format output.
|
||||||
//
|
//
|
||||||
// This method fulfills the type 'prometheus.encoder'.
|
// This method fulfills the type 'prometheus.encoder'.
|
||||||
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) {
|
||||||
var written int
|
|
||||||
|
|
||||||
// Fail-fast checks.
|
// Fail-fast checks.
|
||||||
if len(in.Metric) == 0 {
|
if len(in.Metric) == 0 {
|
||||||
return written, fmt.Errorf("MetricFamily has no metrics: %s", in)
|
return 0, fmt.Errorf("MetricFamily has no metrics: %s", in)
|
||||||
}
|
}
|
||||||
name := in.GetName()
|
name := in.GetName()
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return written, fmt.Errorf("MetricFamily has no name: %s", in)
|
return 0, fmt.Errorf("MetricFamily has no name: %s", in)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try the interface upgrade. If it doesn't work, we'll use a
|
||||||
|
// bytes.Buffer from the sync.Pool and write out its content to out in a
|
||||||
|
// single go in the end.
|
||||||
|
w, ok := out.(enhancedWriter)
|
||||||
|
if !ok {
|
||||||
|
b := bufPool.Get().(*bytes.Buffer)
|
||||||
|
b.Reset()
|
||||||
|
w = b
|
||||||
|
defer func() {
|
||||||
|
bWritten, bErr := out.Write(b.Bytes())
|
||||||
|
written = bWritten
|
||||||
|
if err == nil {
|
||||||
|
err = bErr
|
||||||
|
}
|
||||||
|
bufPool.Put(b)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
|
||||||
// Comments, first HELP, then TYPE.
|
// Comments, first HELP, then TYPE.
|
||||||
if in.Help != nil {
|
if in.Help != nil {
|
||||||
n, err := fmt.Fprintf(
|
n, err = w.WriteString("# HELP ")
|
||||||
out, "# HELP %s %s\n",
|
|
||||||
name, escapeString(*in.Help, false),
|
|
||||||
)
|
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return
|
||||||
|
}
|
||||||
|
n, err = w.WriteString(name)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = w.WriteByte(' ')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n, err = writeEscapedString(w, *in.Help, false)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = w.WriteByte('\n')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
metricType := in.GetType()
|
n, err = w.WriteString("# TYPE ")
|
||||||
n, err := fmt.Fprintf(
|
|
||||||
out, "# TYPE %s %s\n",
|
|
||||||
name, strings.ToLower(metricType.String()),
|
|
||||||
)
|
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return
|
||||||
|
}
|
||||||
|
n, err = w.WriteString(name)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
metricType := in.GetType()
|
||||||
|
switch metricType {
|
||||||
|
case dto.MetricType_COUNTER:
|
||||||
|
n, err = w.WriteString(" counter\n")
|
||||||
|
case dto.MetricType_GAUGE:
|
||||||
|
n, err = w.WriteString(" gauge\n")
|
||||||
|
case dto.MetricType_SUMMARY:
|
||||||
|
n, err = w.WriteString(" summary\n")
|
||||||
|
case dto.MetricType_UNTYPED:
|
||||||
|
n, err = w.WriteString(" untyped\n")
|
||||||
|
case dto.MetricType_HISTOGRAM:
|
||||||
|
n, err = w.WriteString(" histogram\n")
|
||||||
|
default:
|
||||||
|
return written, fmt.Errorf("unknown metric type %s", metricType.String())
|
||||||
|
}
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally the samples, one line for each.
|
// Finally the samples, one line for each.
|
||||||
|
@ -75,9 +162,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name, metric, "", "",
|
w, name, "", metric, "", 0,
|
||||||
metric.Counter.GetValue(),
|
metric.Counter.GetValue(),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
case dto.MetricType_GAUGE:
|
case dto.MetricType_GAUGE:
|
||||||
if metric.Gauge == nil {
|
if metric.Gauge == nil {
|
||||||
|
@ -86,9 +172,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name, metric, "", "",
|
w, name, "", metric, "", 0,
|
||||||
metric.Gauge.GetValue(),
|
metric.Gauge.GetValue(),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
case dto.MetricType_UNTYPED:
|
case dto.MetricType_UNTYPED:
|
||||||
if metric.Untyped == nil {
|
if metric.Untyped == nil {
|
||||||
|
@ -97,9 +182,8 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name, metric, "", "",
|
w, name, "", metric, "", 0,
|
||||||
metric.Untyped.GetValue(),
|
metric.Untyped.GetValue(),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
case dto.MetricType_SUMMARY:
|
case dto.MetricType_SUMMARY:
|
||||||
if metric.Summary == nil {
|
if metric.Summary == nil {
|
||||||
|
@ -109,29 +193,26 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
}
|
}
|
||||||
for _, q := range metric.Summary.Quantile {
|
for _, q := range metric.Summary.Quantile {
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name, metric,
|
w, name, "", metric,
|
||||||
model.QuantileLabel, fmt.Sprint(q.GetQuantile()),
|
model.QuantileLabel, q.GetQuantile(),
|
||||||
q.GetValue(),
|
q.GetValue(),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name+"_sum", metric, "", "",
|
w, name, "_sum", metric, "", 0,
|
||||||
metric.Summary.GetSampleSum(),
|
metric.Summary.GetSampleSum(),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return written, err
|
|
||||||
}
|
|
||||||
written += n
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name+"_count", metric, "", "",
|
w, name, "_count", metric, "", 0,
|
||||||
float64(metric.Summary.GetSampleCount()),
|
float64(metric.Summary.GetSampleCount()),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
case dto.MetricType_HISTOGRAM:
|
case dto.MetricType_HISTOGRAM:
|
||||||
if metric.Histogram == nil {
|
if metric.Histogram == nil {
|
||||||
|
@ -140,46 +221,42 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
infSeen := false
|
infSeen := false
|
||||||
for _, q := range metric.Histogram.Bucket {
|
for _, b := range metric.Histogram.Bucket {
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name+"_bucket", metric,
|
w, name, "_bucket", metric,
|
||||||
model.BucketLabel, fmt.Sprint(q.GetUpperBound()),
|
model.BucketLabel, b.GetUpperBound(),
|
||||||
float64(q.GetCumulativeCount()),
|
float64(b.GetCumulativeCount()),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return
|
||||||
}
|
}
|
||||||
if math.IsInf(q.GetUpperBound(), +1) {
|
if math.IsInf(b.GetUpperBound(), +1) {
|
||||||
infSeen = true
|
infSeen = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !infSeen {
|
if !infSeen {
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name+"_bucket", metric,
|
w, name, "_bucket", metric,
|
||||||
model.BucketLabel, "+Inf",
|
model.BucketLabel, math.Inf(+1),
|
||||||
float64(metric.Histogram.GetSampleCount()),
|
float64(metric.Histogram.GetSampleCount()),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return written, err
|
|
||||||
}
|
|
||||||
written += n
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name+"_sum", metric, "", "",
|
w, name, "_sum", metric, "", 0,
|
||||||
metric.Histogram.GetSampleSum(),
|
metric.Histogram.GetSampleSum(),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
|
||||||
return written, err
|
|
||||||
}
|
|
||||||
written += n
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
n, err = writeSample(
|
n, err = writeSample(
|
||||||
name+"_count", metric, "", "",
|
w, name, "_count", metric, "", 0,
|
||||||
float64(metric.Histogram.GetSampleCount()),
|
float64(metric.Histogram.GetSampleCount()),
|
||||||
out,
|
|
||||||
)
|
)
|
||||||
default:
|
default:
|
||||||
return written, fmt.Errorf(
|
return written, fmt.Errorf(
|
||||||
|
@ -188,116 +265,204 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
|
||||||
}
|
}
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return written, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeSample writes a single sample in text format to out, given the metric
|
// writeSample writes a single sample in text format to w, given the metric
|
||||||
// name, the metric proto message itself, optionally an additional label name
|
// name, the metric proto message itself, optionally an additional label name
|
||||||
// and value (use empty strings if not required), and the value. The function
|
// with a float64 value (use empty string as label name if not required), and
|
||||||
// returns the number of bytes written and any error encountered.
|
// the value. The function returns the number of bytes written and any error
|
||||||
|
// encountered.
|
||||||
func writeSample(
|
func writeSample(
|
||||||
name string,
|
w enhancedWriter,
|
||||||
|
name, suffix string,
|
||||||
metric *dto.Metric,
|
metric *dto.Metric,
|
||||||
additionalLabelName, additionalLabelValue string,
|
additionalLabelName string, additionalLabelValue float64,
|
||||||
value float64,
|
value float64,
|
||||||
out io.Writer,
|
|
||||||
) (int, error) {
|
) (int, error) {
|
||||||
var written int
|
var written int
|
||||||
n, err := fmt.Fprint(out, name)
|
n, err := w.WriteString(name)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
n, err = labelPairsToText(
|
if suffix != "" {
|
||||||
metric.Label,
|
n, err = w.WriteString(suffix)
|
||||||
additionalLabelName, additionalLabelValue,
|
|
||||||
out,
|
|
||||||
)
|
|
||||||
written += n
|
|
||||||
if err != nil {
|
|
||||||
return written, err
|
|
||||||
}
|
|
||||||
n, err = fmt.Fprintf(out, " %v", value)
|
|
||||||
written += n
|
|
||||||
if err != nil {
|
|
||||||
return written, err
|
|
||||||
}
|
|
||||||
if metric.TimestampMs != nil {
|
|
||||||
n, err = fmt.Fprintf(out, " %v", *metric.TimestampMs)
|
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n, err = out.Write([]byte{'\n'})
|
n, err = writeLabelPairs(
|
||||||
|
w, metric.Label, additionalLabelName, additionalLabelValue,
|
||||||
|
)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
|
err = w.WriteByte(' ')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
n, err = writeFloat(w, value)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
if metric.TimestampMs != nil {
|
||||||
|
err = w.WriteByte(' ')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
n, err = writeInt(w, *metric.TimestampMs)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = w.WriteByte('\n')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
return written, nil
|
return written, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// labelPairsToText converts a slice of LabelPair proto messages plus the
|
// writeLabelPairs converts a slice of LabelPair proto messages plus the
|
||||||
// explicitly given additional label pair into text formatted as required by the
|
// explicitly given additional label pair into text formatted as required by the
|
||||||
// text format and writes it to 'out'. An empty slice in combination with an
|
// text format and writes it to 'w'. An empty slice in combination with an empty
|
||||||
// empty string 'additionalLabelName' results in nothing being
|
// string 'additionalLabelName' results in nothing being written. Otherwise, the
|
||||||
// written. Otherwise, the label pairs are written, escaped as required by the
|
// label pairs are written, escaped as required by the text format, and enclosed
|
||||||
// text format, and enclosed in '{...}'. The function returns the number of
|
// in '{...}'. The function returns the number of bytes written and any error
|
||||||
// bytes written and any error encountered.
|
// encountered.
|
||||||
func labelPairsToText(
|
func writeLabelPairs(
|
||||||
|
w enhancedWriter,
|
||||||
in []*dto.LabelPair,
|
in []*dto.LabelPair,
|
||||||
additionalLabelName, additionalLabelValue string,
|
additionalLabelName string, additionalLabelValue float64,
|
||||||
out io.Writer,
|
|
||||||
) (int, error) {
|
) (int, error) {
|
||||||
if len(in) == 0 && additionalLabelName == "" {
|
if len(in) == 0 && additionalLabelName == "" {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
var written int
|
var (
|
||||||
separator := '{'
|
written int
|
||||||
|
separator byte = '{'
|
||||||
|
)
|
||||||
for _, lp := range in {
|
for _, lp := range in {
|
||||||
n, err := fmt.Fprintf(
|
err := w.WriteByte(separator)
|
||||||
out, `%c%s="%s"`,
|
written++
|
||||||
separator, lp.GetName(), escapeString(lp.GetValue(), true),
|
if err != nil {
|
||||||
)
|
return written, err
|
||||||
|
}
|
||||||
|
n, err := w.WriteString(lp.GetName())
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
|
n, err = w.WriteString(`="`)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
n, err = writeEscapedString(w, lp.GetValue(), true)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
err = w.WriteByte('"')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
separator = ','
|
separator = ','
|
||||||
}
|
}
|
||||||
if additionalLabelName != "" {
|
if additionalLabelName != "" {
|
||||||
n, err := fmt.Fprintf(
|
err := w.WriteByte(separator)
|
||||||
out, `%c%s="%s"`,
|
written++
|
||||||
separator, additionalLabelName,
|
if err != nil {
|
||||||
escapeString(additionalLabelValue, true),
|
return written, err
|
||||||
)
|
}
|
||||||
|
n, err := w.WriteString(additionalLabelName)
|
||||||
written += n
|
written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
|
n, err = w.WriteString(`="`)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
n, err = writeFloat(w, additionalLabelValue)
|
||||||
|
written += n
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
err = w.WriteByte('"')
|
||||||
|
written++
|
||||||
|
if err != nil {
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n, err := out.Write([]byte{'}'})
|
err := w.WriteByte('}')
|
||||||
written += n
|
written++
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return written, err
|
return written, err
|
||||||
}
|
}
|
||||||
return written, nil
|
return written, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeEscapedString replaces '\' by '\\', new line character by '\n', and - if
|
||||||
|
// includeDoubleQuote is true - '"' by '\"'.
|
||||||
var (
|
var (
|
||||||
escape = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
escaper = strings.NewReplacer("\\", `\\`, "\n", `\n`)
|
||||||
escapeWithDoubleQuote = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
quotedEscaper = strings.NewReplacer("\\", `\\`, "\n", `\n`, "\"", `\"`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// escapeString replaces '\' by '\\', new line character by '\n', and - if
|
func writeEscapedString(w enhancedWriter, v string, includeDoubleQuote bool) (int, error) {
|
||||||
// includeDoubleQuote is true - '"' by '\"'.
|
|
||||||
func escapeString(v string, includeDoubleQuote bool) string {
|
|
||||||
if includeDoubleQuote {
|
if includeDoubleQuote {
|
||||||
return escapeWithDoubleQuote.Replace(v)
|
return quotedEscaper.WriteString(w, v)
|
||||||
|
} else {
|
||||||
|
return escaper.WriteString(w, v)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return escape.Replace(v)
|
|
||||||
|
// writeFloat is equivalent to fmt.Fprint with a float64 argument but hardcodes
|
||||||
|
// a few common cases for increased efficiency. For non-hardcoded cases, it uses
|
||||||
|
// strconv.AppendFloat to avoid allocations, similar to writeInt.
|
||||||
|
func writeFloat(w enhancedWriter, f float64) (int, error) {
|
||||||
|
switch {
|
||||||
|
case f == 1:
|
||||||
|
return 1, w.WriteByte('1')
|
||||||
|
case f == 0:
|
||||||
|
return 1, w.WriteByte('0')
|
||||||
|
case f == -1:
|
||||||
|
return w.WriteString("-1")
|
||||||
|
case math.IsNaN(f):
|
||||||
|
return w.WriteString("NaN")
|
||||||
|
case math.IsInf(f, +1):
|
||||||
|
return w.WriteString("+Inf")
|
||||||
|
case math.IsInf(f, -1):
|
||||||
|
return w.WriteString("-Inf")
|
||||||
|
default:
|
||||||
|
bp := numBufPool.Get().(*[]byte)
|
||||||
|
*bp = strconv.AppendFloat((*bp)[:0], f, 'g', -1, 64)
|
||||||
|
written, err := w.Write(*bp)
|
||||||
|
numBufPool.Put(bp)
|
||||||
|
return written, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeInt is equivalent to fmt.Fprint with an int64 argument but uses
|
||||||
|
// strconv.AppendInt with a byte slice taken from a sync.Pool to avoid
|
||||||
|
// allocations.
|
||||||
|
func writeInt(w enhancedWriter, i int64) (int, error) {
|
||||||
|
bp := numBufPool.Get().(*[]byte)
|
||||||
|
*bp = strconv.AppendInt((*bp)[:0], i, 10)
|
||||||
|
written, err := w.Write(*bp)
|
||||||
|
numBufPool.Put(bp)
|
||||||
|
return written, err
|
||||||
}
|
}
|
||||||
|
|
10
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
10
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
|
@ -315,6 +315,10 @@ func (p *TextParser) startLabelValue() stateFn {
|
||||||
if p.readTokenAsLabelValue(); p.err != nil {
|
if p.readTokenAsLabelValue(); p.err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if !model.LabelValue(p.currentToken.String()).IsValid() {
|
||||||
|
p.parseError(fmt.Sprintf("invalid label value %q", p.currentToken.String()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
p.currentLabelPair.Value = proto.String(p.currentToken.String())
|
p.currentLabelPair.Value = proto.String(p.currentToken.String())
|
||||||
// Special treatment of summaries:
|
// Special treatment of summaries:
|
||||||
// - Quantile labels are special, will result in dto.Quantile later.
|
// - Quantile labels are special, will result in dto.Quantile later.
|
||||||
|
@ -355,7 +359,7 @@ func (p *TextParser) startLabelValue() stateFn {
|
||||||
}
|
}
|
||||||
return p.readingValue
|
return p.readingValue
|
||||||
default:
|
default:
|
||||||
p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.Value))
|
p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.GetValue()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -552,8 +556,8 @@ func (p *TextParser) readTokenUntilWhitespace() {
|
||||||
// byte considered is the byte already read (now in p.currentByte). The first
|
// byte considered is the byte already read (now in p.currentByte). The first
|
||||||
// newline byte encountered is still copied into p.currentByte, but not into
|
// newline byte encountered is still copied into p.currentByte, but not into
|
||||||
// p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
|
// p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
|
||||||
// recognized: '\\' tranlates into '\', and '\n' into a line-feed character. All
|
// recognized: '\\' translates into '\', and '\n' into a line-feed character.
|
||||||
// other escape sequences are invalid and cause an error.
|
// All other escape sequences are invalid and cause an error.
|
||||||
func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
|
func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
|
||||||
p.currentToken.Reset()
|
p.currentToken.Reset()
|
||||||
escaped := false
|
escaped := false
|
||||||
|
|
6
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
6
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
|
@ -1,12 +1,12 @@
|
||||||
/*
|
/*
|
||||||
|
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
HTTP Content-Type Autonegotiation.
|
HTTP Content-Type Autonegotiation.
|
||||||
|
|
||||||
The functions in this package implement the behaviour specified in
|
The functions in this package implement the behaviour specified in
|
||||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||||
|
|
||||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
met:
|
met:
|
||||||
|
|
1
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
1
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
|
@ -21,7 +21,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
separator = []byte{0}
|
|
||||||
// MetricNameRE is a regular expression matching valid metric
|
// MetricNameRE is a regular expression matching valid metric
|
||||||
// names. Note that the IsValidMetricName function performs the same
|
// names. Note that the IsValidMetricName function performs the same
|
||||||
// check but faster than a match with this regular expression.
|
// check but faster than a match with this regular expression.
|
||||||
|
|
4
vendor/github.com/prometheus/common/model/silence.go
generated
vendored
4
vendor/github.com/prometheus/common/model/silence.go
generated
vendored
|
@ -59,8 +59,8 @@ func (m *Matcher) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Silence defines the representation of a silence definiton
|
// Silence defines the representation of a silence definition in the Prometheus
|
||||||
// in the Prometheus eco-system.
|
// eco-system.
|
||||||
type Silence struct {
|
type Silence struct {
|
||||||
ID uint64 `json:"id,omitempty"`
|
ID uint64 `json:"id,omitempty"`
|
||||||
|
|
||||||
|
|
27
vendor/github.com/prometheus/common/model/time.go
generated
vendored
27
vendor/github.com/prometheus/common/model/time.go
generated
vendored
|
@ -43,7 +43,7 @@ const (
|
||||||
// (1970-01-01 00:00 UTC) excluding leap seconds.
|
// (1970-01-01 00:00 UTC) excluding leap seconds.
|
||||||
type Time int64
|
type Time int64
|
||||||
|
|
||||||
// Interval describes and interval between two timestamps.
|
// Interval describes an interval between two timestamps.
|
||||||
type Interval struct {
|
type Interval struct {
|
||||||
Start, End Time
|
Start, End Time
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,13 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*t = Time(v + va)
|
// If the value was something like -0.1 the negative is lost in the
|
||||||
|
// parsing because of the leading zero, this ensures that we capture it.
|
||||||
|
if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 {
|
||||||
|
*t = Time(v+va) * -1
|
||||||
|
} else {
|
||||||
|
*t = Time(v + va)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid time %q", string(b))
|
return fmt.Errorf("invalid time %q", string(b))
|
||||||
|
@ -163,9 +169,21 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
||||||
// This type should not propagate beyond the scope of input/output processing.
|
// This type should not propagate beyond the scope of input/output processing.
|
||||||
type Duration time.Duration
|
type Duration time.Duration
|
||||||
|
|
||||||
|
// Set implements pflag/flag.Value
|
||||||
|
func (d *Duration) Set(s string) error {
|
||||||
|
var err error
|
||||||
|
*d, err = ParseDuration(s)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements pflag.Value
|
||||||
|
func (d *Duration) Type() string {
|
||||||
|
return "duration"
|
||||||
|
}
|
||||||
|
|
||||||
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
|
var durationRE = regexp.MustCompile("^([0-9]+)(y|w|d|h|m|s|ms)$")
|
||||||
|
|
||||||
// StringToDuration parses a string into a time.Duration, assuming that a year
|
// ParseDuration parses a string into a time.Duration, assuming that a year
|
||||||
// always has 365d, a week always has 7d, and a day always has 24h.
|
// always has 365d, a week always has 7d, and a day always has 24h.
|
||||||
func ParseDuration(durationStr string) (Duration, error) {
|
func ParseDuration(durationStr string) (Duration, error) {
|
||||||
matches := durationRE.FindStringSubmatch(durationStr)
|
matches := durationRE.FindStringSubmatch(durationStr)
|
||||||
|
@ -202,6 +220,9 @@ func (d Duration) String() string {
|
||||||
ms = int64(time.Duration(d) / time.Millisecond)
|
ms = int64(time.Duration(d) / time.Millisecond)
|
||||||
unit = "ms"
|
unit = "ms"
|
||||||
)
|
)
|
||||||
|
if ms == 0 {
|
||||||
|
return "0s"
|
||||||
|
}
|
||||||
factors := map[string]int64{
|
factors := map[string]int64{
|
||||||
"y": 1000 * 60 * 60 * 24 * 365,
|
"y": 1000 * 60 * 60 * 24 * 365,
|
||||||
"w": 1000 * 60 * 60 * 24 * 7,
|
"w": 1000 * 60 * 60 * 24 * 7,
|
||||||
|
|
4
vendor/github.com/prometheus/common/model/value.go
generated
vendored
4
vendor/github.com/prometheus/common/model/value.go
generated
vendored
|
@ -100,7 +100,7 @@ func (s *SamplePair) UnmarshalJSON(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns true if this SamplePair and o have equal Values and equal
|
// Equal returns true if this SamplePair and o have equal Values and equal
|
||||||
// Timestamps. The sematics of Value equality is defined by SampleValue.Equal.
|
// Timestamps. The semantics of Value equality is defined by SampleValue.Equal.
|
||||||
func (s *SamplePair) Equal(o *SamplePair) bool {
|
func (s *SamplePair) Equal(o *SamplePair) bool {
|
||||||
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
|
return s == o || (s.Value.Equal(o.Value) && s.Timestamp.Equal(o.Timestamp))
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ type Sample struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal compares first the metrics, then the timestamp, then the value. The
|
// Equal compares first the metrics, then the timestamp, then the value. The
|
||||||
// sematics of value equality is defined by SampleValue.Equal.
|
// semantics of value equality is defined by SampleValue.Equal.
|
||||||
func (s *Sample) Equal(o *Sample) bool {
|
func (s *Sample) Equal(o *Sample) bool {
|
||||||
if s == o {
|
if s == o {
|
||||||
return true
|
return true
|
||||||
|
|
3
vendor/github.com/prometheus/procfs/MAINTAINERS.md
generated
vendored
3
vendor/github.com/prometheus/procfs/MAINTAINERS.md
generated
vendored
|
@ -1 +1,2 @@
|
||||||
* Tobias Schmidt <tobidt@gmail.com>
|
* Johannes 'fish' Ziemke <github@freigeist.org> @discordianfish
|
||||||
|
* Paul Gier <pgier@redhat.com> @pgier
|
||||||
|
|
35
vendor/github.com/prometheus/procfs/Makefile
generated
vendored
35
vendor/github.com/prometheus/procfs/Makefile
generated
vendored
|
@ -1,6 +1,29 @@
|
||||||
ci:
|
# Copyright 2018 The Prometheus Authors
|
||||||
! gofmt -l *.go | read nothing
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
go vet
|
# you may not use this file except in compliance with the License.
|
||||||
go test -v ./...
|
# You may obtain a copy of the License at
|
||||||
go get github.com/golang/lint/golint
|
#
|
||||||
golint *.go
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
include Makefile.common
|
||||||
|
|
||||||
|
%/.unpacked: %.ttar
|
||||||
|
@echo ">> extracting fixtures"
|
||||||
|
./ttar -C $(dir $*) -x -f $*.ttar
|
||||||
|
touch $@
|
||||||
|
|
||||||
|
update_fixtures:
|
||||||
|
rm -vf fixtures/.unpacked
|
||||||
|
./ttar -c -f fixtures.ttar fixtures/
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: fixtures/.unpacked common-test
|
||||||
|
|
272
vendor/github.com/prometheus/procfs/Makefile.common
generated
vendored
Normal file
272
vendor/github.com/prometheus/procfs/Makefile.common
generated
vendored
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
# Copyright 2018 The Prometheus Authors
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
# A common Makefile that includes rules to be reused in different prometheus projects.
|
||||||
|
# !!! Open PRs only against the prometheus/prometheus/Makefile.common repository!
|
||||||
|
|
||||||
|
# Example usage :
|
||||||
|
# Create the main Makefile in the root project directory.
|
||||||
|
# include Makefile.common
|
||||||
|
# customTarget:
|
||||||
|
# @echo ">> Running customTarget"
|
||||||
|
#
|
||||||
|
|
||||||
|
# Ensure GOBIN is not set during build so that promu is installed to the correct path
|
||||||
|
unexport GOBIN
|
||||||
|
|
||||||
|
GO ?= go
|
||||||
|
GOFMT ?= $(GO)fmt
|
||||||
|
FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
|
||||||
|
GOOPTS ?=
|
||||||
|
GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
|
||||||
|
GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
|
||||||
|
|
||||||
|
GO_VERSION ?= $(shell $(GO) version)
|
||||||
|
GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
|
||||||
|
PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.')
|
||||||
|
|
||||||
|
GOVENDOR :=
|
||||||
|
GO111MODULE :=
|
||||||
|
ifeq (, $(PRE_GO_111))
|
||||||
|
ifneq (,$(wildcard go.mod))
|
||||||
|
# Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI).
|
||||||
|
GO111MODULE := on
|
||||||
|
|
||||||
|
ifneq (,$(wildcard vendor))
|
||||||
|
# Always use the local vendor/ directory to satisfy the dependencies.
|
||||||
|
GOOPTS := $(GOOPTS) -mod=vendor
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
ifneq (,$(wildcard go.mod))
|
||||||
|
ifneq (,$(wildcard vendor))
|
||||||
|
$(warning This repository requires Go >= 1.11 because of Go modules)
|
||||||
|
$(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)')
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
# This repository isn't using Go modules (yet).
|
||||||
|
GOVENDOR := $(FIRST_GOPATH)/bin/govendor
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
PROMU := $(FIRST_GOPATH)/bin/promu
|
||||||
|
pkgs = ./...
|
||||||
|
|
||||||
|
ifeq (arm, $(GOHOSTARCH))
|
||||||
|
GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM)
|
||||||
|
GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM)
|
||||||
|
else
|
||||||
|
GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)
|
||||||
|
endif
|
||||||
|
|
||||||
|
PROMU_VERSION ?= 0.4.0
|
||||||
|
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
|
||||||
|
|
||||||
|
GOLANGCI_LINT :=
|
||||||
|
GOLANGCI_LINT_OPTS ?=
|
||||||
|
GOLANGCI_LINT_VERSION ?= v1.16.0
|
||||||
|
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
|
||||||
|
# windows isn't included here because of the path separator being different.
|
||||||
|
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
|
||||||
|
ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386))
|
||||||
|
GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
PREFIX ?= $(shell pwd)
|
||||||
|
BIN_DIR ?= $(shell pwd)
|
||||||
|
DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
|
||||||
|
DOCKER_REPO ?= prom
|
||||||
|
|
||||||
|
DOCKER_ARCHS ?= amd64
|
||||||
|
|
||||||
|
BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS))
|
||||||
|
PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS))
|
||||||
|
TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS))
|
||||||
|
|
||||||
|
ifeq ($(GOHOSTARCH),amd64)
|
||||||
|
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows))
|
||||||
|
# Only supported on amd64
|
||||||
|
test-flags := -race
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# This rule is used to forward a target like "build" to "common-build". This
|
||||||
|
# allows a new "build" target to be defined in a Makefile which includes this
|
||||||
|
# one and override "common-build" without override warnings.
|
||||||
|
%: common-% ;
|
||||||
|
|
||||||
|
.PHONY: common-all
|
||||||
|
common-all: precheck style check_license lint unused build test
|
||||||
|
|
||||||
|
.PHONY: common-style
|
||||||
|
common-style:
|
||||||
|
@echo ">> checking code style"
|
||||||
|
@fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \
|
||||||
|
if [ -n "$${fmtRes}" ]; then \
|
||||||
|
echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \
|
||||||
|
echo "Please ensure you are using $$($(GO) version) for formatting code."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: common-check_license
|
||||||
|
common-check_license:
|
||||||
|
@echo ">> checking license header"
|
||||||
|
@licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \
|
||||||
|
awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \
|
||||||
|
done); \
|
||||||
|
if [ -n "$${licRes}" ]; then \
|
||||||
|
echo "license header checking failed:"; echo "$${licRes}"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
.PHONY: common-deps
|
||||||
|
common-deps:
|
||||||
|
@echo ">> getting dependencies"
|
||||||
|
ifdef GO111MODULE
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) mod download
|
||||||
|
else
|
||||||
|
$(GO) get $(GOOPTS) -t ./...
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: common-test-short
|
||||||
|
common-test-short:
|
||||||
|
@echo ">> running short tests"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) test -short $(GOOPTS) $(pkgs)
|
||||||
|
|
||||||
|
.PHONY: common-test
|
||||||
|
common-test:
|
||||||
|
@echo ">> running all tests"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) test $(test-flags) $(GOOPTS) $(pkgs)
|
||||||
|
|
||||||
|
.PHONY: common-format
|
||||||
|
common-format:
|
||||||
|
@echo ">> formatting code"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) fmt $(pkgs)
|
||||||
|
|
||||||
|
.PHONY: common-vet
|
||||||
|
common-vet:
|
||||||
|
@echo ">> vetting code"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs)
|
||||||
|
|
||||||
|
.PHONY: common-lint
|
||||||
|
common-lint: $(GOLANGCI_LINT)
|
||||||
|
ifdef GOLANGCI_LINT
|
||||||
|
@echo ">> running golangci-lint"
|
||||||
|
ifdef GO111MODULE
|
||||||
|
# 'go list' needs to be executed before staticcheck to prepopulate the modules cache.
|
||||||
|
# Otherwise staticcheck might fail randomly for some reason not yet explained.
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs)
|
||||||
|
else
|
||||||
|
$(GOLANGCI_LINT) run $(pkgs)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# For backward-compatibility.
|
||||||
|
.PHONY: common-staticcheck
|
||||||
|
common-staticcheck: lint
|
||||||
|
|
||||||
|
.PHONY: common-unused
|
||||||
|
common-unused: $(GOVENDOR)
|
||||||
|
ifdef GOVENDOR
|
||||||
|
@echo ">> running check for unused packages"
|
||||||
|
@$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages'
|
||||||
|
else
|
||||||
|
ifdef GO111MODULE
|
||||||
|
@echo ">> running check for unused/missing packages in go.mod"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) mod tidy
|
||||||
|
ifeq (,$(wildcard vendor))
|
||||||
|
@git diff --exit-code -- go.sum go.mod
|
||||||
|
else
|
||||||
|
@echo ">> running check for unused packages in vendor/"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(GO) mod vendor
|
||||||
|
@git diff --exit-code -- go.sum go.mod vendor/
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: common-build
|
||||||
|
common-build: promu
|
||||||
|
@echo ">> building binaries"
|
||||||
|
GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX)
|
||||||
|
|
||||||
|
.PHONY: common-tarball
|
||||||
|
common-tarball: promu
|
||||||
|
@echo ">> building release tarball"
|
||||||
|
$(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR)
|
||||||
|
|
||||||
|
.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
|
||||||
|
common-docker: $(BUILD_DOCKER_ARCHS)
|
||||||
|
$(BUILD_DOCKER_ARCHS): common-docker-%:
|
||||||
|
docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \
|
||||||
|
--build-arg ARCH="$*" \
|
||||||
|
--build-arg OS="linux" \
|
||||||
|
.
|
||||||
|
|
||||||
|
.PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS)
|
||||||
|
common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
|
||||||
|
$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
|
||||||
|
docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)"
|
||||||
|
|
||||||
|
.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
|
||||||
|
common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
|
||||||
|
$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
|
||||||
|
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
|
||||||
|
|
||||||
|
.PHONY: common-docker-manifest
|
||||||
|
common-docker-manifest:
|
||||||
|
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG))
|
||||||
|
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"
|
||||||
|
|
||||||
|
.PHONY: promu
|
||||||
|
promu: $(PROMU)
|
||||||
|
|
||||||
|
$(PROMU):
|
||||||
|
$(eval PROMU_TMP := $(shell mktemp -d))
|
||||||
|
curl -s -L $(PROMU_URL) | tar -xvzf - -C $(PROMU_TMP)
|
||||||
|
mkdir -p $(FIRST_GOPATH)/bin
|
||||||
|
cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
|
||||||
|
rm -r $(PROMU_TMP)
|
||||||
|
|
||||||
|
.PHONY: proto
|
||||||
|
proto:
|
||||||
|
@echo ">> generating code from proto files"
|
||||||
|
@./scripts/genproto.sh
|
||||||
|
|
||||||
|
ifdef GOLANGCI_LINT
|
||||||
|
$(GOLANGCI_LINT):
|
||||||
|
mkdir -p $(FIRST_GOPATH)/bin
|
||||||
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef GOVENDOR
|
||||||
|
.PHONY: $(GOVENDOR)
|
||||||
|
$(GOVENDOR):
|
||||||
|
GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: precheck
|
||||||
|
precheck::
|
||||||
|
|
||||||
|
define PRECHECK_COMMAND_template =
|
||||||
|
precheck:: $(1)_precheck
|
||||||
|
|
||||||
|
PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1)))
|
||||||
|
.PHONY: $(1)_precheck
|
||||||
|
$(1)_precheck:
|
||||||
|
@if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \
|
||||||
|
echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
endef
|
37
vendor/github.com/prometheus/procfs/README.md
generated
vendored
37
vendor/github.com/prometheus/procfs/README.md
generated
vendored
|
@ -1,7 +1,7 @@
|
||||||
# procfs
|
# procfs
|
||||||
|
|
||||||
This procfs package provides functions to retrieve system, kernel and process
|
This procfs package provides functions to retrieve system, kernel and process
|
||||||
metrics from the pseudo-filesystem proc.
|
metrics from the pseudo-filesystems /proc and /sys.
|
||||||
|
|
||||||
*WARNING*: This package is a work in progress. Its API may still break in
|
*WARNING*: This package is a work in progress. Its API may still break in
|
||||||
backwards-incompatible ways without warnings. Use it at your own risk.
|
backwards-incompatible ways without warnings. Use it at your own risk.
|
||||||
|
@ -9,3 +9,38 @@ backwards-incompatible ways without warnings. Use it at your own risk.
|
||||||
[](https://godoc.org/github.com/prometheus/procfs)
|
[](https://godoc.org/github.com/prometheus/procfs)
|
||||||
[](https://travis-ci.org/prometheus/procfs)
|
[](https://travis-ci.org/prometheus/procfs)
|
||||||
[](https://goreportcard.com/report/github.com/prometheus/procfs)
|
[](https://goreportcard.com/report/github.com/prometheus/procfs)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The procfs library is organized by packages based on whether the gathered data is coming from
|
||||||
|
/proc, /sys, or both. Each package contains an `FS` type which represents the path to either /proc, /sys, or both. For example, current cpu statistics are gathered from
|
||||||
|
`/proc/stat` and are available via the root procfs package. First, the proc filesystem mount
|
||||||
|
point is initialized, and then the stat information is read.
|
||||||
|
|
||||||
|
```go
|
||||||
|
fs, err := procfs.NewFS("/proc")
|
||||||
|
stats, err := fs.NewStat()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building and Testing
|
||||||
|
|
||||||
|
The procfs library is normally built as part of another application. However, when making
|
||||||
|
changes to the library, the `make test` command can be used to run the API test suite.
|
||||||
|
|
||||||
|
### Updating Test Fixtures
|
||||||
|
|
||||||
|
The procfs library includes a set of test fixtures which include many example files from
|
||||||
|
the `/proc` and `/sys` filesystems. These fixtures are included as a ttar (text tar) file
|
||||||
|
which is extracted automatically during testing. To add/update the test fixtures, first
|
||||||
|
ensure the `fixtures` directory is up to date by removing the existing directory and then
|
||||||
|
extracting the ttar file using `make fixtures/.unpacked` or just `make test`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf fixtures
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, make the required changes to the extracted files in the `fixtures` directory. When
|
||||||
|
the changes are complete, run `make update_fixtures` to create a new `fixtures.ttar` file
|
||||||
|
based on the updated `fixtures` directory. And finally, verify the changes using
|
||||||
|
`git diff fixtures.ttar`.
|
||||||
|
|
4
vendor/github.com/prometheus/procfs/buddyinfo.go
generated
vendored
4
vendor/github.com/prometheus/procfs/buddyinfo.go
generated
vendored
|
@ -43,7 +43,7 @@ func NewBuddyInfo() ([]BuddyInfo, error) {
|
||||||
|
|
||||||
// NewBuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem.
|
// NewBuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem.
|
||||||
func (fs FS) NewBuddyInfo() ([]BuddyInfo, error) {
|
func (fs FS) NewBuddyInfo() ([]BuddyInfo, error) {
|
||||||
file, err := os.Open(fs.Path("buddyinfo"))
|
file, err := os.Open(fs.proc.Path("buddyinfo"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) {
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
var err error
|
var err error
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
parts := strings.Fields(string(line))
|
parts := strings.Fields(line)
|
||||||
|
|
||||||
if len(parts) < 4 {
|
if len(parts) < 4 {
|
||||||
return nil, fmt.Errorf("invalid number of fields when parsing buddyinfo")
|
return nil, fmt.Errorf("invalid number of fields when parsing buddyinfo")
|
||||||
|
|
1808
vendor/github.com/prometheus/procfs/fixtures.ttar
generated
vendored
Normal file
1808
vendor/github.com/prometheus/procfs/fixtures.ttar
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
57
vendor/github.com/prometheus/procfs/fs.go
generated
vendored
57
vendor/github.com/prometheus/procfs/fs.go
generated
vendored
|
@ -1,46 +1,37 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/prometheus/procfs/internal/fs"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/prometheus/procfs/xfs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// FS represents the pseudo-filesystem proc, which provides an interface to
|
// FS represents the pseudo-filesystem sys, which provides an interface to
|
||||||
// kernel data structures.
|
// kernel data structures.
|
||||||
type FS string
|
type FS struct {
|
||||||
|
proc fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultMountPoint is the common mount point of the proc filesystem.
|
// DefaultMountPoint is the common mount point of the proc filesystem.
|
||||||
const DefaultMountPoint = "/proc"
|
const DefaultMountPoint = fs.DefaultProcMountPoint
|
||||||
|
|
||||||
// NewFS returns a new FS mounted under the given mountPoint. It will error
|
// NewFS returns a new proc FS mounted under the given proc mountPoint. It will error
|
||||||
// if the mount point can't be read.
|
// if the mount point directory can't be read or is a file.
|
||||||
func NewFS(mountPoint string) (FS, error) {
|
func NewFS(mountPoint string) (FS, error) {
|
||||||
info, err := os.Stat(mountPoint)
|
fs, err := fs.NewFS(mountPoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
|
return FS{}, err
|
||||||
}
|
}
|
||||||
if !info.IsDir() {
|
return FS{fs}, nil
|
||||||
return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
return FS(mountPoint), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path returns the path of the given subsystem relative to the procfs root.
|
|
||||||
func (fs FS) Path(p ...string) string {
|
|
||||||
return path.Join(append([]string{string(fs)}, p...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// XFSStats retrieves XFS filesystem runtime statistics.
|
|
||||||
func (fs FS) XFSStats() (*xfs.Stats, error) {
|
|
||||||
f, err := os.Open(fs.Path("fs/xfs/stat"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
return xfs.ParseStats(f)
|
|
||||||
}
|
}
|
||||||
|
|
3
vendor/github.com/prometheus/procfs/go.mod
generated
vendored
Normal file
3
vendor/github.com/prometheus/procfs/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/prometheus/procfs
|
||||||
|
|
||||||
|
require golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
2
vendor/github.com/prometheus/procfs/go.sum
generated
vendored
Normal file
2
vendor/github.com/prometheus/procfs/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
52
vendor/github.com/prometheus/procfs/internal/fs/fs.go
generated
vendored
Normal file
52
vendor/github.com/prometheus/procfs/internal/fs/fs.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2019 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultProcMountPoint is the common mount point of the proc filesystem.
|
||||||
|
DefaultProcMountPoint = "/proc"
|
||||||
|
|
||||||
|
// DefaultSysMountPoint is the common mount point of the sys filesystem.
|
||||||
|
DefaultSysMountPoint = "/sys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FS represents a pseudo-filesystem, normally /proc or /sys, which provides an
|
||||||
|
// interface to kernel data structures.
|
||||||
|
type FS string
|
||||||
|
|
||||||
|
// NewFS returns a new FS mounted under the given mountPoint. It will error
|
||||||
|
// if the mount point can't be read.
|
||||||
|
func NewFS(mountPoint string) (FS, error) {
|
||||||
|
info, err := os.Stat(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return FS(mountPoint), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path appends the given path elements to the filesystem path, adding separators
|
||||||
|
// as necessary.
|
||||||
|
func (fs FS) Path(p ...string) string {
|
||||||
|
return filepath.Join(append([]string{string(fs)}, p...)...)
|
||||||
|
}
|
73
vendor/github.com/prometheus/procfs/ipvs.go
generated
vendored
73
vendor/github.com/prometheus/procfs/ipvs.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -31,14 +44,16 @@ type IPVSStats struct {
|
||||||
type IPVSBackendStatus struct {
|
type IPVSBackendStatus struct {
|
||||||
// The local (virtual) IP address.
|
// The local (virtual) IP address.
|
||||||
LocalAddress net.IP
|
LocalAddress net.IP
|
||||||
// The local (virtual) port.
|
|
||||||
LocalPort uint16
|
|
||||||
// The transport protocol (TCP, UDP).
|
|
||||||
Proto string
|
|
||||||
// The remote (real) IP address.
|
// The remote (real) IP address.
|
||||||
RemoteAddress net.IP
|
RemoteAddress net.IP
|
||||||
|
// The local (virtual) port.
|
||||||
|
LocalPort uint16
|
||||||
// The remote (real) port.
|
// The remote (real) port.
|
||||||
RemotePort uint16
|
RemotePort uint16
|
||||||
|
// The local firewall mark
|
||||||
|
LocalMark string
|
||||||
|
// The transport protocol (TCP, UDP).
|
||||||
|
Proto string
|
||||||
// The current number of active connections for this virtual/real address pair.
|
// The current number of active connections for this virtual/real address pair.
|
||||||
ActiveConn uint64
|
ActiveConn uint64
|
||||||
// The current number of inactive connections for this virtual/real address pair.
|
// The current number of inactive connections for this virtual/real address pair.
|
||||||
|
@ -59,7 +74,7 @@ func NewIPVSStats() (IPVSStats, error) {
|
||||||
|
|
||||||
// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
|
// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
|
||||||
func (fs FS) NewIPVSStats() (IPVSStats, error) {
|
func (fs FS) NewIPVSStats() (IPVSStats, error) {
|
||||||
file, err := os.Open(fs.Path("net/ip_vs_stats"))
|
file, err := os.Open(fs.proc.Path("net/ip_vs_stats"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return IPVSStats{}, err
|
return IPVSStats{}, err
|
||||||
}
|
}
|
||||||
|
@ -128,7 +143,7 @@ func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||||
|
|
||||||
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
|
// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
|
||||||
func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
|
||||||
file, err := os.Open(fs.Path("net/ip_vs"))
|
file, err := os.Open(fs.proc.Path("net/ip_vs"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -142,13 +157,14 @@ func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
||||||
status []IPVSBackendStatus
|
status []IPVSBackendStatus
|
||||||
scanner = bufio.NewScanner(file)
|
scanner = bufio.NewScanner(file)
|
||||||
proto string
|
proto string
|
||||||
|
localMark string
|
||||||
localAddress net.IP
|
localAddress net.IP
|
||||||
localPort uint16
|
localPort uint16
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
fields := strings.Fields(string(scanner.Text()))
|
fields := strings.Fields(scanner.Text())
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -160,10 +176,19 @@ func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
proto = fields[0]
|
proto = fields[0]
|
||||||
|
localMark = ""
|
||||||
localAddress, localPort, err = parseIPPort(fields[1])
|
localAddress, localPort, err = parseIPPort(fields[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
case fields[0] == "FWM":
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
proto = fields[0]
|
||||||
|
localMark = fields[1]
|
||||||
|
localAddress = nil
|
||||||
|
localPort = 0
|
||||||
case fields[0] == "->":
|
case fields[0] == "->":
|
||||||
if len(fields) < 6 {
|
if len(fields) < 6 {
|
||||||
continue
|
continue
|
||||||
|
@ -187,6 +212,7 @@ func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
||||||
status = append(status, IPVSBackendStatus{
|
status = append(status, IPVSBackendStatus{
|
||||||
LocalAddress: localAddress,
|
LocalAddress: localAddress,
|
||||||
LocalPort: localPort,
|
LocalPort: localPort,
|
||||||
|
LocalMark: localMark,
|
||||||
RemoteAddress: remoteAddress,
|
RemoteAddress: remoteAddress,
|
||||||
RemotePort: remotePort,
|
RemotePort: remotePort,
|
||||||
Proto: proto,
|
Proto: proto,
|
||||||
|
@ -200,22 +226,31 @@ func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIPPort(s string) (net.IP, uint16, error) {
|
func parseIPPort(s string) (net.IP, uint16, error) {
|
||||||
tmp := strings.SplitN(s, ":", 2)
|
var (
|
||||||
|
ip net.IP
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
if len(tmp) != 2 {
|
switch len(s) {
|
||||||
return nil, 0, fmt.Errorf("invalid IP:Port: %s", s)
|
case 13:
|
||||||
|
ip, err = hex.DecodeString(s[0:8])
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
case 46:
|
||||||
|
ip = net.ParseIP(s[1:40])
|
||||||
|
if ip == nil {
|
||||||
|
return nil, 0, fmt.Errorf("invalid IPv6 address: %s", s[1:40])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, 0, fmt.Errorf("unexpected IP:Port: %s", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(tmp[0]) != 8 && len(tmp[0]) != 32 {
|
portString := s[len(s)-4:]
|
||||||
return nil, 0, fmt.Errorf("invalid IP: %s", tmp[0])
|
if len(portString) != 4 {
|
||||||
|
return nil, 0, fmt.Errorf("unexpected port string format: %s", portString)
|
||||||
}
|
}
|
||||||
|
port, err := strconv.ParseUint(portString, 16, 16)
|
||||||
ip, err := hex.DecodeString(tmp[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
port, err := strconv.ParseUint(tmp[1], 16, 16)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
15
vendor/github.com/prometheus/procfs/mdstat.go
generated
vendored
15
vendor/github.com/prometheus/procfs/mdstat.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -31,7 +44,7 @@ type MDStat struct {
|
||||||
|
|
||||||
// ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
|
// ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
|
||||||
func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
|
||||||
mdStatusFilePath := fs.Path("mdstat")
|
mdStatusFilePath := fs.proc.Path("mdstat")
|
||||||
content, err := ioutil.ReadFile(mdStatusFilePath)
|
content, err := ioutil.ReadFile(mdStatusFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
|
||||||
|
|
107
vendor/github.com/prometheus/procfs/mountstats.go
generated
vendored
107
vendor/github.com/prometheus/procfs/mountstats.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
// While implementing parsing of /proc/[pid]/mountstats, this blog was used
|
// While implementing parsing of /proc/[pid]/mountstats, this blog was used
|
||||||
|
@ -26,8 +39,11 @@ const (
|
||||||
statVersion10 = "1.0"
|
statVersion10 = "1.0"
|
||||||
statVersion11 = "1.1"
|
statVersion11 = "1.1"
|
||||||
|
|
||||||
fieldTransport10Len = 10
|
fieldTransport10TCPLen = 10
|
||||||
fieldTransport11Len = 13
|
fieldTransport10UDPLen = 7
|
||||||
|
|
||||||
|
fieldTransport11TCPLen = 13
|
||||||
|
fieldTransport11UDPLen = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Mount is a device mount parsed from /proc/[pid]/mountstats.
|
// A Mount is a device mount parsed from /proc/[pid]/mountstats.
|
||||||
|
@ -53,6 +69,8 @@ type MountStats interface {
|
||||||
type MountStatsNFS struct {
|
type MountStatsNFS struct {
|
||||||
// The version of statistics provided.
|
// The version of statistics provided.
|
||||||
StatVersion string
|
StatVersion string
|
||||||
|
// The mount options of the NFS mount.
|
||||||
|
Opts map[string]string
|
||||||
// The age of the NFS mount.
|
// The age of the NFS mount.
|
||||||
Age time.Duration
|
Age time.Duration
|
||||||
// Statistics related to byte counters for various operations.
|
// Statistics related to byte counters for various operations.
|
||||||
|
@ -163,16 +181,18 @@ type NFSOperationStats struct {
|
||||||
// Number of bytes received for this operation, including RPC headers and payload.
|
// Number of bytes received for this operation, including RPC headers and payload.
|
||||||
BytesReceived uint64
|
BytesReceived uint64
|
||||||
// Duration all requests spent queued for transmission before they were sent.
|
// Duration all requests spent queued for transmission before they were sent.
|
||||||
CumulativeQueueTime time.Duration
|
CumulativeQueueMilliseconds uint64
|
||||||
// Duration it took to get a reply back after the request was transmitted.
|
// Duration it took to get a reply back after the request was transmitted.
|
||||||
CumulativeTotalResponseTime time.Duration
|
CumulativeTotalResponseMilliseconds uint64
|
||||||
// Duration from when a request was enqueued to when it was completely handled.
|
// Duration from when a request was enqueued to when it was completely handled.
|
||||||
CumulativeTotalRequestTime time.Duration
|
CumulativeTotalRequestMilliseconds uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// A NFSTransportStats contains statistics for the NFS mount RPC requests and
|
// A NFSTransportStats contains statistics for the NFS mount RPC requests and
|
||||||
// responses.
|
// responses.
|
||||||
type NFSTransportStats struct {
|
type NFSTransportStats struct {
|
||||||
|
// The transport protocol used for the NFS mount.
|
||||||
|
Protocol string
|
||||||
// The local port used for the NFS mount.
|
// The local port used for the NFS mount.
|
||||||
Port uint64
|
Port uint64
|
||||||
// Number of times the client has had to establish a connection from scratch
|
// Number of times the client has had to establish a connection from scratch
|
||||||
|
@ -184,7 +204,7 @@ type NFSTransportStats struct {
|
||||||
// spent waiting for connections to the server to be established.
|
// spent waiting for connections to the server to be established.
|
||||||
ConnectIdleTime uint64
|
ConnectIdleTime uint64
|
||||||
// Duration since the NFS mount last saw any RPC traffic.
|
// Duration since the NFS mount last saw any RPC traffic.
|
||||||
IdleTime time.Duration
|
IdleTimeSeconds uint64
|
||||||
// Number of RPC requests for this mount sent to the NFS server.
|
// Number of RPC requests for this mount sent to the NFS server.
|
||||||
Sends uint64
|
Sends uint64
|
||||||
// Number of RPC responses for this mount received from the NFS server.
|
// Number of RPC responses for this mount received from the NFS server.
|
||||||
|
@ -299,6 +319,7 @@ func parseMount(ss []string) (*Mount, error) {
|
||||||
func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, error) {
|
func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, error) {
|
||||||
// Field indicators for parsing specific types of data
|
// Field indicators for parsing specific types of data
|
||||||
const (
|
const (
|
||||||
|
fieldOpts = "opts:"
|
||||||
fieldAge = "age:"
|
fieldAge = "age:"
|
||||||
fieldBytes = "bytes:"
|
fieldBytes = "bytes:"
|
||||||
fieldEvents = "events:"
|
fieldEvents = "events:"
|
||||||
|
@ -320,6 +341,18 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ss[0] {
|
switch ss[0] {
|
||||||
|
case fieldOpts:
|
||||||
|
if stats.Opts == nil {
|
||||||
|
stats.Opts = map[string]string{}
|
||||||
|
}
|
||||||
|
for _, opt := range strings.Split(ss[1], ",") {
|
||||||
|
split := strings.Split(opt, "=")
|
||||||
|
if len(split) == 2 {
|
||||||
|
stats.Opts[split[0]] = split[1]
|
||||||
|
} else {
|
||||||
|
stats.Opts[opt] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
case fieldAge:
|
case fieldAge:
|
||||||
// Age integer is in seconds
|
// Age integer is in seconds
|
||||||
d, err := time.ParseDuration(ss[1] + "s")
|
d, err := time.ParseDuration(ss[1] + "s")
|
||||||
|
@ -347,7 +380,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
|
||||||
return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss)
|
return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
tstats, err := parseNFSTransportStats(ss[2:], statVersion)
|
tstats, err := parseNFSTransportStats(ss[1:], statVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -491,15 +524,15 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = append(ops, NFSOperationStats{
|
ops = append(ops, NFSOperationStats{
|
||||||
Operation: strings.TrimSuffix(ss[0], ":"),
|
Operation: strings.TrimSuffix(ss[0], ":"),
|
||||||
Requests: ns[0],
|
Requests: ns[0],
|
||||||
Transmissions: ns[1],
|
Transmissions: ns[1],
|
||||||
MajorTimeouts: ns[2],
|
MajorTimeouts: ns[2],
|
||||||
BytesSent: ns[3],
|
BytesSent: ns[3],
|
||||||
BytesReceived: ns[4],
|
BytesReceived: ns[4],
|
||||||
CumulativeQueueTime: time.Duration(ns[5]) * time.Millisecond,
|
CumulativeQueueMilliseconds: ns[5],
|
||||||
CumulativeTotalResponseTime: time.Duration(ns[6]) * time.Millisecond,
|
CumulativeTotalResponseMilliseconds: ns[6],
|
||||||
CumulativeTotalRequestTime: time.Duration(ns[7]) * time.Millisecond,
|
CumulativeTotalRequestMilliseconds: ns[7],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,13 +542,33 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
|
||||||
// parseNFSTransportStats parses a NFSTransportStats line using an input set of
|
// parseNFSTransportStats parses a NFSTransportStats line using an input set of
|
||||||
// integer fields matched to a specific stats version.
|
// integer fields matched to a specific stats version.
|
||||||
func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats, error) {
|
func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats, error) {
|
||||||
|
// Extract the protocol field. It is the only string value in the line
|
||||||
|
protocol := ss[0]
|
||||||
|
ss = ss[1:]
|
||||||
|
|
||||||
switch statVersion {
|
switch statVersion {
|
||||||
case statVersion10:
|
case statVersion10:
|
||||||
if len(ss) != fieldTransport10Len {
|
var expectedLength int
|
||||||
|
if protocol == "tcp" {
|
||||||
|
expectedLength = fieldTransport10TCPLen
|
||||||
|
} else if protocol == "udp" {
|
||||||
|
expectedLength = fieldTransport10UDPLen
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.0 statement: %v", protocol, ss)
|
||||||
|
}
|
||||||
|
if len(ss) != expectedLength {
|
||||||
return nil, fmt.Errorf("invalid NFS transport stats 1.0 statement: %v", ss)
|
return nil, fmt.Errorf("invalid NFS transport stats 1.0 statement: %v", ss)
|
||||||
}
|
}
|
||||||
case statVersion11:
|
case statVersion11:
|
||||||
if len(ss) != fieldTransport11Len {
|
var expectedLength int
|
||||||
|
if protocol == "tcp" {
|
||||||
|
expectedLength = fieldTransport11TCPLen
|
||||||
|
} else if protocol == "udp" {
|
||||||
|
expectedLength = fieldTransport11UDPLen
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.1 statement: %v", protocol, ss)
|
||||||
|
}
|
||||||
|
if len(ss) != expectedLength {
|
||||||
return nil, fmt.Errorf("invalid NFS transport stats 1.1 statement: %v", ss)
|
return nil, fmt.Errorf("invalid NFS transport stats 1.1 statement: %v", ss)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -523,12 +576,13 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay
|
// Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay
|
||||||
// in a v1.0 response.
|
// in a v1.0 response. Since the stat length is bigger for TCP stats, we use
|
||||||
|
// the TCP length here.
|
||||||
//
|
//
|
||||||
// Note: slice length must be set to length of v1.1 stats to avoid a panic when
|
// Note: slice length must be set to length of v1.1 stats to avoid a panic when
|
||||||
// only v1.0 stats are present.
|
// only v1.0 stats are present.
|
||||||
// See: https://github.com/prometheus/node_exporter/issues/571.
|
// See: https://github.com/prometheus/node_exporter/issues/571.
|
||||||
ns := make([]uint64, fieldTransport11Len)
|
ns := make([]uint64, fieldTransport11TCPLen)
|
||||||
for i, s := range ss {
|
for i, s := range ss {
|
||||||
n, err := strconv.ParseUint(s, 10, 64)
|
n, err := strconv.ParseUint(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -538,12 +592,23 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats
|
||||||
ns[i] = n
|
ns[i] = n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The fields differ depending on the transport protocol (TCP or UDP)
|
||||||
|
// From https://utcc.utoronto.ca/%7Ecks/space/blog/linux/NFSMountstatsXprt
|
||||||
|
//
|
||||||
|
// For the udp RPC transport there is no connection count, connect idle time,
|
||||||
|
// or idle time (fields #3, #4, and #5); all other fields are the same. So
|
||||||
|
// we set them to 0 here.
|
||||||
|
if protocol == "udp" {
|
||||||
|
ns = append(ns[:2], append(make([]uint64, 3), ns[2:]...)...)
|
||||||
|
}
|
||||||
|
|
||||||
return &NFSTransportStats{
|
return &NFSTransportStats{
|
||||||
|
Protocol: protocol,
|
||||||
Port: ns[0],
|
Port: ns[0],
|
||||||
Bind: ns[1],
|
Bind: ns[1],
|
||||||
Connect: ns[2],
|
Connect: ns[2],
|
||||||
ConnectIdleTime: ns[3],
|
ConnectIdleTime: ns[3],
|
||||||
IdleTime: time.Duration(ns[4]) * time.Second,
|
IdleTimeSeconds: ns[4],
|
||||||
Sends: ns[5],
|
Sends: ns[5],
|
||||||
Receives: ns[6],
|
Receives: ns[6],
|
||||||
BadTransactionIDs: ns[7],
|
BadTransactionIDs: ns[7],
|
||||||
|
|
216
vendor/github.com/prometheus/procfs/net_dev.go
generated
vendored
Normal file
216
vendor/github.com/prometheus/procfs/net_dev.go
generated
vendored
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetDevLine is single line parsed from /proc/net/dev or /proc/[pid]/net/dev.
|
||||||
|
type NetDevLine struct {
|
||||||
|
Name string `json:"name"` // The name of the interface.
|
||||||
|
RxBytes uint64 `json:"rx_bytes"` // Cumulative count of bytes received.
|
||||||
|
RxPackets uint64 `json:"rx_packets"` // Cumulative count of packets received.
|
||||||
|
RxErrors uint64 `json:"rx_errors"` // Cumulative count of receive errors encountered.
|
||||||
|
RxDropped uint64 `json:"rx_dropped"` // Cumulative count of packets dropped while receiving.
|
||||||
|
RxFIFO uint64 `json:"rx_fifo"` // Cumulative count of FIFO buffer errors.
|
||||||
|
RxFrame uint64 `json:"rx_frame"` // Cumulative count of packet framing errors.
|
||||||
|
RxCompressed uint64 `json:"rx_compressed"` // Cumulative count of compressed packets received by the device driver.
|
||||||
|
RxMulticast uint64 `json:"rx_multicast"` // Cumulative count of multicast frames received by the device driver.
|
||||||
|
TxBytes uint64 `json:"tx_bytes"` // Cumulative count of bytes transmitted.
|
||||||
|
TxPackets uint64 `json:"tx_packets"` // Cumulative count of packets transmitted.
|
||||||
|
TxErrors uint64 `json:"tx_errors"` // Cumulative count of transmit errors encountered.
|
||||||
|
TxDropped uint64 `json:"tx_dropped"` // Cumulative count of packets dropped while transmitting.
|
||||||
|
TxFIFO uint64 `json:"tx_fifo"` // Cumulative count of FIFO buffer errors.
|
||||||
|
TxCollisions uint64 `json:"tx_collisions"` // Cumulative count of collisions detected on the interface.
|
||||||
|
TxCarrier uint64 `json:"tx_carrier"` // Cumulative count of carrier losses detected by the device driver.
|
||||||
|
TxCompressed uint64 `json:"tx_compressed"` // Cumulative count of compressed packets transmitted by the device driver.
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetDev is parsed from /proc/net/dev or /proc/[pid]/net/dev. The map keys
|
||||||
|
// are interface names.
|
||||||
|
type NetDev map[string]NetDevLine
|
||||||
|
|
||||||
|
// NewNetDev returns kernel/system statistics read from /proc/net/dev.
|
||||||
|
func NewNetDev() (NetDev, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewNetDev()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetDev returns kernel/system statistics read from /proc/net/dev.
|
||||||
|
func (fs FS) NewNetDev() (NetDev, error) {
|
||||||
|
return newNetDev(fs.proc.Path("net/dev"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetDev returns kernel/system statistics read from /proc/[pid]/net/dev.
|
||||||
|
func (p Proc) NewNetDev() (NetDev, error) {
|
||||||
|
return newNetDev(p.path("net/dev"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newNetDev creates a new NetDev from the contents of the given file.
|
||||||
|
func newNetDev(file string) (NetDev, error) {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return NetDev{}, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
netDev := NetDev{}
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
for n := 0; s.Scan(); n++ {
|
||||||
|
// Skip the 2 header lines.
|
||||||
|
if n < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
line, err := netDev.parseLine(s.Text())
|
||||||
|
if err != nil {
|
||||||
|
return netDev, err
|
||||||
|
}
|
||||||
|
|
||||||
|
netDev[line.Name] = *line
|
||||||
|
}
|
||||||
|
|
||||||
|
return netDev, s.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLine parses a single line from the /proc/net/dev file. Header lines
|
||||||
|
// must be filtered prior to calling this method.
|
||||||
|
func (netDev NetDev) parseLine(rawLine string) (*NetDevLine, error) {
|
||||||
|
parts := strings.SplitN(rawLine, ":", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, errors.New("invalid net/dev line, missing colon")
|
||||||
|
}
|
||||||
|
fields := strings.Fields(strings.TrimSpace(parts[1]))
|
||||||
|
|
||||||
|
var err error
|
||||||
|
line := &NetDevLine{}
|
||||||
|
|
||||||
|
// Interface Name
|
||||||
|
line.Name = strings.TrimSpace(parts[0])
|
||||||
|
if line.Name == "" {
|
||||||
|
return nil, errors.New("invalid net/dev line, empty interface name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RX
|
||||||
|
line.RxBytes, err = strconv.ParseUint(fields[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxPackets, err = strconv.ParseUint(fields[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxErrors, err = strconv.ParseUint(fields[2], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxDropped, err = strconv.ParseUint(fields[3], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxFIFO, err = strconv.ParseUint(fields[4], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxFrame, err = strconv.ParseUint(fields[5], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxCompressed, err = strconv.ParseUint(fields[6], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.RxMulticast, err = strconv.ParseUint(fields[7], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TX
|
||||||
|
line.TxBytes, err = strconv.ParseUint(fields[8], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxPackets, err = strconv.ParseUint(fields[9], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxErrors, err = strconv.ParseUint(fields[10], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxDropped, err = strconv.ParseUint(fields[11], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxFIFO, err = strconv.ParseUint(fields[12], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxCollisions, err = strconv.ParseUint(fields[13], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxCarrier, err = strconv.ParseUint(fields[14], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
line.TxCompressed, err = strconv.ParseUint(fields[15], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return line, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total aggregates the values across interfaces and returns a new NetDevLine.
|
||||||
|
// The Name field will be a sorted comma separated list of interface names.
|
||||||
|
func (netDev NetDev) Total() NetDevLine {
|
||||||
|
total := NetDevLine{}
|
||||||
|
|
||||||
|
names := make([]string, 0, len(netDev))
|
||||||
|
for _, ifc := range netDev {
|
||||||
|
names = append(names, ifc.Name)
|
||||||
|
total.RxBytes += ifc.RxBytes
|
||||||
|
total.RxPackets += ifc.RxPackets
|
||||||
|
total.RxPackets += ifc.RxPackets
|
||||||
|
total.RxErrors += ifc.RxErrors
|
||||||
|
total.RxDropped += ifc.RxDropped
|
||||||
|
total.RxFIFO += ifc.RxFIFO
|
||||||
|
total.RxFrame += ifc.RxFrame
|
||||||
|
total.RxCompressed += ifc.RxCompressed
|
||||||
|
total.RxMulticast += ifc.RxMulticast
|
||||||
|
total.TxBytes += ifc.TxBytes
|
||||||
|
total.TxPackets += ifc.TxPackets
|
||||||
|
total.TxErrors += ifc.TxErrors
|
||||||
|
total.TxDropped += ifc.TxDropped
|
||||||
|
total.TxFIFO += ifc.TxFIFO
|
||||||
|
total.TxCollisions += ifc.TxCollisions
|
||||||
|
total.TxCarrier += ifc.TxCarrier
|
||||||
|
total.TxCompressed += ifc.TxCompressed
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
total.Name = strings.Join(names, ", ")
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
275
vendor/github.com/prometheus/procfs/net_unix.go
generated
vendored
Normal file
275
vendor/github.com/prometheus/procfs/net_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// For the proc file format details,
|
||||||
|
// see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815
|
||||||
|
// and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48.
|
||||||
|
|
||||||
|
const (
|
||||||
|
netUnixKernelPtrIdx = iota
|
||||||
|
netUnixRefCountIdx
|
||||||
|
_
|
||||||
|
netUnixFlagsIdx
|
||||||
|
netUnixTypeIdx
|
||||||
|
netUnixStateIdx
|
||||||
|
netUnixInodeIdx
|
||||||
|
|
||||||
|
// Inode and Path are optional.
|
||||||
|
netUnixStaticFieldsCnt = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
netUnixTypeStream = 1
|
||||||
|
netUnixTypeDgram = 2
|
||||||
|
netUnixTypeSeqpacket = 5
|
||||||
|
|
||||||
|
netUnixFlagListen = 1 << 16
|
||||||
|
|
||||||
|
netUnixStateUnconnected = 1
|
||||||
|
netUnixStateConnecting = 2
|
||||||
|
netUnixStateConnected = 3
|
||||||
|
netUnixStateDisconnected = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
var errInvalidKernelPtrFmt = errors.New("Invalid Num(the kernel table slot number) format")
|
||||||
|
|
||||||
|
// NetUnixType is the type of the type field.
|
||||||
|
type NetUnixType uint64
|
||||||
|
|
||||||
|
// NetUnixFlags is the type of the flags field.
|
||||||
|
type NetUnixFlags uint64
|
||||||
|
|
||||||
|
// NetUnixState is the type of the state field.
|
||||||
|
type NetUnixState uint64
|
||||||
|
|
||||||
|
// NetUnixLine represents a line of /proc/net/unix.
|
||||||
|
type NetUnixLine struct {
|
||||||
|
KernelPtr string
|
||||||
|
RefCount uint64
|
||||||
|
Protocol uint64
|
||||||
|
Flags NetUnixFlags
|
||||||
|
Type NetUnixType
|
||||||
|
State NetUnixState
|
||||||
|
Inode uint64
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetUnix holds the data read from /proc/net/unix.
|
||||||
|
type NetUnix struct {
|
||||||
|
Rows []*NetUnixLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetUnix returns data read from /proc/net/unix.
|
||||||
|
func NewNetUnix() (*NetUnix, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewNetUnix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetUnix returns data read from /proc/net/unix.
|
||||||
|
func (fs FS) NewNetUnix() (*NetUnix, error) {
|
||||||
|
return NewNetUnixByPath(fs.proc.Path("net/unix"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetUnixByPath returns data read from /proc/net/unix by file path.
|
||||||
|
// It might returns an error with partial parsed data, if an error occur after some data parsed.
|
||||||
|
func NewNetUnixByPath(path string) (*NetUnix, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return NewNetUnixByReader(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetUnixByReader returns data read from /proc/net/unix by a reader.
|
||||||
|
// It might returns an error with partial parsed data, if an error occur after some data parsed.
|
||||||
|
func NewNetUnixByReader(reader io.Reader) (*NetUnix, error) {
|
||||||
|
nu := &NetUnix{
|
||||||
|
Rows: make([]*NetUnixLine, 0, 32),
|
||||||
|
}
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
// Omit the header line.
|
||||||
|
scanner.Scan()
|
||||||
|
header := scanner.Text()
|
||||||
|
// From the man page of proc(5), it does not contain an Inode field,
|
||||||
|
// but in actually it exists.
|
||||||
|
// This code works for both cases.
|
||||||
|
hasInode := strings.Contains(header, "Inode")
|
||||||
|
|
||||||
|
minFieldsCnt := netUnixStaticFieldsCnt
|
||||||
|
if hasInode {
|
||||||
|
minFieldsCnt++
|
||||||
|
}
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
item, err := nu.parseLine(line, hasInode, minFieldsCnt)
|
||||||
|
if err != nil {
|
||||||
|
return nu, err
|
||||||
|
}
|
||||||
|
nu.Rows = append(nu.Rows, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nu, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *NetUnix) parseLine(line string, hasInode bool, minFieldsCnt int) (*NetUnixLine, error) {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
fieldsLen := len(fields)
|
||||||
|
if fieldsLen < minFieldsCnt {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Parse Unix domain failed: expect at least %d fields but got %d",
|
||||||
|
minFieldsCnt, fieldsLen)
|
||||||
|
}
|
||||||
|
kernelPtr, err := u.parseKernelPtr(fields[netUnixKernelPtrIdx])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Parse Unix domain num(%s) failed: %s", fields[netUnixKernelPtrIdx], err)
|
||||||
|
}
|
||||||
|
users, err := u.parseUsers(fields[netUnixRefCountIdx])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Parse Unix domain ref count(%s) failed: %s", fields[netUnixRefCountIdx], err)
|
||||||
|
}
|
||||||
|
flags, err := u.parseFlags(fields[netUnixFlagsIdx])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Parse Unix domain flags(%s) failed: %s", fields[netUnixFlagsIdx], err)
|
||||||
|
}
|
||||||
|
typ, err := u.parseType(fields[netUnixTypeIdx])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Parse Unix domain type(%s) failed: %s", fields[netUnixTypeIdx], err)
|
||||||
|
}
|
||||||
|
state, err := u.parseState(fields[netUnixStateIdx])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Parse Unix domain state(%s) failed: %s", fields[netUnixStateIdx], err)
|
||||||
|
}
|
||||||
|
var inode uint64
|
||||||
|
if hasInode {
|
||||||
|
inodeStr := fields[netUnixInodeIdx]
|
||||||
|
inode, err = u.parseInode(inodeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Parse Unix domain inode(%s) failed: %s", inodeStr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nuLine := &NetUnixLine{
|
||||||
|
KernelPtr: kernelPtr,
|
||||||
|
RefCount: users,
|
||||||
|
Type: typ,
|
||||||
|
Flags: flags,
|
||||||
|
State: state,
|
||||||
|
Inode: inode,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path field is optional.
|
||||||
|
if fieldsLen > minFieldsCnt {
|
||||||
|
pathIdx := netUnixInodeIdx + 1
|
||||||
|
if !hasInode {
|
||||||
|
pathIdx--
|
||||||
|
}
|
||||||
|
nuLine.Path = fields[pathIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nuLine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseKernelPtr(str string) (string, error) {
|
||||||
|
if !strings.HasSuffix(str, ":") {
|
||||||
|
return "", errInvalidKernelPtrFmt
|
||||||
|
}
|
||||||
|
return str[:len(str)-1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseUsers(hexStr string) (uint64, error) {
|
||||||
|
return strconv.ParseUint(hexStr, 16, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseProtocol(hexStr string) (uint64, error) {
|
||||||
|
return strconv.ParseUint(hexStr, 16, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseType(hexStr string) (NetUnixType, error) {
|
||||||
|
typ, err := strconv.ParseUint(hexStr, 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return NetUnixType(typ), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseFlags(hexStr string) (NetUnixFlags, error) {
|
||||||
|
flags, err := strconv.ParseUint(hexStr, 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return NetUnixFlags(flags), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseState(hexStr string) (NetUnixState, error) {
|
||||||
|
st, err := strconv.ParseInt(hexStr, 16, 8)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return NetUnixState(st), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u NetUnix) parseInode(inodeStr string) (uint64, error) {
|
||||||
|
return strconv.ParseUint(inodeStr, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t NetUnixType) String() string {
|
||||||
|
switch t {
|
||||||
|
case netUnixTypeStream:
|
||||||
|
return "stream"
|
||||||
|
case netUnixTypeDgram:
|
||||||
|
return "dgram"
|
||||||
|
case netUnixTypeSeqpacket:
|
||||||
|
return "seqpacket"
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f NetUnixFlags) String() string {
|
||||||
|
switch f {
|
||||||
|
case netUnixFlagListen:
|
||||||
|
return "listen"
|
||||||
|
default:
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s NetUnixState) String() string {
|
||||||
|
switch s {
|
||||||
|
case netUnixStateUnconnected:
|
||||||
|
return "unconnected"
|
||||||
|
case netUnixStateConnecting:
|
||||||
|
return "connecting"
|
||||||
|
case netUnixStateConnected:
|
||||||
|
return "connected"
|
||||||
|
case netUnixStateDisconnected:
|
||||||
|
return "disconnected"
|
||||||
|
}
|
||||||
|
return "unknown"
|
||||||
|
}
|
52
vendor/github.com/prometheus/procfs/proc.go
generated
vendored
52
vendor/github.com/prometheus/procfs/proc.go
generated
vendored
|
@ -1,11 +1,27 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/prometheus/procfs/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proc provides information about a running process.
|
// Proc provides information about a running process.
|
||||||
|
@ -13,7 +29,7 @@ type Proc struct {
|
||||||
// The process ID.
|
// The process ID.
|
||||||
PID int
|
PID int
|
||||||
|
|
||||||
fs FS
|
fs fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procs represents a list of Proc structs.
|
// Procs represents a list of Proc structs.
|
||||||
|
@ -52,11 +68,11 @@ func AllProcs() (Procs, error) {
|
||||||
|
|
||||||
// Self returns a process for the current process.
|
// Self returns a process for the current process.
|
||||||
func (fs FS) Self() (Proc, error) {
|
func (fs FS) Self() (Proc, error) {
|
||||||
p, err := os.Readlink(fs.Path("self"))
|
p, err := os.Readlink(fs.proc.Path("self"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Proc{}, err
|
return Proc{}, err
|
||||||
}
|
}
|
||||||
pid, err := strconv.Atoi(strings.Replace(p, string(fs), "", -1))
|
pid, err := strconv.Atoi(strings.Replace(p, string(fs.proc), "", -1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Proc{}, err
|
return Proc{}, err
|
||||||
}
|
}
|
||||||
|
@ -65,15 +81,15 @@ func (fs FS) Self() (Proc, error) {
|
||||||
|
|
||||||
// NewProc returns a process for the given pid.
|
// NewProc returns a process for the given pid.
|
||||||
func (fs FS) NewProc(pid int) (Proc, error) {
|
func (fs FS) NewProc(pid int) (Proc, error) {
|
||||||
if _, err := os.Stat(fs.Path(strconv.Itoa(pid))); err != nil {
|
if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
|
||||||
return Proc{}, err
|
return Proc{}, err
|
||||||
}
|
}
|
||||||
return Proc{PID: pid, fs: fs}, nil
|
return Proc{PID: pid, fs: fs.proc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllProcs returns a list of all currently available processes.
|
// AllProcs returns a list of all currently available processes.
|
||||||
func (fs FS) AllProcs() (Procs, error) {
|
func (fs FS) AllProcs() (Procs, error) {
|
||||||
d, err := os.Open(fs.Path())
|
d, err := os.Open(fs.proc.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Procs{}, err
|
return Procs{}, err
|
||||||
}
|
}
|
||||||
|
@ -90,7 +106,7 @@ func (fs FS) AllProcs() (Procs, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
p = append(p, Proc{PID: int(pid), fs: fs})
|
p = append(p, Proc{PID: int(pid), fs: fs.proc})
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
|
@ -113,7 +129,7 @@ func (p Proc) CmdLine() ([]string, error) {
|
||||||
return []string{}, nil
|
return []string{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Split(string(data[:len(data)-1]), string(byte(0))), nil
|
return strings.Split(string(bytes.TrimRight(data, string("\x00"))), string(byte(0))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comm returns the command name of a process.
|
// Comm returns the command name of a process.
|
||||||
|
@ -142,6 +158,26 @@ func (p Proc) Executable() (string, error) {
|
||||||
return exe, err
|
return exe, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cwd returns the absolute path to the current working directory of the process.
|
||||||
|
func (p Proc) Cwd() (string, error) {
|
||||||
|
wd, err := os.Readlink(p.path("cwd"))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return wd, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RootDir returns the absolute path to the process's root directory (as set by chroot)
|
||||||
|
func (p Proc) RootDir() (string, error) {
|
||||||
|
rdir, err := os.Readlink(p.path("root"))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return rdir, err
|
||||||
|
}
|
||||||
|
|
||||||
// FileDescriptors returns the currently open file descriptors of a process.
|
// FileDescriptors returns the currently open file descriptors of a process.
|
||||||
func (p Proc) FileDescriptors() ([]uintptr, error) {
|
func (p Proc) FileDescriptors() ([]uintptr, error) {
|
||||||
names, err := p.fileDescriptors()
|
names, err := p.fileDescriptors()
|
||||||
|
|
18
vendor/github.com/prometheus/procfs/proc_io.go
generated
vendored
18
vendor/github.com/prometheus/procfs/proc_io.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -47,9 +60,6 @@ func (p Proc) NewIO() (ProcIO, error) {
|
||||||
|
|
||||||
_, err = fmt.Sscanf(string(data), ioFormat, &pio.RChar, &pio.WChar, &pio.SyscR,
|
_, err = fmt.Sscanf(string(data), ioFormat, &pio.RChar, &pio.WChar, &pio.SyscR,
|
||||||
&pio.SyscW, &pio.ReadBytes, &pio.WriteBytes, &pio.CancelledWriteBytes)
|
&pio.SyscW, &pio.ReadBytes, &pio.WriteBytes, &pio.CancelledWriteBytes)
|
||||||
if err != nil {
|
|
||||||
return pio, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pio, nil
|
return pio, err
|
||||||
}
|
}
|
||||||
|
|
51
vendor/github.com/prometheus/procfs/proc_limits.go
generated
vendored
51
vendor/github.com/prometheus/procfs/proc_limits.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -13,46 +26,46 @@ import (
|
||||||
// http://man7.org/linux/man-pages/man2/getrlimit.2.html.
|
// http://man7.org/linux/man-pages/man2/getrlimit.2.html.
|
||||||
type ProcLimits struct {
|
type ProcLimits struct {
|
||||||
// CPU time limit in seconds.
|
// CPU time limit in seconds.
|
||||||
CPUTime int
|
CPUTime int64
|
||||||
// Maximum size of files that the process may create.
|
// Maximum size of files that the process may create.
|
||||||
FileSize int
|
FileSize int64
|
||||||
// Maximum size of the process's data segment (initialized data,
|
// Maximum size of the process's data segment (initialized data,
|
||||||
// uninitialized data, and heap).
|
// uninitialized data, and heap).
|
||||||
DataSize int
|
DataSize int64
|
||||||
// Maximum size of the process stack in bytes.
|
// Maximum size of the process stack in bytes.
|
||||||
StackSize int
|
StackSize int64
|
||||||
// Maximum size of a core file.
|
// Maximum size of a core file.
|
||||||
CoreFileSize int
|
CoreFileSize int64
|
||||||
// Limit of the process's resident set in pages.
|
// Limit of the process's resident set in pages.
|
||||||
ResidentSet int
|
ResidentSet int64
|
||||||
// Maximum number of processes that can be created for the real user ID of
|
// Maximum number of processes that can be created for the real user ID of
|
||||||
// the calling process.
|
// the calling process.
|
||||||
Processes int
|
Processes int64
|
||||||
// Value one greater than the maximum file descriptor number that can be
|
// Value one greater than the maximum file descriptor number that can be
|
||||||
// opened by this process.
|
// opened by this process.
|
||||||
OpenFiles int
|
OpenFiles int64
|
||||||
// Maximum number of bytes of memory that may be locked into RAM.
|
// Maximum number of bytes of memory that may be locked into RAM.
|
||||||
LockedMemory int
|
LockedMemory int64
|
||||||
// Maximum size of the process's virtual memory address space in bytes.
|
// Maximum size of the process's virtual memory address space in bytes.
|
||||||
AddressSpace int
|
AddressSpace int64
|
||||||
// Limit on the combined number of flock(2) locks and fcntl(2) leases that
|
// Limit on the combined number of flock(2) locks and fcntl(2) leases that
|
||||||
// this process may establish.
|
// this process may establish.
|
||||||
FileLocks int
|
FileLocks int64
|
||||||
// Limit of signals that may be queued for the real user ID of the calling
|
// Limit of signals that may be queued for the real user ID of the calling
|
||||||
// process.
|
// process.
|
||||||
PendingSignals int
|
PendingSignals int64
|
||||||
// Limit on the number of bytes that can be allocated for POSIX message
|
// Limit on the number of bytes that can be allocated for POSIX message
|
||||||
// queues for the real user ID of the calling process.
|
// queues for the real user ID of the calling process.
|
||||||
MsqqueueSize int
|
MsqqueueSize int64
|
||||||
// Limit of the nice priority set using setpriority(2) or nice(2).
|
// Limit of the nice priority set using setpriority(2) or nice(2).
|
||||||
NicePriority int
|
NicePriority int64
|
||||||
// Limit of the real-time priority set using sched_setscheduler(2) or
|
// Limit of the real-time priority set using sched_setscheduler(2) or
|
||||||
// sched_setparam(2).
|
// sched_setparam(2).
|
||||||
RealtimePriority int
|
RealtimePriority int64
|
||||||
// Limit (in microseconds) on the amount of CPU time that a process
|
// Limit (in microseconds) on the amount of CPU time that a process
|
||||||
// scheduled under a real-time scheduling policy may consume without making
|
// scheduled under a real-time scheduling policy may consume without making
|
||||||
// a blocking system call.
|
// a blocking system call.
|
||||||
RealtimeTimeout int
|
RealtimeTimeout int64
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -125,13 +138,13 @@ func (p Proc) NewLimits() (ProcLimits, error) {
|
||||||
return l, s.Err()
|
return l, s.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseInt(s string) (int, error) {
|
func parseInt(s string) (int64, error) {
|
||||||
if s == limitsUnlimited {
|
if s == limitsUnlimited {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
i, err := strconv.ParseInt(s, 10, 32)
|
i, err := strconv.ParseInt(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("couldn't parse value %s: %s", s, err)
|
return 0, fmt.Errorf("couldn't parse value %s: %s", s, err)
|
||||||
}
|
}
|
||||||
return int(i), nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
68
vendor/github.com/prometheus/procfs/proc_ns.go
generated
vendored
Normal file
68
vendor/github.com/prometheus/procfs/proc_ns.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Namespace represents a single namespace of a process.
|
||||||
|
type Namespace struct {
|
||||||
|
Type string // Namespace type.
|
||||||
|
Inode uint32 // Inode number of the namespace. If two processes are in the same namespace their inodes will match.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Namespaces contains all of the namespaces that the process is contained in.
|
||||||
|
type Namespaces map[string]Namespace
|
||||||
|
|
||||||
|
// NewNamespaces reads from /proc/<pid>/ns/* to get the namespaces of which the
|
||||||
|
// process is a member.
|
||||||
|
func (p Proc) NewNamespaces() (Namespaces, error) {
|
||||||
|
d, err := os.Open(p.path("ns"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
|
||||||
|
names, err := d.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read contents of ns dir: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := make(Namespaces, len(names))
|
||||||
|
for _, name := range names {
|
||||||
|
target, err := os.Readlink(p.path("ns", name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.SplitN(target, ":", 2)
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return nil, fmt.Errorf("failed to parse namespace type and inode from '%v'", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := fields[0]
|
||||||
|
inode, err := strconv.ParseUint(strings.Trim(fields[1], "[]"), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse inode from '%v': %v", fields[1], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ns[name] = Namespace{typ, uint32(inode)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ns, nil
|
||||||
|
}
|
110
vendor/github.com/prometheus/procfs/proc_psi.go
generated
vendored
Normal file
110
vendor/github.com/prometheus/procfs/proc_psi.go
generated
vendored
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2019 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
// The PSI / pressure interface is described at
|
||||||
|
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/accounting/psi.txt
|
||||||
|
// Each resource (cpu, io, memory, ...) is exposed as a single file.
|
||||||
|
// Each file may contain up to two lines, one for "some" pressure and one for "full" pressure.
|
||||||
|
// Each line contains several averages (over n seconds) and a total in µs.
|
||||||
|
//
|
||||||
|
// Example io pressure file:
|
||||||
|
// > some avg10=0.06 avg60=0.21 avg300=0.99 total=8537362
|
||||||
|
// > full avg10=0.00 avg60=0.13 avg300=0.96 total=8183134
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const lineFormat = "avg10=%f avg60=%f avg300=%f total=%d"
|
||||||
|
|
||||||
|
// PSILine is a single line of values as returned by /proc/pressure/*
|
||||||
|
// The Avg entries are averages over n seconds, as a percentage
|
||||||
|
// The Total line is in microseconds
|
||||||
|
type PSILine struct {
|
||||||
|
Avg10 float64
|
||||||
|
Avg60 float64
|
||||||
|
Avg300 float64
|
||||||
|
Total uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSIStats represent pressure stall information from /proc/pressure/*
|
||||||
|
// Some indicates the share of time in which at least some tasks are stalled
|
||||||
|
// Full indicates the share of time in which all non-idle tasks are stalled simultaneously
|
||||||
|
type PSIStats struct {
|
||||||
|
Some *PSILine
|
||||||
|
Full *PSILine
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPSIStatsForResource reads pressure stall information for the specified
|
||||||
|
// resource. At time of writing this can be either "cpu", "memory" or "io".
|
||||||
|
func NewPSIStatsForResource(resource string) (PSIStats, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return PSIStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewPSIStatsForResource(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPSIStatsForResource reads pressure stall information from /proc/pressure/<resource>
|
||||||
|
func (fs FS) NewPSIStatsForResource(resource string) (PSIStats, error) {
|
||||||
|
file, err := os.Open(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource)))
|
||||||
|
if err != nil {
|
||||||
|
return PSIStats{}, fmt.Errorf("psi_stats: unavailable for %s", resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
return parsePSIStats(resource, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePSIStats parses the specified file for pressure stall information
|
||||||
|
func parsePSIStats(resource string, file io.Reader) (PSIStats, error) {
|
||||||
|
psiStats := PSIStats{}
|
||||||
|
stats, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return psiStats, fmt.Errorf("psi_stats: unable to read data for %s", resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range strings.Split(string(stats), "\n") {
|
||||||
|
prefix := strings.Split(l, " ")[0]
|
||||||
|
switch prefix {
|
||||||
|
case "some":
|
||||||
|
psi := PSILine{}
|
||||||
|
_, err := fmt.Sscanf(l, fmt.Sprintf("some %s", lineFormat), &psi.Avg10, &psi.Avg60, &psi.Avg300, &psi.Total)
|
||||||
|
if err != nil {
|
||||||
|
return PSIStats{}, err
|
||||||
|
}
|
||||||
|
psiStats.Some = &psi
|
||||||
|
case "full":
|
||||||
|
psi := PSILine{}
|
||||||
|
_, err := fmt.Sscanf(l, fmt.Sprintf("full %s", lineFormat), &psi.Avg10, &psi.Avg60, &psi.Avg300, &psi.Total)
|
||||||
|
if err != nil {
|
||||||
|
return PSIStats{}, err
|
||||||
|
}
|
||||||
|
psiStats.Full = &psi
|
||||||
|
default:
|
||||||
|
// If we encounter a line with an unknown prefix, ignore it and move on
|
||||||
|
// Should new measurement types be added in the future we'll simply ignore them instead
|
||||||
|
// of erroring on retrieval
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return psiStats, nil
|
||||||
|
}
|
26
vendor/github.com/prometheus/procfs/proc_stat.go
generated
vendored
26
vendor/github.com/prometheus/procfs/proc_stat.go
generated
vendored
|
@ -1,3 +1,16 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -5,6 +18,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/prometheus/procfs/internal/fs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Originally, this USER_HZ value was dynamically retrieved via a sysconf call
|
// Originally, this USER_HZ value was dynamically retrieved via a sysconf call
|
||||||
|
@ -82,11 +97,11 @@ type ProcStat struct {
|
||||||
// in clock ticks.
|
// in clock ticks.
|
||||||
Starttime uint64
|
Starttime uint64
|
||||||
// Virtual memory size in bytes.
|
// Virtual memory size in bytes.
|
||||||
VSize int
|
VSize uint
|
||||||
// Resident set size in pages.
|
// Resident set size in pages.
|
||||||
RSS int
|
RSS int
|
||||||
|
|
||||||
fs FS
|
proc fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStat returns the current status information of the process.
|
// NewStat returns the current status information of the process.
|
||||||
|
@ -105,7 +120,7 @@ func (p Proc) NewStat() (ProcStat, error) {
|
||||||
var (
|
var (
|
||||||
ignore int
|
ignore int
|
||||||
|
|
||||||
s = ProcStat{PID: p.PID, fs: p.fs}
|
s = ProcStat{PID: p.PID, proc: p.fs}
|
||||||
l = bytes.Index(data, []byte("("))
|
l = bytes.Index(data, []byte("("))
|
||||||
r = bytes.LastIndex(data, []byte(")"))
|
r = bytes.LastIndex(data, []byte(")"))
|
||||||
)
|
)
|
||||||
|
@ -151,7 +166,7 @@ func (p Proc) NewStat() (ProcStat, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// VirtualMemory returns the virtual memory size in bytes.
|
// VirtualMemory returns the virtual memory size in bytes.
|
||||||
func (s ProcStat) VirtualMemory() int {
|
func (s ProcStat) VirtualMemory() uint {
|
||||||
return s.VSize
|
return s.VSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +177,8 @@ func (s ProcStat) ResidentMemory() int {
|
||||||
|
|
||||||
// StartTime returns the unix timestamp of the process in seconds.
|
// StartTime returns the unix timestamp of the process in seconds.
|
||||||
func (s ProcStat) StartTime() (float64, error) {
|
func (s ProcStat) StartTime() (float64, error) {
|
||||||
stat, err := s.fs.NewStat()
|
fs := FS{proc: s.proc}
|
||||||
|
stat, err := fs.NewStat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
162
vendor/github.com/prometheus/procfs/proc_status.go
generated
vendored
Normal file
162
vendor/github.com/prometheus/procfs/proc_status.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcStat provides status information about the process,
|
||||||
|
// read from /proc/[pid]/stat.
|
||||||
|
type ProcStatus struct {
|
||||||
|
// The process ID.
|
||||||
|
PID int
|
||||||
|
// The process name.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Peak virtual memory size.
|
||||||
|
VmPeak uint64
|
||||||
|
// Virtual memory size.
|
||||||
|
VmSize uint64
|
||||||
|
// Locked memory size.
|
||||||
|
VmLck uint64
|
||||||
|
// Pinned memory size.
|
||||||
|
VmPin uint64
|
||||||
|
// Peak resident set size.
|
||||||
|
VmHWM uint64
|
||||||
|
// Resident set size (sum of RssAnnon RssFile and RssShmem).
|
||||||
|
VmRSS uint64
|
||||||
|
// Size of resident anonymous memory.
|
||||||
|
RssAnon uint64
|
||||||
|
// Size of resident file mappings.
|
||||||
|
RssFile uint64
|
||||||
|
// Size of resident shared memory.
|
||||||
|
RssShmem uint64
|
||||||
|
// Size of data segments.
|
||||||
|
VmData uint64
|
||||||
|
// Size of stack segments.
|
||||||
|
VmStk uint64
|
||||||
|
// Size of text segments.
|
||||||
|
VmExe uint64
|
||||||
|
// Shared library code size.
|
||||||
|
VmLib uint64
|
||||||
|
// Page table entries size.
|
||||||
|
VmPTE uint64
|
||||||
|
// Size of second-level page tables.
|
||||||
|
VmPMD uint64
|
||||||
|
// Swapped-out virtual memory size by anonymous private.
|
||||||
|
VmSwap uint64
|
||||||
|
// Size of hugetlb memory portions
|
||||||
|
HugetlbPages uint64
|
||||||
|
|
||||||
|
// Number of voluntary context switches.
|
||||||
|
VoluntaryCtxtSwitches uint64
|
||||||
|
// Number of involuntary context switches.
|
||||||
|
NonVoluntaryCtxtSwitches uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStatus returns the current status information of the process.
|
||||||
|
func (p Proc) NewStatus() (ProcStatus, error) {
|
||||||
|
f, err := os.Open(p.path("status"))
|
||||||
|
if err != nil {
|
||||||
|
return ProcStatus{}, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
return ProcStatus{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := ProcStatus{PID: p.PID}
|
||||||
|
|
||||||
|
lines := strings.Split(string(data), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if !bytes.Contains([]byte(line), []byte(":")) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
kv := strings.SplitN(line, ":", 2)
|
||||||
|
|
||||||
|
// removes spaces
|
||||||
|
k := string(strings.TrimSpace(kv[0]))
|
||||||
|
v := string(strings.TrimSpace(kv[1]))
|
||||||
|
// removes "kB"
|
||||||
|
v = string(bytes.Trim([]byte(v), " kB"))
|
||||||
|
|
||||||
|
// value to int when possible
|
||||||
|
// we can skip error check here, 'cause vKBytes is not used when value is a string
|
||||||
|
vKBytes, _ := strconv.ParseUint(v, 10, 64)
|
||||||
|
// convert kB to B
|
||||||
|
vBytes := vKBytes * 1024
|
||||||
|
|
||||||
|
s.fillStatus(k, v, vKBytes, vBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintBytes uint64) {
|
||||||
|
switch k {
|
||||||
|
case "Name":
|
||||||
|
s.Name = vString
|
||||||
|
case "VmPeak":
|
||||||
|
s.VmPeak = vUintBytes
|
||||||
|
case "VmSize":
|
||||||
|
s.VmSize = vUintBytes
|
||||||
|
case "VmLck":
|
||||||
|
s.VmLck = vUintBytes
|
||||||
|
case "VmPin":
|
||||||
|
s.VmPin = vUintBytes
|
||||||
|
case "VmHWM":
|
||||||
|
s.VmHWM = vUintBytes
|
||||||
|
case "VmRSS":
|
||||||
|
s.VmRSS = vUintBytes
|
||||||
|
case "RssAnon":
|
||||||
|
s.RssAnon = vUintBytes
|
||||||
|
case "RssFile":
|
||||||
|
s.RssFile = vUintBytes
|
||||||
|
case "RssShmem":
|
||||||
|
s.RssShmem = vUintBytes
|
||||||
|
case "VmData":
|
||||||
|
s.VmData = vUintBytes
|
||||||
|
case "VmStk":
|
||||||
|
s.VmStk = vUintBytes
|
||||||
|
case "VmExe":
|
||||||
|
s.VmExe = vUintBytes
|
||||||
|
case "VmLib":
|
||||||
|
s.VmLib = vUintBytes
|
||||||
|
case "VmPTE":
|
||||||
|
s.VmPTE = vUintBytes
|
||||||
|
case "VmPMD":
|
||||||
|
s.VmPMD = vUintBytes
|
||||||
|
case "VmSwap":
|
||||||
|
s.VmSwap = vUintBytes
|
||||||
|
case "HugetlbPages":
|
||||||
|
s.HugetlbPages = vUintBytes
|
||||||
|
case "voluntary_ctxt_switches":
|
||||||
|
s.VoluntaryCtxtSwitches = vUint
|
||||||
|
case "nonvoluntary_ctxt_switches":
|
||||||
|
s.NonVoluntaryCtxtSwitches = vUint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TotalCtxtSwitches returns the total context switch.
|
||||||
|
func (s ProcStatus) TotalCtxtSwitches() uint64 {
|
||||||
|
return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches
|
||||||
|
}
|
208
vendor/github.com/prometheus/procfs/stat.go
generated
vendored
208
vendor/github.com/prometheus/procfs/stat.go
generated
vendored
|
@ -1,17 +1,81 @@
|
||||||
|
// Copyright 2018 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
package procfs
|
package procfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CPUStat shows how much time the cpu spend in various stages.
|
||||||
|
type CPUStat struct {
|
||||||
|
User float64
|
||||||
|
Nice float64
|
||||||
|
System float64
|
||||||
|
Idle float64
|
||||||
|
Iowait float64
|
||||||
|
IRQ float64
|
||||||
|
SoftIRQ float64
|
||||||
|
Steal float64
|
||||||
|
Guest float64
|
||||||
|
GuestNice float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// SoftIRQStat represent the softirq statistics as exported in the procfs stat file.
|
||||||
|
// A nice introduction can be found at https://0xax.gitbooks.io/linux-insides/content/interrupts/interrupts-9.html
|
||||||
|
// It is possible to get per-cpu stats by reading /proc/softirqs
|
||||||
|
type SoftIRQStat struct {
|
||||||
|
Hi uint64
|
||||||
|
Timer uint64
|
||||||
|
NetTx uint64
|
||||||
|
NetRx uint64
|
||||||
|
Block uint64
|
||||||
|
BlockIoPoll uint64
|
||||||
|
Tasklet uint64
|
||||||
|
Sched uint64
|
||||||
|
Hrtimer uint64
|
||||||
|
Rcu uint64
|
||||||
|
}
|
||||||
|
|
||||||
// Stat represents kernel/system statistics.
|
// Stat represents kernel/system statistics.
|
||||||
type Stat struct {
|
type Stat struct {
|
||||||
// Boot time in seconds since the Epoch.
|
// Boot time in seconds since the Epoch.
|
||||||
BootTime int64
|
BootTime uint64
|
||||||
|
// Summed up cpu statistics.
|
||||||
|
CPUTotal CPUStat
|
||||||
|
// Per-CPU statistics.
|
||||||
|
CPU []CPUStat
|
||||||
|
// Number of times interrupts were handled, which contains numbered and unnumbered IRQs.
|
||||||
|
IRQTotal uint64
|
||||||
|
// Number of times a numbered IRQ was triggered.
|
||||||
|
IRQ []uint64
|
||||||
|
// Number of times a context switch happened.
|
||||||
|
ContextSwitches uint64
|
||||||
|
// Number of times a process was created.
|
||||||
|
ProcessCreated uint64
|
||||||
|
// Number of processes currently running.
|
||||||
|
ProcessesRunning uint64
|
||||||
|
// Number of processes currently blocked (waiting for IO).
|
||||||
|
ProcessesBlocked uint64
|
||||||
|
// Number of times a softirq was scheduled.
|
||||||
|
SoftIRQTotal uint64
|
||||||
|
// Detailed softirq statistics.
|
||||||
|
SoftIRQ SoftIRQStat
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStat returns kernel/system statistics read from /proc/stat.
|
// NewStat returns kernel/system statistics read from /proc/stat.
|
||||||
|
@ -24,33 +88,145 @@ func NewStat() (Stat, error) {
|
||||||
return fs.NewStat()
|
return fs.NewStat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse a cpu statistics line and returns the CPUStat struct plus the cpu id (or -1 for the overall sum).
|
||||||
|
func parseCPUStat(line string) (CPUStat, int64, error) {
|
||||||
|
cpuStat := CPUStat{}
|
||||||
|
var cpu string
|
||||||
|
|
||||||
|
count, err := fmt.Sscanf(line, "%s %f %f %f %f %f %f %f %f %f %f",
|
||||||
|
&cpu,
|
||||||
|
&cpuStat.User, &cpuStat.Nice, &cpuStat.System, &cpuStat.Idle,
|
||||||
|
&cpuStat.Iowait, &cpuStat.IRQ, &cpuStat.SoftIRQ, &cpuStat.Steal,
|
||||||
|
&cpuStat.Guest, &cpuStat.GuestNice)
|
||||||
|
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return CPUStat{}, -1, fmt.Errorf("couldn't parse %s (cpu): %s", line, err)
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return CPUStat{}, -1, fmt.Errorf("couldn't parse %s (cpu): 0 elements parsed", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuStat.User /= userHZ
|
||||||
|
cpuStat.Nice /= userHZ
|
||||||
|
cpuStat.System /= userHZ
|
||||||
|
cpuStat.Idle /= userHZ
|
||||||
|
cpuStat.Iowait /= userHZ
|
||||||
|
cpuStat.IRQ /= userHZ
|
||||||
|
cpuStat.SoftIRQ /= userHZ
|
||||||
|
cpuStat.Steal /= userHZ
|
||||||
|
cpuStat.Guest /= userHZ
|
||||||
|
cpuStat.GuestNice /= userHZ
|
||||||
|
|
||||||
|
if cpu == "cpu" {
|
||||||
|
return cpuStat, -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuID, err := strconv.ParseInt(cpu[3:], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return CPUStat{}, -1, fmt.Errorf("couldn't parse %s (cpu/cpuid): %s", line, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpuStat, cpuID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a softirq line.
|
||||||
|
func parseSoftIRQStat(line string) (SoftIRQStat, uint64, error) {
|
||||||
|
softIRQStat := SoftIRQStat{}
|
||||||
|
var total uint64
|
||||||
|
var prefix string
|
||||||
|
|
||||||
|
_, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d",
|
||||||
|
&prefix, &total,
|
||||||
|
&softIRQStat.Hi, &softIRQStat.Timer, &softIRQStat.NetTx, &softIRQStat.NetRx,
|
||||||
|
&softIRQStat.Block, &softIRQStat.BlockIoPoll,
|
||||||
|
&softIRQStat.Tasklet, &softIRQStat.Sched,
|
||||||
|
&softIRQStat.Hrtimer, &softIRQStat.Rcu)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return SoftIRQStat{}, 0, fmt.Errorf("couldn't parse %s (softirq): %s", line, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return softIRQStat, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewStat returns an information about current kernel/system statistics.
|
// NewStat returns an information about current kernel/system statistics.
|
||||||
func (fs FS) NewStat() (Stat, error) {
|
func (fs FS) NewStat() (Stat, error) {
|
||||||
f, err := os.Open(fs.Path("stat"))
|
// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
||||||
|
|
||||||
|
f, err := os.Open(fs.proc.Path("stat"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Stat{}, err
|
return Stat{}, err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
s := bufio.NewScanner(f)
|
stat := Stat{}
|
||||||
for s.Scan() {
|
|
||||||
line := s.Text()
|
scanner := bufio.NewScanner(f)
|
||||||
if !strings.HasPrefix(line, "btime") {
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
parts := strings.Fields(scanner.Text())
|
||||||
|
// require at least <key> <value>
|
||||||
|
if len(parts) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fields := strings.Fields(line)
|
switch {
|
||||||
if len(fields) != 2 {
|
case parts[0] == "btime":
|
||||||
return Stat{}, fmt.Errorf("couldn't parse %s line %s", f.Name(), line)
|
if stat.BootTime, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (btime): %s", parts[1], err)
|
||||||
|
}
|
||||||
|
case parts[0] == "intr":
|
||||||
|
if stat.IRQTotal, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (intr): %s", parts[1], err)
|
||||||
|
}
|
||||||
|
numberedIRQs := parts[2:]
|
||||||
|
stat.IRQ = make([]uint64, len(numberedIRQs))
|
||||||
|
for i, count := range numberedIRQs {
|
||||||
|
if stat.IRQ[i], err = strconv.ParseUint(count, 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (intr%d): %s", count, i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case parts[0] == "ctxt":
|
||||||
|
if stat.ContextSwitches, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (ctxt): %s", parts[1], err)
|
||||||
|
}
|
||||||
|
case parts[0] == "processes":
|
||||||
|
if stat.ProcessCreated, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (processes): %s", parts[1], err)
|
||||||
|
}
|
||||||
|
case parts[0] == "procs_running":
|
||||||
|
if stat.ProcessesRunning, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (procs_running): %s", parts[1], err)
|
||||||
|
}
|
||||||
|
case parts[0] == "procs_blocked":
|
||||||
|
if stat.ProcessesBlocked, err = strconv.ParseUint(parts[1], 10, 64); err != nil {
|
||||||
|
return Stat{}, fmt.Errorf("couldn't parse %s (procs_blocked): %s", parts[1], err)
|
||||||
|
}
|
||||||
|
case parts[0] == "softirq":
|
||||||
|
softIRQStats, total, err := parseSoftIRQStat(line)
|
||||||
|
if err != nil {
|
||||||
|
return Stat{}, err
|
||||||
|
}
|
||||||
|
stat.SoftIRQTotal = total
|
||||||
|
stat.SoftIRQ = softIRQStats
|
||||||
|
case strings.HasPrefix(parts[0], "cpu"):
|
||||||
|
cpuStat, cpuID, err := parseCPUStat(line)
|
||||||
|
if err != nil {
|
||||||
|
return Stat{}, err
|
||||||
|
}
|
||||||
|
if cpuID == -1 {
|
||||||
|
stat.CPUTotal = cpuStat
|
||||||
|
} else {
|
||||||
|
for int64(len(stat.CPU)) <= cpuID {
|
||||||
|
stat.CPU = append(stat.CPU, CPUStat{})
|
||||||
|
}
|
||||||
|
stat.CPU[cpuID] = cpuStat
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i, err := strconv.ParseInt(fields[1], 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return Stat{}, fmt.Errorf("couldn't parse %s: %s", fields[1], err)
|
|
||||||
}
|
|
||||||
return Stat{BootTime: i}, nil
|
|
||||||
}
|
}
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
return Stat{}, fmt.Errorf("couldn't parse %s: %s", f.Name(), err)
|
return Stat{}, fmt.Errorf("couldn't parse %s: %s", f.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Stat{}, fmt.Errorf("couldn't parse %s, missing btime", f.Name())
|
return stat, nil
|
||||||
}
|
}
|
||||||
|
|
413
vendor/github.com/prometheus/procfs/ttar
generated
vendored
Executable file
413
vendor/github.com/prometheus/procfs/ttar
generated
vendored
Executable file
|
@ -0,0 +1,413 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Purpose: plain text tar format
|
||||||
|
# Limitations: - only suitable for text files, directories, and symlinks
|
||||||
|
# - stores only filename, content, and mode
|
||||||
|
# - not designed for untrusted input
|
||||||
|
#
|
||||||
|
# Note: must work with bash version 3.2 (macOS)
|
||||||
|
|
||||||
|
# Copyright 2017 Roger Luethi
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
set -o errexit -o nounset
|
||||||
|
|
||||||
|
# Sanitize environment (for instance, standard sorting of glob matches)
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
|
path=""
|
||||||
|
CMD=""
|
||||||
|
ARG_STRING="$*"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Not all sed implementations can work on null bytes. In order to make ttar
|
||||||
|
# work out of the box on macOS, use Python as a stream editor.
|
||||||
|
|
||||||
|
USE_PYTHON=0
|
||||||
|
|
||||||
|
PYTHON_CREATE_FILTER=$(cat << 'PCF'
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
for line in sys.stdin:
|
||||||
|
line = re.sub(r'EOF', r'\EOF', line)
|
||||||
|
line = re.sub(r'NULLBYTE', r'\NULLBYTE', line)
|
||||||
|
line = re.sub('\x00', r'NULLBYTE', line)
|
||||||
|
sys.stdout.write(line)
|
||||||
|
PCF
|
||||||
|
)
|
||||||
|
|
||||||
|
PYTHON_EXTRACT_FILTER=$(cat << 'PEF'
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
for line in sys.stdin:
|
||||||
|
line = re.sub(r'(?<!\\)NULLBYTE', '\x00', line)
|
||||||
|
line = re.sub(r'\\NULLBYTE', 'NULLBYTE', line)
|
||||||
|
line = re.sub(r'([^\\])EOF', r'\1', line)
|
||||||
|
line = re.sub(r'\\EOF', 'EOF', line)
|
||||||
|
sys.stdout.write(line)
|
||||||
|
PEF
|
||||||
|
)
|
||||||
|
|
||||||
|
function test_environment {
|
||||||
|
if [[ "$(echo "a" | sed 's/a/\x0/' | wc -c)" -ne 2 ]]; then
|
||||||
|
echo "WARNING sed unable to handle null bytes, using Python (slow)."
|
||||||
|
if ! which python >/dev/null; then
|
||||||
|
echo "ERROR Python not found. Aborting."
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
USE_PYTHON=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function usage {
|
||||||
|
bname=$(basename "$0")
|
||||||
|
cat << USAGE
|
||||||
|
Usage: $bname [-C <DIR>] -c -f <ARCHIVE> <FILE...> (create archive)
|
||||||
|
$bname -t -f <ARCHIVE> (list archive contents)
|
||||||
|
$bname [-C <DIR>] -x -f <ARCHIVE> (extract archive)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-C <DIR> (change directory)
|
||||||
|
-v (verbose)
|
||||||
|
--recursive-unlink (recursively delete existing directory if path
|
||||||
|
collides with file or directory to extract)
|
||||||
|
|
||||||
|
Example: Change to sysfs directory, create ttar file from fixtures directory
|
||||||
|
$bname -C sysfs -c -f sysfs/fixtures.ttar fixtures/
|
||||||
|
USAGE
|
||||||
|
exit "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
function vecho {
|
||||||
|
if [ "${VERBOSE:-}" == "yes" ]; then
|
||||||
|
echo >&7 "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_cmd {
|
||||||
|
if [ -n "$CMD" ]; then
|
||||||
|
echo "ERROR: more than one command given"
|
||||||
|
echo
|
||||||
|
usage 2
|
||||||
|
fi
|
||||||
|
CMD=$1
|
||||||
|
}
|
||||||
|
|
||||||
|
unset VERBOSE
|
||||||
|
unset RECURSIVE_UNLINK
|
||||||
|
|
||||||
|
while getopts :cf:-:htxvC: opt; do
|
||||||
|
case $opt in
|
||||||
|
c)
|
||||||
|
set_cmd "create"
|
||||||
|
;;
|
||||||
|
f)
|
||||||
|
ARCHIVE=$OPTARG
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
usage 0
|
||||||
|
;;
|
||||||
|
t)
|
||||||
|
set_cmd "list"
|
||||||
|
;;
|
||||||
|
x)
|
||||||
|
set_cmd "extract"
|
||||||
|
;;
|
||||||
|
v)
|
||||||
|
VERBOSE=yes
|
||||||
|
exec 7>&1
|
||||||
|
;;
|
||||||
|
C)
|
||||||
|
CDIR=$OPTARG
|
||||||
|
;;
|
||||||
|
-)
|
||||||
|
case $OPTARG in
|
||||||
|
recursive-unlink)
|
||||||
|
RECURSIVE_UNLINK="yes"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "Error: invalid option -$OPTARG"
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo >&2 "ERROR: invalid option -$OPTARG"
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove processed options from arguments
|
||||||
|
shift $(( OPTIND - 1 ));
|
||||||
|
|
||||||
|
if [ "${CMD:-}" == "" ]; then
|
||||||
|
echo >&2 "ERROR: no command given"
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
elif [ "${ARCHIVE:-}" == "" ]; then
|
||||||
|
echo >&2 "ERROR: no archive name given"
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function list {
|
||||||
|
local path=""
|
||||||
|
local size=0
|
||||||
|
local line_no=0
|
||||||
|
local ttar_file=$1
|
||||||
|
if [ -n "${2:-}" ]; then
|
||||||
|
echo >&2 "ERROR: too many arguments."
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
if [ ! -e "$ttar_file" ]; then
|
||||||
|
echo >&2 "ERROR: file not found ($ttar_file)"
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
while read -r line; do
|
||||||
|
line_no=$(( line_no + 1 ))
|
||||||
|
if [ $size -gt 0 ]; then
|
||||||
|
size=$(( size - 1 ))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ $line =~ ^Path:\ (.*)$ ]]; then
|
||||||
|
path=${BASH_REMATCH[1]}
|
||||||
|
elif [[ $line =~ ^Lines:\ (.*)$ ]]; then
|
||||||
|
size=${BASH_REMATCH[1]}
|
||||||
|
echo "$path"
|
||||||
|
elif [[ $line =~ ^Directory:\ (.*)$ ]]; then
|
||||||
|
path=${BASH_REMATCH[1]}
|
||||||
|
echo "$path/"
|
||||||
|
elif [[ $line =~ ^SymlinkTo:\ (.*)$ ]]; then
|
||||||
|
echo "$path -> ${BASH_REMATCH[1]}"
|
||||||
|
fi
|
||||||
|
done < "$ttar_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract {
|
||||||
|
local path=""
|
||||||
|
local size=0
|
||||||
|
local line_no=0
|
||||||
|
local ttar_file=$1
|
||||||
|
if [ -n "${2:-}" ]; then
|
||||||
|
echo >&2 "ERROR: too many arguments."
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
if [ ! -e "$ttar_file" ]; then
|
||||||
|
echo >&2 "ERROR: file not found ($ttar_file)"
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line_no=$(( line_no + 1 ))
|
||||||
|
local eof_without_newline
|
||||||
|
if [ "$size" -gt 0 ]; then
|
||||||
|
if [[ "$line" =~ [^\\]EOF ]]; then
|
||||||
|
# An EOF not preceded by a backslash indicates that the line
|
||||||
|
# does not end with a newline
|
||||||
|
eof_without_newline=1
|
||||||
|
else
|
||||||
|
eof_without_newline=0
|
||||||
|
fi
|
||||||
|
# Replace NULLBYTE with null byte if at beginning of line
|
||||||
|
# Replace NULLBYTE with null byte unless preceded by backslash
|
||||||
|
# Remove one backslash in front of NULLBYTE (if any)
|
||||||
|
# Remove EOF unless preceded by backslash
|
||||||
|
# Remove one backslash in front of EOF
|
||||||
|
if [ $USE_PYTHON -eq 1 ]; then
|
||||||
|
echo -n "$line" | python -c "$PYTHON_EXTRACT_FILTER" >> "$path"
|
||||||
|
else
|
||||||
|
# The repeated pattern makes up for sed's lack of negative
|
||||||
|
# lookbehind assertions (for consecutive null bytes).
|
||||||
|
echo -n "$line" | \
|
||||||
|
sed -e 's/^NULLBYTE/\x0/g;
|
||||||
|
s/\([^\\]\)NULLBYTE/\1\x0/g;
|
||||||
|
s/\([^\\]\)NULLBYTE/\1\x0/g;
|
||||||
|
s/\\NULLBYTE/NULLBYTE/g;
|
||||||
|
s/\([^\\]\)EOF/\1/g;
|
||||||
|
s/\\EOF/EOF/g;
|
||||||
|
' >> "$path"
|
||||||
|
fi
|
||||||
|
if [[ "$eof_without_newline" -eq 0 ]]; then
|
||||||
|
echo >> "$path"
|
||||||
|
fi
|
||||||
|
size=$(( size - 1 ))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [[ $line =~ ^Path:\ (.*)$ ]]; then
|
||||||
|
path=${BASH_REMATCH[1]}
|
||||||
|
if [ -L "$path" ]; then
|
||||||
|
rm "$path"
|
||||||
|
elif [ -d "$path" ]; then
|
||||||
|
if [ "${RECURSIVE_UNLINK:-}" == "yes" ]; then
|
||||||
|
rm -r "$path"
|
||||||
|
else
|
||||||
|
# Safe because symlinks to directories are dealt with above
|
||||||
|
rmdir "$path"
|
||||||
|
fi
|
||||||
|
elif [ -e "$path" ]; then
|
||||||
|
rm "$path"
|
||||||
|
fi
|
||||||
|
elif [[ $line =~ ^Lines:\ (.*)$ ]]; then
|
||||||
|
size=${BASH_REMATCH[1]}
|
||||||
|
# Create file even if it is zero-length.
|
||||||
|
touch "$path"
|
||||||
|
vecho " $path"
|
||||||
|
elif [[ $line =~ ^Mode:\ (.*)$ ]]; then
|
||||||
|
mode=${BASH_REMATCH[1]}
|
||||||
|
chmod "$mode" "$path"
|
||||||
|
vecho "$mode"
|
||||||
|
elif [[ $line =~ ^Directory:\ (.*)$ ]]; then
|
||||||
|
path=${BASH_REMATCH[1]}
|
||||||
|
mkdir -p "$path"
|
||||||
|
vecho " $path/"
|
||||||
|
elif [[ $line =~ ^SymlinkTo:\ (.*)$ ]]; then
|
||||||
|
ln -s "${BASH_REMATCH[1]}" "$path"
|
||||||
|
vecho " $path -> ${BASH_REMATCH[1]}"
|
||||||
|
elif [[ $line =~ ^# ]]; then
|
||||||
|
# Ignore comments between files
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
echo >&2 "ERROR: Unknown keyword on line $line_no: $line"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done < "$ttar_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
function div {
|
||||||
|
echo "# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" \
|
||||||
|
"- - - - - -"
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_mode {
|
||||||
|
local mfile=$1
|
||||||
|
if [ -z "${STAT_OPTION:-}" ]; then
|
||||||
|
if stat -c '%a' "$mfile" >/dev/null 2>&1; then
|
||||||
|
# GNU stat
|
||||||
|
STAT_OPTION='-c'
|
||||||
|
STAT_FORMAT='%a'
|
||||||
|
else
|
||||||
|
# BSD stat
|
||||||
|
STAT_OPTION='-f'
|
||||||
|
# Octal output, user/group/other (omit file type, sticky bit)
|
||||||
|
STAT_FORMAT='%OLp'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
stat "${STAT_OPTION}" "${STAT_FORMAT}" "$mfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
function _create {
|
||||||
|
shopt -s nullglob
|
||||||
|
local mode
|
||||||
|
local eof_without_newline
|
||||||
|
while (( "$#" )); do
|
||||||
|
file=$1
|
||||||
|
if [ -L "$file" ]; then
|
||||||
|
echo "Path: $file"
|
||||||
|
symlinkTo=$(readlink "$file")
|
||||||
|
echo "SymlinkTo: $symlinkTo"
|
||||||
|
vecho " $file -> $symlinkTo"
|
||||||
|
div
|
||||||
|
elif [ -d "$file" ]; then
|
||||||
|
# Strip trailing slash (if there is one)
|
||||||
|
file=${file%/}
|
||||||
|
echo "Directory: $file"
|
||||||
|
mode=$(get_mode "$file")
|
||||||
|
echo "Mode: $mode"
|
||||||
|
vecho "$mode $file/"
|
||||||
|
div
|
||||||
|
# Find all files and dirs, including hidden/dot files
|
||||||
|
for x in "$file/"{*,.[^.]*}; do
|
||||||
|
_create "$x"
|
||||||
|
done
|
||||||
|
elif [ -f "$file" ]; then
|
||||||
|
echo "Path: $file"
|
||||||
|
lines=$(wc -l "$file"|awk '{print $1}')
|
||||||
|
eof_without_newline=0
|
||||||
|
if [[ "$(wc -c "$file"|awk '{print $1}')" -gt 0 ]] && \
|
||||||
|
[[ "$(tail -c 1 "$file" | wc -l)" -eq 0 ]]; then
|
||||||
|
eof_without_newline=1
|
||||||
|
lines=$((lines+1))
|
||||||
|
fi
|
||||||
|
echo "Lines: $lines"
|
||||||
|
# Add backslash in front of EOF
|
||||||
|
# Add backslash in front of NULLBYTE
|
||||||
|
# Replace null byte with NULLBYTE
|
||||||
|
if [ $USE_PYTHON -eq 1 ]; then
|
||||||
|
< "$file" python -c "$PYTHON_CREATE_FILTER"
|
||||||
|
else
|
||||||
|
< "$file" \
|
||||||
|
sed 's/EOF/\\EOF/g;
|
||||||
|
s/NULLBYTE/\\NULLBYTE/g;
|
||||||
|
s/\x0/NULLBYTE/g;
|
||||||
|
'
|
||||||
|
fi
|
||||||
|
if [[ "$eof_without_newline" -eq 1 ]]; then
|
||||||
|
# Finish line with EOF to indicate that the original line did
|
||||||
|
# not end with a linefeed
|
||||||
|
echo "EOF"
|
||||||
|
fi
|
||||||
|
mode=$(get_mode "$file")
|
||||||
|
echo "Mode: $mode"
|
||||||
|
vecho "$mode $file"
|
||||||
|
div
|
||||||
|
else
|
||||||
|
echo >&2 "ERROR: file not found ($file in $(pwd))"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function create {
|
||||||
|
ttar_file=$1
|
||||||
|
shift
|
||||||
|
if [ -z "${1:-}" ]; then
|
||||||
|
echo >&2 "ERROR: missing arguments."
|
||||||
|
echo
|
||||||
|
usage 1
|
||||||
|
fi
|
||||||
|
if [ -e "$ttar_file" ]; then
|
||||||
|
rm "$ttar_file"
|
||||||
|
fi
|
||||||
|
exec > "$ttar_file"
|
||||||
|
echo "# Archive created by ttar $ARG_STRING"
|
||||||
|
_create "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_environment
|
||||||
|
|
||||||
|
if [ -n "${CDIR:-}" ]; then
|
||||||
|
if [[ "$ARCHIVE" != /* ]]; then
|
||||||
|
# Relative path: preserve the archive's location before changing
|
||||||
|
# directory
|
||||||
|
ARCHIVE="$(pwd)/$ARCHIVE"
|
||||||
|
fi
|
||||||
|
cd "$CDIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"$CMD" "$ARCHIVE" "$@"
|
187
vendor/github.com/prometheus/procfs/xfrm.go
generated
vendored
Normal file
187
vendor/github.com/prometheus/procfs/xfrm.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// Copyright 2017 Prometheus Team
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package procfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XfrmStat models the contents of /proc/net/xfrm_stat.
|
||||||
|
type XfrmStat struct {
|
||||||
|
// All errors which are not matched by other
|
||||||
|
XfrmInError int
|
||||||
|
// No buffer is left
|
||||||
|
XfrmInBufferError int
|
||||||
|
// Header Error
|
||||||
|
XfrmInHdrError int
|
||||||
|
// No state found
|
||||||
|
// i.e. either inbound SPI, address, or IPSEC protocol at SA is wrong
|
||||||
|
XfrmInNoStates int
|
||||||
|
// Transformation protocol specific error
|
||||||
|
// e.g. SA Key is wrong
|
||||||
|
XfrmInStateProtoError int
|
||||||
|
// Transformation mode specific error
|
||||||
|
XfrmInStateModeError int
|
||||||
|
// Sequence error
|
||||||
|
// e.g. sequence number is out of window
|
||||||
|
XfrmInStateSeqError int
|
||||||
|
// State is expired
|
||||||
|
XfrmInStateExpired int
|
||||||
|
// State has mismatch option
|
||||||
|
// e.g. UDP encapsulation type is mismatched
|
||||||
|
XfrmInStateMismatch int
|
||||||
|
// State is invalid
|
||||||
|
XfrmInStateInvalid int
|
||||||
|
// No matching template for states
|
||||||
|
// e.g. Inbound SAs are correct but SP rule is wrong
|
||||||
|
XfrmInTmplMismatch int
|
||||||
|
// No policy is found for states
|
||||||
|
// e.g. Inbound SAs are correct but no SP is found
|
||||||
|
XfrmInNoPols int
|
||||||
|
// Policy discards
|
||||||
|
XfrmInPolBlock int
|
||||||
|
// Policy error
|
||||||
|
XfrmInPolError int
|
||||||
|
// All errors which are not matched by others
|
||||||
|
XfrmOutError int
|
||||||
|
// Bundle generation error
|
||||||
|
XfrmOutBundleGenError int
|
||||||
|
// Bundle check error
|
||||||
|
XfrmOutBundleCheckError int
|
||||||
|
// No state was found
|
||||||
|
XfrmOutNoStates int
|
||||||
|
// Transformation protocol specific error
|
||||||
|
XfrmOutStateProtoError int
|
||||||
|
// Transportation mode specific error
|
||||||
|
XfrmOutStateModeError int
|
||||||
|
// Sequence error
|
||||||
|
// i.e sequence number overflow
|
||||||
|
XfrmOutStateSeqError int
|
||||||
|
// State is expired
|
||||||
|
XfrmOutStateExpired int
|
||||||
|
// Policy discads
|
||||||
|
XfrmOutPolBlock int
|
||||||
|
// Policy is dead
|
||||||
|
XfrmOutPolDead int
|
||||||
|
// Policy Error
|
||||||
|
XfrmOutPolError int
|
||||||
|
XfrmFwdHdrError int
|
||||||
|
XfrmOutStateInvalid int
|
||||||
|
XfrmAcquireError int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXfrmStat reads the xfrm_stat statistics.
|
||||||
|
func NewXfrmStat() (XfrmStat, error) {
|
||||||
|
fs, err := NewFS(DefaultMountPoint)
|
||||||
|
if err != nil {
|
||||||
|
return XfrmStat{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs.NewXfrmStat()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXfrmStat reads the xfrm_stat statistics from the 'proc' filesystem.
|
||||||
|
func (fs FS) NewXfrmStat() (XfrmStat, error) {
|
||||||
|
file, err := os.Open(fs.proc.Path("net/xfrm_stat"))
|
||||||
|
if err != nil {
|
||||||
|
return XfrmStat{}, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
x = XfrmStat{}
|
||||||
|
s = bufio.NewScanner(file)
|
||||||
|
)
|
||||||
|
|
||||||
|
for s.Scan() {
|
||||||
|
fields := strings.Fields(s.Text())
|
||||||
|
|
||||||
|
if len(fields) != 2 {
|
||||||
|
return XfrmStat{}, fmt.Errorf(
|
||||||
|
"couldn't parse %s line %s", file.Name(), s.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := fields[0]
|
||||||
|
value, err := strconv.Atoi(fields[1])
|
||||||
|
if err != nil {
|
||||||
|
return XfrmStat{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case "XfrmInError":
|
||||||
|
x.XfrmInError = value
|
||||||
|
case "XfrmInBufferError":
|
||||||
|
x.XfrmInBufferError = value
|
||||||
|
case "XfrmInHdrError":
|
||||||
|
x.XfrmInHdrError = value
|
||||||
|
case "XfrmInNoStates":
|
||||||
|
x.XfrmInNoStates = value
|
||||||
|
case "XfrmInStateProtoError":
|
||||||
|
x.XfrmInStateProtoError = value
|
||||||
|
case "XfrmInStateModeError":
|
||||||
|
x.XfrmInStateModeError = value
|
||||||
|
case "XfrmInStateSeqError":
|
||||||
|
x.XfrmInStateSeqError = value
|
||||||
|
case "XfrmInStateExpired":
|
||||||
|
x.XfrmInStateExpired = value
|
||||||
|
case "XfrmInStateInvalid":
|
||||||
|
x.XfrmInStateInvalid = value
|
||||||
|
case "XfrmInTmplMismatch":
|
||||||
|
x.XfrmInTmplMismatch = value
|
||||||
|
case "XfrmInNoPols":
|
||||||
|
x.XfrmInNoPols = value
|
||||||
|
case "XfrmInPolBlock":
|
||||||
|
x.XfrmInPolBlock = value
|
||||||
|
case "XfrmInPolError":
|
||||||
|
x.XfrmInPolError = value
|
||||||
|
case "XfrmOutError":
|
||||||
|
x.XfrmOutError = value
|
||||||
|
case "XfrmInStateMismatch":
|
||||||
|
x.XfrmInStateMismatch = value
|
||||||
|
case "XfrmOutBundleGenError":
|
||||||
|
x.XfrmOutBundleGenError = value
|
||||||
|
case "XfrmOutBundleCheckError":
|
||||||
|
x.XfrmOutBundleCheckError = value
|
||||||
|
case "XfrmOutNoStates":
|
||||||
|
x.XfrmOutNoStates = value
|
||||||
|
case "XfrmOutStateProtoError":
|
||||||
|
x.XfrmOutStateProtoError = value
|
||||||
|
case "XfrmOutStateModeError":
|
||||||
|
x.XfrmOutStateModeError = value
|
||||||
|
case "XfrmOutStateSeqError":
|
||||||
|
x.XfrmOutStateSeqError = value
|
||||||
|
case "XfrmOutStateExpired":
|
||||||
|
x.XfrmOutStateExpired = value
|
||||||
|
case "XfrmOutPolBlock":
|
||||||
|
x.XfrmOutPolBlock = value
|
||||||
|
case "XfrmOutPolDead":
|
||||||
|
x.XfrmOutPolDead = value
|
||||||
|
case "XfrmOutPolError":
|
||||||
|
x.XfrmOutPolError = value
|
||||||
|
case "XfrmFwdHdrError":
|
||||||
|
x.XfrmFwdHdrError = value
|
||||||
|
case "XfrmOutStateInvalid":
|
||||||
|
x.XfrmOutStateInvalid = value
|
||||||
|
case "XfrmAcquireError":
|
||||||
|
x.XfrmAcquireError = value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return x, s.Err()
|
||||||
|
}
|
359
vendor/github.com/prometheus/procfs/xfs/parse.go
generated
vendored
359
vendor/github.com/prometheus/procfs/xfs/parse.go
generated
vendored
|
@ -1,359 +0,0 @@
|
||||||
// Copyright 2017 The Prometheus Authors
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package xfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ParseStats parses a Stats from an input io.Reader, using the format
|
|
||||||
// found in /proc/fs/xfs/stat.
|
|
||||||
func ParseStats(r io.Reader) (*Stats, error) {
|
|
||||||
const (
|
|
||||||
// Fields parsed into stats structures.
|
|
||||||
fieldExtentAlloc = "extent_alloc"
|
|
||||||
fieldAbt = "abt"
|
|
||||||
fieldBlkMap = "blk_map"
|
|
||||||
fieldBmbt = "bmbt"
|
|
||||||
fieldDir = "dir"
|
|
||||||
fieldTrans = "trans"
|
|
||||||
fieldIg = "ig"
|
|
||||||
fieldLog = "log"
|
|
||||||
fieldRw = "rw"
|
|
||||||
fieldAttr = "attr"
|
|
||||||
fieldIcluster = "icluster"
|
|
||||||
fieldVnodes = "vnodes"
|
|
||||||
fieldBuf = "buf"
|
|
||||||
fieldXpc = "xpc"
|
|
||||||
|
|
||||||
// Unimplemented at this time due to lack of documentation.
|
|
||||||
fieldPushAil = "push_ail"
|
|
||||||
fieldXstrat = "xstrat"
|
|
||||||
fieldAbtb2 = "abtb2"
|
|
||||||
fieldAbtc2 = "abtc2"
|
|
||||||
fieldBmbt2 = "bmbt2"
|
|
||||||
fieldIbt2 = "ibt2"
|
|
||||||
fieldFibt2 = "fibt2"
|
|
||||||
fieldQm = "qm"
|
|
||||||
fieldDebug = "debug"
|
|
||||||
)
|
|
||||||
|
|
||||||
var xfss Stats
|
|
||||||
|
|
||||||
s := bufio.NewScanner(r)
|
|
||||||
for s.Scan() {
|
|
||||||
// Expect at least a string label and a single integer value, ex:
|
|
||||||
// - abt 0
|
|
||||||
// - rw 1 2
|
|
||||||
ss := strings.Fields(string(s.Bytes()))
|
|
||||||
if len(ss) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
label := ss[0]
|
|
||||||
|
|
||||||
// Extended precision counters are uint64 values.
|
|
||||||
if label == fieldXpc {
|
|
||||||
us, err := parseUint64s(ss[1:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
xfss.ExtendedPrecision, err = extendedPrecisionStats(us)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// All other counters are uint32 values.
|
|
||||||
us, err := parseUint32s(ss[1:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch label {
|
|
||||||
case fieldExtentAlloc:
|
|
||||||
xfss.ExtentAllocation, err = extentAllocationStats(us)
|
|
||||||
case fieldAbt:
|
|
||||||
xfss.AllocationBTree, err = btreeStats(us)
|
|
||||||
case fieldBlkMap:
|
|
||||||
xfss.BlockMapping, err = blockMappingStats(us)
|
|
||||||
case fieldBmbt:
|
|
||||||
xfss.BlockMapBTree, err = btreeStats(us)
|
|
||||||
case fieldDir:
|
|
||||||
xfss.DirectoryOperation, err = directoryOperationStats(us)
|
|
||||||
case fieldTrans:
|
|
||||||
xfss.Transaction, err = transactionStats(us)
|
|
||||||
case fieldIg:
|
|
||||||
xfss.InodeOperation, err = inodeOperationStats(us)
|
|
||||||
case fieldLog:
|
|
||||||
xfss.LogOperation, err = logOperationStats(us)
|
|
||||||
case fieldRw:
|
|
||||||
xfss.ReadWrite, err = readWriteStats(us)
|
|
||||||
case fieldAttr:
|
|
||||||
xfss.AttributeOperation, err = attributeOperationStats(us)
|
|
||||||
case fieldIcluster:
|
|
||||||
xfss.InodeClustering, err = inodeClusteringStats(us)
|
|
||||||
case fieldVnodes:
|
|
||||||
xfss.Vnode, err = vnodeStats(us)
|
|
||||||
case fieldBuf:
|
|
||||||
xfss.Buffer, err = bufferStats(us)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &xfss, s.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// extentAllocationStats builds an ExtentAllocationStats from a slice of uint32s.
|
|
||||||
func extentAllocationStats(us []uint32) (ExtentAllocationStats, error) {
|
|
||||||
if l := len(us); l != 4 {
|
|
||||||
return ExtentAllocationStats{}, fmt.Errorf("incorrect number of values for XFS extent allocation stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExtentAllocationStats{
|
|
||||||
ExtentsAllocated: us[0],
|
|
||||||
BlocksAllocated: us[1],
|
|
||||||
ExtentsFreed: us[2],
|
|
||||||
BlocksFreed: us[3],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// btreeStats builds a BTreeStats from a slice of uint32s.
|
|
||||||
func btreeStats(us []uint32) (BTreeStats, error) {
|
|
||||||
if l := len(us); l != 4 {
|
|
||||||
return BTreeStats{}, fmt.Errorf("incorrect number of values for XFS btree stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return BTreeStats{
|
|
||||||
Lookups: us[0],
|
|
||||||
Compares: us[1],
|
|
||||||
RecordsInserted: us[2],
|
|
||||||
RecordsDeleted: us[3],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockMappingStat builds a BlockMappingStats from a slice of uint32s.
|
|
||||||
func blockMappingStats(us []uint32) (BlockMappingStats, error) {
|
|
||||||
if l := len(us); l != 7 {
|
|
||||||
return BlockMappingStats{}, fmt.Errorf("incorrect number of values for XFS block mapping stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return BlockMappingStats{
|
|
||||||
Reads: us[0],
|
|
||||||
Writes: us[1],
|
|
||||||
Unmaps: us[2],
|
|
||||||
ExtentListInsertions: us[3],
|
|
||||||
ExtentListDeletions: us[4],
|
|
||||||
ExtentListLookups: us[5],
|
|
||||||
ExtentListCompares: us[6],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectoryOperationStats builds a DirectoryOperationStats from a slice of uint32s.
|
|
||||||
func directoryOperationStats(us []uint32) (DirectoryOperationStats, error) {
|
|
||||||
if l := len(us); l != 4 {
|
|
||||||
return DirectoryOperationStats{}, fmt.Errorf("incorrect number of values for XFS directory operation stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return DirectoryOperationStats{
|
|
||||||
Lookups: us[0],
|
|
||||||
Creates: us[1],
|
|
||||||
Removes: us[2],
|
|
||||||
Getdents: us[3],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionStats builds a TransactionStats from a slice of uint32s.
|
|
||||||
func transactionStats(us []uint32) (TransactionStats, error) {
|
|
||||||
if l := len(us); l != 3 {
|
|
||||||
return TransactionStats{}, fmt.Errorf("incorrect number of values for XFS transaction stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return TransactionStats{
|
|
||||||
Sync: us[0],
|
|
||||||
Async: us[1],
|
|
||||||
Empty: us[2],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InodeOperationStats builds an InodeOperationStats from a slice of uint32s.
|
|
||||||
func inodeOperationStats(us []uint32) (InodeOperationStats, error) {
|
|
||||||
if l := len(us); l != 7 {
|
|
||||||
return InodeOperationStats{}, fmt.Errorf("incorrect number of values for XFS inode operation stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return InodeOperationStats{
|
|
||||||
Attempts: us[0],
|
|
||||||
Found: us[1],
|
|
||||||
Recycle: us[2],
|
|
||||||
Missed: us[3],
|
|
||||||
Duplicate: us[4],
|
|
||||||
Reclaims: us[5],
|
|
||||||
AttributeChange: us[6],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogOperationStats builds a LogOperationStats from a slice of uint32s.
|
|
||||||
func logOperationStats(us []uint32) (LogOperationStats, error) {
|
|
||||||
if l := len(us); l != 5 {
|
|
||||||
return LogOperationStats{}, fmt.Errorf("incorrect number of values for XFS log operation stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return LogOperationStats{
|
|
||||||
Writes: us[0],
|
|
||||||
Blocks: us[1],
|
|
||||||
NoInternalBuffers: us[2],
|
|
||||||
Force: us[3],
|
|
||||||
ForceSleep: us[4],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadWriteStats builds a ReadWriteStats from a slice of uint32s.
|
|
||||||
func readWriteStats(us []uint32) (ReadWriteStats, error) {
|
|
||||||
if l := len(us); l != 2 {
|
|
||||||
return ReadWriteStats{}, fmt.Errorf("incorrect number of values for XFS read write stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReadWriteStats{
|
|
||||||
Read: us[0],
|
|
||||||
Write: us[1],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttributeOperationStats builds an AttributeOperationStats from a slice of uint32s.
|
|
||||||
func attributeOperationStats(us []uint32) (AttributeOperationStats, error) {
|
|
||||||
if l := len(us); l != 4 {
|
|
||||||
return AttributeOperationStats{}, fmt.Errorf("incorrect number of values for XFS attribute operation stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return AttributeOperationStats{
|
|
||||||
Get: us[0],
|
|
||||||
Set: us[1],
|
|
||||||
Remove: us[2],
|
|
||||||
List: us[3],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// InodeClusteringStats builds an InodeClusteringStats from a slice of uint32s.
|
|
||||||
func inodeClusteringStats(us []uint32) (InodeClusteringStats, error) {
|
|
||||||
if l := len(us); l != 3 {
|
|
||||||
return InodeClusteringStats{}, fmt.Errorf("incorrect number of values for XFS inode clustering stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return InodeClusteringStats{
|
|
||||||
Iflush: us[0],
|
|
||||||
Flush: us[1],
|
|
||||||
FlushInode: us[2],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VnodeStats builds a VnodeStats from a slice of uint32s.
|
|
||||||
func vnodeStats(us []uint32) (VnodeStats, error) {
|
|
||||||
// The attribute "Free" appears to not be available on older XFS
|
|
||||||
// stats versions. Therefore, 7 or 8 elements may appear in
|
|
||||||
// this slice.
|
|
||||||
l := len(us)
|
|
||||||
if l != 7 && l != 8 {
|
|
||||||
return VnodeStats{}, fmt.Errorf("incorrect number of values for XFS vnode stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := VnodeStats{
|
|
||||||
Active: us[0],
|
|
||||||
Allocate: us[1],
|
|
||||||
Get: us[2],
|
|
||||||
Hold: us[3],
|
|
||||||
Release: us[4],
|
|
||||||
Reclaim: us[5],
|
|
||||||
Remove: us[6],
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip adding free, unless it is present. The zero value will
|
|
||||||
// be used in place of an actual count.
|
|
||||||
if l == 7 {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Free = us[7]
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BufferStats builds a BufferStats from a slice of uint32s.
|
|
||||||
func bufferStats(us []uint32) (BufferStats, error) {
|
|
||||||
if l := len(us); l != 9 {
|
|
||||||
return BufferStats{}, fmt.Errorf("incorrect number of values for XFS buffer stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return BufferStats{
|
|
||||||
Get: us[0],
|
|
||||||
Create: us[1],
|
|
||||||
GetLocked: us[2],
|
|
||||||
GetLockedWaited: us[3],
|
|
||||||
BusyLocked: us[4],
|
|
||||||
MissLocked: us[5],
|
|
||||||
PageRetries: us[6],
|
|
||||||
PageFound: us[7],
|
|
||||||
GetRead: us[8],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendedPrecisionStats builds an ExtendedPrecisionStats from a slice of uint32s.
|
|
||||||
func extendedPrecisionStats(us []uint64) (ExtendedPrecisionStats, error) {
|
|
||||||
if l := len(us); l != 3 {
|
|
||||||
return ExtendedPrecisionStats{}, fmt.Errorf("incorrect number of values for XFS extended precision stats: %d", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExtendedPrecisionStats{
|
|
||||||
FlushBytes: us[0],
|
|
||||||
WriteBytes: us[1],
|
|
||||||
ReadBytes: us[2],
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseUint32s parses a slice of strings into a slice of uint32s.
|
|
||||||
func parseUint32s(ss []string) ([]uint32, error) {
|
|
||||||
us := make([]uint32, 0, len(ss))
|
|
||||||
for _, s := range ss {
|
|
||||||
u, err := strconv.ParseUint(s, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
us = append(us, uint32(u))
|
|
||||||
}
|
|
||||||
|
|
||||||
return us, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseUint64s parses a slice of strings into a slice of uint64s.
|
|
||||||
func parseUint64s(ss []string) ([]uint64, error) {
|
|
||||||
us := make([]uint64, 0, len(ss))
|
|
||||||
for _, s := range ss {
|
|
||||||
u, err := strconv.ParseUint(s, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
us = append(us, u)
|
|
||||||
}
|
|
||||||
|
|
||||||
return us, nil
|
|
||||||
}
|
|
163
vendor/github.com/prometheus/procfs/xfs/xfs.go
generated
vendored
163
vendor/github.com/prometheus/procfs/xfs/xfs.go
generated
vendored
|
@ -1,163 +0,0 @@
|
||||||
// Copyright 2017 The Prometheus Authors
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
// Package xfs provides access to statistics exposed by the XFS filesystem.
|
|
||||||
package xfs
|
|
||||||
|
|
||||||
// Stats contains XFS filesystem runtime statistics, parsed from
|
|
||||||
// /proc/fs/xfs/stat.
|
|
||||||
//
|
|
||||||
// The names and meanings of each statistic were taken from
|
|
||||||
// http://xfs.org/index.php/Runtime_Stats and xfs_stats.h in the Linux
|
|
||||||
// kernel source. Most counters are uint32s (same data types used in
|
|
||||||
// xfs_stats.h), but some of the "extended precision stats" are uint64s.
|
|
||||||
type Stats struct {
|
|
||||||
// The name of the filesystem used to source these statistics.
|
|
||||||
// If empty, this indicates aggregated statistics for all XFS
|
|
||||||
// filesystems on the host.
|
|
||||||
Name string
|
|
||||||
|
|
||||||
ExtentAllocation ExtentAllocationStats
|
|
||||||
AllocationBTree BTreeStats
|
|
||||||
BlockMapping BlockMappingStats
|
|
||||||
BlockMapBTree BTreeStats
|
|
||||||
DirectoryOperation DirectoryOperationStats
|
|
||||||
Transaction TransactionStats
|
|
||||||
InodeOperation InodeOperationStats
|
|
||||||
LogOperation LogOperationStats
|
|
||||||
ReadWrite ReadWriteStats
|
|
||||||
AttributeOperation AttributeOperationStats
|
|
||||||
InodeClustering InodeClusteringStats
|
|
||||||
Vnode VnodeStats
|
|
||||||
Buffer BufferStats
|
|
||||||
ExtendedPrecision ExtendedPrecisionStats
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtentAllocationStats contains statistics regarding XFS extent allocations.
|
|
||||||
type ExtentAllocationStats struct {
|
|
||||||
ExtentsAllocated uint32
|
|
||||||
BlocksAllocated uint32
|
|
||||||
ExtentsFreed uint32
|
|
||||||
BlocksFreed uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// BTreeStats contains statistics regarding an XFS internal B-tree.
|
|
||||||
type BTreeStats struct {
|
|
||||||
Lookups uint32
|
|
||||||
Compares uint32
|
|
||||||
RecordsInserted uint32
|
|
||||||
RecordsDeleted uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockMappingStats contains statistics regarding XFS block maps.
|
|
||||||
type BlockMappingStats struct {
|
|
||||||
Reads uint32
|
|
||||||
Writes uint32
|
|
||||||
Unmaps uint32
|
|
||||||
ExtentListInsertions uint32
|
|
||||||
ExtentListDeletions uint32
|
|
||||||
ExtentListLookups uint32
|
|
||||||
ExtentListCompares uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// DirectoryOperationStats contains statistics regarding XFS directory entries.
|
|
||||||
type DirectoryOperationStats struct {
|
|
||||||
Lookups uint32
|
|
||||||
Creates uint32
|
|
||||||
Removes uint32
|
|
||||||
Getdents uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionStats contains statistics regarding XFS metadata transactions.
|
|
||||||
type TransactionStats struct {
|
|
||||||
Sync uint32
|
|
||||||
Async uint32
|
|
||||||
Empty uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// InodeOperationStats contains statistics regarding XFS inode operations.
|
|
||||||
type InodeOperationStats struct {
|
|
||||||
Attempts uint32
|
|
||||||
Found uint32
|
|
||||||
Recycle uint32
|
|
||||||
Missed uint32
|
|
||||||
Duplicate uint32
|
|
||||||
Reclaims uint32
|
|
||||||
AttributeChange uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogOperationStats contains statistics regarding the XFS log buffer.
|
|
||||||
type LogOperationStats struct {
|
|
||||||
Writes uint32
|
|
||||||
Blocks uint32
|
|
||||||
NoInternalBuffers uint32
|
|
||||||
Force uint32
|
|
||||||
ForceSleep uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadWriteStats contains statistics regarding the number of read and write
|
|
||||||
// system calls for XFS filesystems.
|
|
||||||
type ReadWriteStats struct {
|
|
||||||
Read uint32
|
|
||||||
Write uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// AttributeOperationStats contains statistics regarding manipulation of
|
|
||||||
// XFS extended file attributes.
|
|
||||||
type AttributeOperationStats struct {
|
|
||||||
Get uint32
|
|
||||||
Set uint32
|
|
||||||
Remove uint32
|
|
||||||
List uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// InodeClusteringStats contains statistics regarding XFS inode clustering
|
|
||||||
// operations.
|
|
||||||
type InodeClusteringStats struct {
|
|
||||||
Iflush uint32
|
|
||||||
Flush uint32
|
|
||||||
FlushInode uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// VnodeStats contains statistics regarding XFS vnode operations.
|
|
||||||
type VnodeStats struct {
|
|
||||||
Active uint32
|
|
||||||
Allocate uint32
|
|
||||||
Get uint32
|
|
||||||
Hold uint32
|
|
||||||
Release uint32
|
|
||||||
Reclaim uint32
|
|
||||||
Remove uint32
|
|
||||||
Free uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// BufferStats contains statistics regarding XFS read/write I/O buffers.
|
|
||||||
type BufferStats struct {
|
|
||||||
Get uint32
|
|
||||||
Create uint32
|
|
||||||
GetLocked uint32
|
|
||||||
GetLockedWaited uint32
|
|
||||||
BusyLocked uint32
|
|
||||||
MissLocked uint32
|
|
||||||
PageRetries uint32
|
|
||||||
PageFound uint32
|
|
||||||
GetRead uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtendedPrecisionStats contains high precision counters used to track the
|
|
||||||
// total number of bytes read, written, or flushed, during XFS operations.
|
|
||||||
type ExtendedPrecisionStats struct {
|
|
||||||
FlushBytes uint64
|
|
||||||
WriteBytes uint64
|
|
||||||
ReadBytes uint64
|
|
||||||
}
|
|
50
vendor/vendor.json
vendored
50
vendor/vendor.json
vendored
|
@ -528,10 +528,16 @@
|
||||||
"revisionTime": "2016-01-29T19:31:06Z"
|
"revisionTime": "2016-01-29T19:31:06Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "X5E2qzbDix710dxoN9Qu0qeHGqU=",
|
"checksumSHA1": "Se6I1e5ROiv0SrS9WKsiq5yur9Q=",
|
||||||
"path": "github.com/prometheus/client_golang/prometheus",
|
"path": "github.com/prometheus/client_golang/prometheus",
|
||||||
"revision": "7d9484283ebefa862b5b7727d4344cfdf9a0d138",
|
"revision": "f0a455664ecb0634bfb64d58b0c8226c2dab0804",
|
||||||
"revisionTime": "2017-04-25T21:35:58Z"
|
"revisionTime": "2019-05-28T13:29:58Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "UBqhkyjCz47+S19MVTigxJ2VjVQ=",
|
||||||
|
"path": "github.com/prometheus/client_golang/prometheus/internal",
|
||||||
|
"revision": "f0a455664ecb0634bfb64d58b0c8226c2dab0804",
|
||||||
|
"revisionTime": "2019-05-28T13:29:58Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "tqpiUpHWFso3wcHDAPXoEhT2CoM=",
|
"checksumSHA1": "tqpiUpHWFso3wcHDAPXoEhT2CoM=",
|
||||||
|
@ -540,40 +546,40 @@
|
||||||
"revisionTime": "2017-04-25T21:35:58Z"
|
"revisionTime": "2017-04-25T21:35:58Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=",
|
"checksumSHA1": "V8xkqgmP66sq2ZW4QO5wi9a4oZE=",
|
||||||
"path": "github.com/prometheus/client_model/go",
|
"path": "github.com/prometheus/client_model/go",
|
||||||
"revision": "6f3806018612930941127f2a7c6c453ba2c527d2",
|
"revision": "fd36f4220a901265f90734c3183c5f0c91daa0b8",
|
||||||
"revisionTime": "2017-02-16T18:52:47Z"
|
"revisionTime": "2019-01-29T23:31:27Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Wtpzndm/+bdwwNU5PCTfb4oUhc8=",
|
"checksumSHA1": "ljxJzXiQ7dNsmuRIUhqqP+qjRWc=",
|
||||||
"path": "github.com/prometheus/common/expfmt",
|
"path": "github.com/prometheus/common/expfmt",
|
||||||
"revision": "13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207",
|
"revision": "17f5ca1748182ddf24fc33a5a7caaaf790a52fcc",
|
||||||
"revisionTime": "2017-04-27T09:54:55Z"
|
"revisionTime": "2019-05-16T16:00:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "GWlM3d2vPYyNATtTFgftS10/A9w=",
|
"checksumSHA1": "1Mhfofk+wGZ94M0+Bd98K8imPD4=",
|
||||||
"path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
|
"path": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
|
||||||
"revision": "13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207",
|
"revision": "17f5ca1748182ddf24fc33a5a7caaaf790a52fcc",
|
||||||
"revisionTime": "2017-04-27T09:54:55Z"
|
"revisionTime": "2019-05-16T16:00:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "0LL9u9tfv1KPBjNEiMDP6q7lpog=",
|
"checksumSHA1": "ccmMs+h9Jo8kE7izqsUkWShD4d0=",
|
||||||
"path": "github.com/prometheus/common/model",
|
"path": "github.com/prometheus/common/model",
|
||||||
"revision": "13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207",
|
"revision": "17f5ca1748182ddf24fc33a5a7caaaf790a52fcc",
|
||||||
"revisionTime": "2017-04-27T09:54:55Z"
|
"revisionTime": "2019-05-16T16:00:40Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "dA8hiwcAbubYRxTnpocR5VZkqAY=",
|
"checksumSHA1": "SO2tz5Y0bxgJjDyQeEpZfJCEfYc=",
|
||||||
"path": "github.com/prometheus/procfs",
|
"path": "github.com/prometheus/procfs",
|
||||||
"revision": "6ac8c5d890d415025dd5aae7595bcb2a6e7e2fad",
|
"revision": "65bdadfa96aecebf4dcf888da995a29eab4fc964",
|
||||||
"revisionTime": "2017-04-24T20:45:52Z"
|
"revisionTime": "2019-05-28T16:49:32Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "xCiFAAwVTrjsfZT1BIJQ3DgeNCY=",
|
"checksumSHA1": "Kmjs49lbjGmlgUPx3pks0tVDed0=",
|
||||||
"path": "github.com/prometheus/procfs/xfs",
|
"path": "github.com/prometheus/procfs/internal/fs",
|
||||||
"revision": "6ac8c5d890d415025dd5aae7595bcb2a6e7e2fad",
|
"revision": "65bdadfa96aecebf4dcf888da995a29eab4fc964",
|
||||||
"revisionTime": "2017-04-24T20:45:52Z"
|
"revisionTime": "2019-05-28T16:49:32Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "3Ie7HG2k47G/gwz8prjymTMLEms=",
|
"checksumSHA1": "3Ie7HG2k47G/gwz8prjymTMLEms=",
|
||||||
|
|
Loading…
Reference in a new issue