Update dependencies
This commit is contained in:
5
vendor/github.com/prometheus/client_golang/NOTICE
generated
vendored
5
vendor/github.com/prometheus/client_golang/NOTICE
generated
vendored
@@ -16,8 +16,3 @@ Go support for Protocol Buffers - Google's data interchange format
|
||||
http://github.com/golang/protobuf/
|
||||
Copyright 2010 The Go Authors
|
||||
See source code for license details.
|
||||
|
||||
Support for streaming Protocol Buffer messages for the Go language (golang).
|
||||
https://github.com/matttproud/golang_protobuf_extensions
|
||||
Copyright 2013 Matt T. Proud
|
||||
Licensed under the Apache License, Version 2.0
|
||||
|
||||
30
vendor/github.com/prometheus/client_golang/prometheus/collectorfunc.go
generated
vendored
Normal file
30
vendor/github.com/prometheus/client_golang/prometheus/collectorfunc.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 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
|
||||
|
||||
// CollectorFunc is a convenient way to implement a Prometheus Collector
|
||||
// without interface boilerplate.
|
||||
// This implementation is based on DescribeByCollect method.
|
||||
// familiarize yourself to it before using.
|
||||
type CollectorFunc func(chan<- Metric)
|
||||
|
||||
// Collect calls the defined CollectorFunc function with the provided Metrics channel
|
||||
func (f CollectorFunc) Collect(ch chan<- Metric) {
|
||||
f(ch)
|
||||
}
|
||||
|
||||
// Describe sends the descriptor information using DescribeByCollect
|
||||
func (f CollectorFunc) Describe(ch chan<- *Desc) {
|
||||
DescribeByCollect(f, ch)
|
||||
}
|
||||
26
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
26
vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
@@ -20,6 +20,7 @@ import (
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// Counter is a Metric that represents a single numerical value that only ever
|
||||
@@ -66,7 +67,7 @@ type CounterVecOpts struct {
|
||||
CounterOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -90,8 +91,12 @@ func NewCounter(opts CounterOpts) Counter {
|
||||
nil,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: time.Now}
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: desc.constLabelPairs, now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -106,10 +111,12 @@ type counter struct {
|
||||
selfCollector
|
||||
desc *Desc
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
labelPairs []*dto.LabelPair
|
||||
exemplar atomic.Value // Containing nil or a *dto.Exemplar.
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func (c *counter) Desc() *Desc {
|
||||
@@ -159,8 +166,7 @@ func (c *counter) Write(out *dto.Metric) error {
|
||||
exemplar = e.(*dto.Exemplar)
|
||||
}
|
||||
val := c.get()
|
||||
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out)
|
||||
return populateMetric(CounterValue, val, c.labelPairs, exemplar, out, c.createdTs)
|
||||
}
|
||||
|
||||
func (c *counter) updateExemplar(v float64, l Labels) {
|
||||
@@ -200,13 +206,17 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
|
||||
opts.VariableLabels,
|
||||
opts.ConstLabels,
|
||||
)
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
return &CounterVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: opts.now}
|
||||
result.init(result) // Init self-collection.
|
||||
result.createdTs = timestamppb.New(opts.now())
|
||||
return result
|
||||
}),
|
||||
}
|
||||
|
||||
38
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
38
vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
@@ -18,12 +18,12 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/internal"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/model"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/internal"
|
||||
)
|
||||
|
||||
// Desc is the descriptor used by every Prometheus Metric. It is essentially
|
||||
@@ -52,7 +52,7 @@ type Desc struct {
|
||||
constLabelPairs []*dto.LabelPair
|
||||
// variableLabels contains names of labels and normalization function for
|
||||
// which the metric maintains variable values.
|
||||
variableLabels ConstrainedLabels
|
||||
variableLabels *compiledLabels
|
||||
// id is a hash of the values of the ConstLabels and fqName. This
|
||||
// must be unique among all registered descriptors and can therefore be
|
||||
// used as an identifier of the descriptor.
|
||||
@@ -93,9 +93,10 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||
d := &Desc{
|
||||
fqName: fqName,
|
||||
help: help,
|
||||
variableLabels: variableLabels.constrainedLabels(),
|
||||
variableLabels: variableLabels.compile(),
|
||||
}
|
||||
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||
//nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme.
|
||||
if !model.NameValidationScheme.IsValidMetricName(fqName) {
|
||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||
return d
|
||||
}
|
||||
@@ -103,7 +104,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||
// their sorted label names) plus the fqName (at position 0).
|
||||
labelValues := make([]string, 1, len(constLabels)+1)
|
||||
labelValues[0] = fqName
|
||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels))
|
||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels.names))
|
||||
labelNameSet := map[string]struct{}{}
|
||||
// First add only the const label names and sort them...
|
||||
for labelName := range constLabels {
|
||||
@@ -128,13 +129,13 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||
// Now add the variable label names, but prefix them with something that
|
||||
// cannot be in a regular label name. That prevents matching the label
|
||||
// dimension with a different mix between preset and variable labels.
|
||||
for _, label := range d.variableLabels {
|
||||
if !checkLabelName(label.Name) {
|
||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label.Name, fqName)
|
||||
for _, label := range d.variableLabels.names {
|
||||
if !checkLabelName(label) {
|
||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label, fqName)
|
||||
return d
|
||||
}
|
||||
labelNames = append(labelNames, "$"+label.Name)
|
||||
labelNameSet[label.Name] = struct{}{}
|
||||
labelNames = append(labelNames, "$"+label)
|
||||
labelNameSet[label] = struct{}{}
|
||||
}
|
||||
if len(labelNames) != len(labelNameSet) {
|
||||
d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName)
|
||||
@@ -189,11 +190,22 @@ func (d *Desc) String() string {
|
||||
fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
|
||||
)
|
||||
}
|
||||
vlStrings := []string{}
|
||||
if d.variableLabels != nil {
|
||||
vlStrings = make([]string, 0, len(d.variableLabels.names))
|
||||
for _, vl := range d.variableLabels.names {
|
||||
if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil {
|
||||
vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl))
|
||||
} else {
|
||||
vlStrings = append(vlStrings, vl)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
|
||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}",
|
||||
d.fqName,
|
||||
d.help,
|
||||
strings.Join(lpStrings, ","),
|
||||
d.variableLabels,
|
||||
strings.Join(vlStrings, ","),
|
||||
)
|
||||
}
|
||||
|
||||
2
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
2
vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go
generated
vendored
@@ -48,7 +48,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) {
|
||||
continue
|
||||
}
|
||||
var v interface{}
|
||||
labels := make([]string, len(desc.variableLabels))
|
||||
labels := make([]string, len(desc.variableLabels.names))
|
||||
if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
|
||||
ch <- NewInvalidMetric(desc, err)
|
||||
continue
|
||||
|
||||
8
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
8
vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
@@ -62,7 +62,7 @@ type GaugeVecOpts struct {
|
||||
GaugeOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -135,7 +135,7 @@ func (g *gauge) Sub(val float64) {
|
||||
|
||||
func (g *gauge) Write(out *dto.Metric) error {
|
||||
val := math.Float64frombits(atomic.LoadUint64(&g.valBits))
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out)
|
||||
return populateMetric(GaugeValue, val, g.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// GaugeVec is a Collector that bundles a set of Gauges that all share the same
|
||||
@@ -166,8 +166,8 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec {
|
||||
)
|
||||
return &GaugeVec{
|
||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
|
||||
if len(lvs) != len(desc.variableLabels.names) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||
}
|
||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
||||
result.init(result) // Init self-collection.
|
||||
|
||||
55
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
55
vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
@@ -22,13 +22,13 @@ import (
|
||||
// goRuntimeMemStats provides the metrics initially provided by runtime.ReadMemStats.
|
||||
// From Go 1.17 those similar (and better) statistics are provided by runtime/metrics, so
|
||||
// while eval closure works on runtime.MemStats, the struct from Go 1.17+ is
|
||||
// populated using runtime/metrics.
|
||||
// populated using runtime/metrics. Those are the defaults we can't alter.
|
||||
func goRuntimeMemStats() memStatsMetrics {
|
||||
return memStatsMetrics{
|
||||
{
|
||||
desc: NewDesc(
|
||||
memstatNamespace("alloc_bytes"),
|
||||
"Number of bytes allocated and still in use.",
|
||||
"Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
|
||||
@@ -36,7 +36,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("alloc_bytes_total"),
|
||||
"Total number of bytes allocated, even if freed.",
|
||||
"Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
|
||||
@@ -44,23 +44,16 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("sys_bytes"),
|
||||
"Number of bytes obtained from system.",
|
||||
"Number of bytes obtained from system. Equals to /memory/classes/total:byte.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
|
||||
valType: GaugeValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("lookups_total"),
|
||||
"Total number of pointer lookups.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
|
||||
valType: CounterValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mallocs_total"),
|
||||
"Total number of mallocs.",
|
||||
// TODO(bwplotka): We could add go_memstats_heap_objects, probably useful for discovery. Let's gather more feedback, kind of a waste of bytes for everybody for compatibility reasons to keep both, and we can't really rename/remove useful metric.
|
||||
"Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
|
||||
@@ -68,7 +61,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("frees_total"),
|
||||
"Total number of frees.",
|
||||
"Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
|
||||
@@ -76,7 +69,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_alloc_bytes"),
|
||||
"Number of heap bytes allocated and still in use.",
|
||||
"Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
|
||||
@@ -84,7 +77,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_sys_bytes"),
|
||||
"Number of heap bytes obtained from system.",
|
||||
"Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
|
||||
@@ -92,7 +85,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_idle_bytes"),
|
||||
"Number of heap bytes waiting to be used.",
|
||||
"Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
|
||||
@@ -100,7 +93,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_inuse_bytes"),
|
||||
"Number of heap bytes that are in use.",
|
||||
"Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
|
||||
@@ -108,7 +101,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_released_bytes"),
|
||||
"Number of heap bytes released to OS.",
|
||||
"Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
|
||||
@@ -116,7 +109,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_objects"),
|
||||
"Number of allocated objects.",
|
||||
"Number of currently allocated objects. Equals to /gc/heap/objects:objects.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
|
||||
@@ -124,7 +117,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("stack_inuse_bytes"),
|
||||
"Number of bytes in use by the stack allocator.",
|
||||
"Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
|
||||
@@ -132,7 +125,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("stack_sys_bytes"),
|
||||
"Number of bytes obtained from system for stack allocator.",
|
||||
"Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
|
||||
@@ -140,7 +133,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mspan_inuse_bytes"),
|
||||
"Number of bytes in use by mspan structures.",
|
||||
"Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
|
||||
@@ -148,7 +141,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mspan_sys_bytes"),
|
||||
"Number of bytes used for mspan structures obtained from system.",
|
||||
"Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
|
||||
@@ -156,7 +149,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mcache_inuse_bytes"),
|
||||
"Number of bytes in use by mcache structures.",
|
||||
"Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
|
||||
@@ -164,7 +157,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("mcache_sys_bytes"),
|
||||
"Number of bytes used for mcache structures obtained from system.",
|
||||
"Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
|
||||
@@ -172,7 +165,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("buck_hash_sys_bytes"),
|
||||
"Number of bytes used by the profiling bucket hash table.",
|
||||
"Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
|
||||
@@ -180,7 +173,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("gc_sys_bytes"),
|
||||
"Number of bytes used for garbage collection system metadata.",
|
||||
"Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
|
||||
@@ -188,7 +181,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("other_sys_bytes"),
|
||||
"Number of bytes used for other system allocations.",
|
||||
"Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
|
||||
@@ -196,7 +189,7 @@ func goRuntimeMemStats() memStatsMetrics {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("next_gc_bytes"),
|
||||
"Number of heap bytes when next garbage collection will take place.",
|
||||
"Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
|
||||
@@ -225,7 +218,7 @@ func newBaseGoCollector() baseGoCollector {
|
||||
nil, nil),
|
||||
gcDesc: NewDesc(
|
||||
"go_gc_duration_seconds",
|
||||
"A summary of the pause duration of garbage collection cycles.",
|
||||
"A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.",
|
||||
nil, nil),
|
||||
gcLastTimeDesc: NewDesc(
|
||||
"go_memstats_last_gc_time_seconds",
|
||||
|
||||
19
vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go
generated
vendored
19
vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go
generated
vendored
@@ -17,6 +17,7 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"runtime/metrics"
|
||||
@@ -153,7 +154,8 @@ func defaultGoCollectorOptions() internal.GoCollectorOptions {
|
||||
"/gc/heap/frees-by-size:bytes": goGCHeapFreesBytes,
|
||||
},
|
||||
RuntimeMetricRules: []internal.GoCollectorRule{
|
||||
//{Matcher: regexp.MustCompile("")},
|
||||
// Recommended metrics we want by default from runtime/metrics.
|
||||
{Matcher: internal.GoCollectorDefaultRuntimeMetrics},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -203,6 +205,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
// to fail here. This condition is tested in TestExpectedRuntimeMetrics.
|
||||
continue
|
||||
}
|
||||
help := attachOriginalName(d.Description.Description, d.Name)
|
||||
|
||||
sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name})
|
||||
sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1]
|
||||
@@ -214,7 +217,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
m = newBatchHistogram(
|
||||
NewDesc(
|
||||
BuildFQName(namespace, subsystem, name),
|
||||
d.Description.Description,
|
||||
help,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
@@ -226,7 +229,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: name,
|
||||
Help: d.Description.Description,
|
||||
Help: help,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
@@ -234,7 +237,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
Namespace: namespace,
|
||||
Subsystem: subsystem,
|
||||
Name: name,
|
||||
Help: d.Description.Description,
|
||||
Help: help,
|
||||
})
|
||||
}
|
||||
metricSet = append(metricSet, m)
|
||||
@@ -284,6 +287,10 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
|
||||
}
|
||||
}
|
||||
|
||||
func attachOriginalName(desc, origName string) string {
|
||||
return fmt.Sprintf("%s Sourced from %s.", desc, origName)
|
||||
}
|
||||
|
||||
// Describe returns all descriptions of the collector.
|
||||
func (c *goCollector) Describe(ch chan<- *Desc) {
|
||||
c.base.Describe(ch)
|
||||
@@ -376,13 +383,13 @@ func unwrapScalarRMValue(v metrics.Value) float64 {
|
||||
//
|
||||
// This should never happen because we always populate our metric
|
||||
// set from the runtime/metrics package.
|
||||
panic("unexpected unsupported metric")
|
||||
panic("unexpected bad kind metric")
|
||||
default:
|
||||
// Unsupported metric kind.
|
||||
//
|
||||
// This should never happen because we check for this during initialization
|
||||
// and flag and filter metrics whose kinds we don't understand.
|
||||
panic("unexpected unsupported metric kind")
|
||||
panic(fmt.Sprintf("unexpected unsupported metric: %v", v.Kind()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
691
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
691
vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
@@ -14,6 +14,7 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
@@ -25,6 +26,12 @@ import (
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
const (
|
||||
nativeHistogramSchemaMaximum = 8
|
||||
nativeHistogramSchemaMinimum = -4
|
||||
)
|
||||
|
||||
// nativeHistogramBounds for the frac of observed values. Only relevant for
|
||||
@@ -329,11 +336,11 @@ func ExponentialBuckets(start, factor float64, count int) []float64 {
|
||||
// used for the Buckets field of HistogramOpts.
|
||||
//
|
||||
// The function panics if 'count' is 0 or negative, if 'min' is 0 or negative.
|
||||
func ExponentialBucketsRange(min, max float64, count int) []float64 {
|
||||
func ExponentialBucketsRange(minBucket, maxBucket float64, count int) []float64 {
|
||||
if count < 1 {
|
||||
panic("ExponentialBucketsRange count needs a positive count")
|
||||
}
|
||||
if min <= 0 {
|
||||
if minBucket <= 0 {
|
||||
panic("ExponentialBucketsRange min needs to be greater than 0")
|
||||
}
|
||||
|
||||
@@ -341,12 +348,12 @@ func ExponentialBucketsRange(min, max float64, count int) []float64 {
|
||||
// max = min*growthFactor^(bucketCount-1)
|
||||
|
||||
// We know max/min and highest bucket. Solve for growthFactor.
|
||||
growthFactor := math.Pow(max/min, 1.0/float64(count-1))
|
||||
growthFactor := math.Pow(maxBucket/minBucket, 1.0/float64(count-1))
|
||||
|
||||
// Now that we know growthFactor, solve for each bucket.
|
||||
buckets := make([]float64, count)
|
||||
for i := 1; i <= count; i++ {
|
||||
buckets[i-1] = min * math.Pow(growthFactor, float64(i-1))
|
||||
buckets[i-1] = minBucket * math.Pow(growthFactor, float64(i-1))
|
||||
}
|
||||
return buckets
|
||||
}
|
||||
@@ -391,7 +398,7 @@ type HistogramOpts struct {
|
||||
// zero, it is replaced by default buckets. The default buckets are
|
||||
// DefBuckets if no buckets for a native histogram (see below) are used,
|
||||
// otherwise the default is no buckets. (In other words, if you want to
|
||||
// use both reguler buckets and buckets for a native histogram, you have
|
||||
// use both regular buckets and buckets for a native histogram, you have
|
||||
// to define the regular buckets here explicitly.)
|
||||
Buckets []float64
|
||||
|
||||
@@ -401,7 +408,7 @@ type HistogramOpts struct {
|
||||
// Histogram by a Prometheus server with that feature enabled (requires
|
||||
// Prometheus v2.40+). Sparse buckets are exponential buckets covering
|
||||
// the whole float64 range (with the exception of the “zero” bucket, see
|
||||
// SparseBucketsZeroThreshold below). From any one bucket to the next,
|
||||
// NativeHistogramZeroThreshold below). From any one bucket to the next,
|
||||
// the width of the bucket grows by a constant
|
||||
// factor. NativeHistogramBucketFactor provides an upper bound for this
|
||||
// factor (exception see below). The smaller
|
||||
@@ -413,8 +420,8 @@ type HistogramOpts struct {
|
||||
// and 2, same as between 2 and 4, and 4 and 8, etc.).
|
||||
//
|
||||
// Details about the actually used factor: The factor is calculated as
|
||||
// 2^(2^n), where n is an integer number between (and including) -8 and
|
||||
// 4. n is chosen so that the resulting factor is the largest that is
|
||||
// 2^(2^-n), where n is an integer number between (and including) -4 and
|
||||
// 8. n is chosen so that the resulting factor is the largest that is
|
||||
// still smaller or equal to NativeHistogramBucketFactor. Note that the
|
||||
// smallest possible factor is therefore approx. 1.00271 (i.e. 2^(2^-8)
|
||||
// ). If NativeHistogramBucketFactor is greater than 1 but smaller than
|
||||
@@ -428,44 +435,71 @@ type HistogramOpts struct {
|
||||
// a major version bump.
|
||||
NativeHistogramBucketFactor float64
|
||||
// All observations with an absolute value of less or equal
|
||||
// NativeHistogramZeroThreshold are accumulated into a “zero”
|
||||
// bucket. For best results, this should be close to a bucket
|
||||
// boundary. This is usually the case if picking a power of two. If
|
||||
// NativeHistogramZeroThreshold are accumulated into a “zero” bucket.
|
||||
// For best results, this should be close to a bucket boundary. This is
|
||||
// usually the case if picking a power of two. If
|
||||
// NativeHistogramZeroThreshold is left at zero,
|
||||
// DefSparseBucketsZeroThreshold is used as the threshold. To configure
|
||||
// a zero bucket with an actual threshold of zero (i.e. only
|
||||
// DefNativeHistogramZeroThreshold is used as the threshold. To
|
||||
// configure a zero bucket with an actual threshold of zero (i.e. only
|
||||
// observations of precisely zero will go into the zero bucket), set
|
||||
// NativeHistogramZeroThreshold to the NativeHistogramZeroThresholdZero
|
||||
// constant (or any negative float value).
|
||||
NativeHistogramZeroThreshold float64
|
||||
|
||||
// The remaining fields define a strategy to limit the number of
|
||||
// The next three fields define a strategy to limit the number of
|
||||
// populated sparse buckets. If NativeHistogramMaxBucketNumber is left
|
||||
// at zero, the number of buckets is not limited. (Note that this might
|
||||
// lead to unbounded memory consumption if the values observed by the
|
||||
// Histogram are sufficiently wide-spread. In particular, this could be
|
||||
// used as a DoS attack vector. Where the observed values depend on
|
||||
// external inputs, it is highly recommended to set a
|
||||
// NativeHistogramMaxBucketNumber.) Once the set
|
||||
// NativeHistogramMaxBucketNumber.) Once the set
|
||||
// NativeHistogramMaxBucketNumber is exceeded, the following strategy is
|
||||
// enacted: First, if the last reset (or the creation) of the histogram
|
||||
// is at least NativeHistogramMinResetDuration ago, then the whole
|
||||
// histogram is reset to its initial state (including regular
|
||||
// buckets). If less time has passed, or if
|
||||
// NativeHistogramMinResetDuration is zero, no reset is
|
||||
// performed. Instead, the zero threshold is increased sufficiently to
|
||||
// reduce the number of buckets to or below
|
||||
// NativeHistogramMaxBucketNumber, but not to more than
|
||||
// NativeHistogramMaxZeroThreshold. Thus, if
|
||||
// NativeHistogramMaxZeroThreshold is already at or below the current
|
||||
// zero threshold, nothing happens at this step. After that, if the
|
||||
// number of buckets still exceeds NativeHistogramMaxBucketNumber, the
|
||||
// resolution of the histogram is reduced by doubling the width of the
|
||||
// sparse buckets (up to a growth factor between one bucket to the next
|
||||
// of 2^(2^4) = 65536, see above).
|
||||
// enacted:
|
||||
// - First, if the last reset (or the creation) of the histogram is at
|
||||
// least NativeHistogramMinResetDuration ago, then the whole
|
||||
// histogram is reset to its initial state (including regular
|
||||
// buckets).
|
||||
// - If less time has passed, or if NativeHistogramMinResetDuration is
|
||||
// zero, no reset is performed. Instead, the zero threshold is
|
||||
// increased sufficiently to reduce the number of buckets to or below
|
||||
// NativeHistogramMaxBucketNumber, but not to more than
|
||||
// NativeHistogramMaxZeroThreshold. Thus, if
|
||||
// NativeHistogramMaxZeroThreshold is already at or below the current
|
||||
// zero threshold, nothing happens at this step.
|
||||
// - After that, if the number of buckets still exceeds
|
||||
// NativeHistogramMaxBucketNumber, the resolution of the histogram is
|
||||
// reduced by doubling the width of the sparse buckets (up to a
|
||||
// growth factor between one bucket to the next of 2^(2^4) = 65536,
|
||||
// see above).
|
||||
// - Any increased zero threshold or reduced resolution is reset back
|
||||
// to their original values once NativeHistogramMinResetDuration has
|
||||
// passed (since the last reset or the creation of the histogram).
|
||||
NativeHistogramMaxBucketNumber uint32
|
||||
NativeHistogramMinResetDuration time.Duration
|
||||
NativeHistogramMaxZeroThreshold float64
|
||||
|
||||
// NativeHistogramMaxExemplars limits the number of exemplars
|
||||
// that are kept in memory for each native histogram. If you leave it at
|
||||
// zero, a default value of 10 is used. If no exemplars should be kept specifically
|
||||
// for native histograms, set it to a negative value. (Scrapers can
|
||||
// still use the exemplars exposed for classic buckets, which are managed
|
||||
// independently.)
|
||||
NativeHistogramMaxExemplars int
|
||||
// NativeHistogramExemplarTTL is only checked once
|
||||
// NativeHistogramMaxExemplars is exceeded. In that case, the
|
||||
// oldest exemplar is removed if it is older than NativeHistogramExemplarTTL.
|
||||
// Otherwise, the older exemplar in the pair of exemplars that are closest
|
||||
// together (on an exponential scale) is removed.
|
||||
// If NativeHistogramExemplarTTL is left at its zero value, a default value of
|
||||
// 5m is used. To always delete the oldest exemplar, set it to a negative value.
|
||||
NativeHistogramExemplarTTL time.Duration
|
||||
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
|
||||
// afterFunc is for testing purposes, by default it's time.AfterFunc.
|
||||
afterFunc func(time.Duration, func()) *time.Timer
|
||||
}
|
||||
|
||||
// HistogramVecOpts bundles the options to create a HistogramVec metric.
|
||||
@@ -475,7 +509,7 @@ type HistogramVecOpts struct {
|
||||
HistogramOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -499,12 +533,12 @@ func NewHistogram(opts HistogramOpts) Histogram {
|
||||
}
|
||||
|
||||
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
||||
if len(desc.variableLabels) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
|
||||
if len(desc.variableLabels.names) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
|
||||
}
|
||||
|
||||
for _, n := range desc.variableLabels {
|
||||
if n.Name == bucketLabel {
|
||||
for _, n := range desc.variableLabels.names {
|
||||
if n == bucketLabel {
|
||||
panic(errBucketLabelNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -514,6 +548,13 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||
}
|
||||
}
|
||||
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
if opts.afterFunc == nil {
|
||||
opts.afterFunc = time.AfterFunc
|
||||
}
|
||||
|
||||
h := &histogram{
|
||||
desc: desc,
|
||||
upperBounds: opts.Buckets,
|
||||
@@ -521,8 +562,9 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||
nativeHistogramMaxBuckets: opts.NativeHistogramMaxBucketNumber,
|
||||
nativeHistogramMaxZeroThreshold: opts.NativeHistogramMaxZeroThreshold,
|
||||
nativeHistogramMinResetDuration: opts.NativeHistogramMinResetDuration,
|
||||
lastResetTime: time.Now(),
|
||||
now: time.Now,
|
||||
lastResetTime: opts.now(),
|
||||
now: opts.now,
|
||||
afterFunc: opts.afterFunc,
|
||||
}
|
||||
if len(h.upperBounds) == 0 && opts.NativeHistogramBucketFactor <= 1 {
|
||||
h.upperBounds = DefBuckets
|
||||
@@ -537,6 +579,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
||||
h.nativeHistogramZeroThreshold = DefNativeHistogramZeroThreshold
|
||||
} // Leave h.nativeHistogramZeroThreshold at 0 otherwise.
|
||||
h.nativeHistogramSchema = pickSchema(opts.NativeHistogramBucketFactor)
|
||||
h.nativeExemplars = makeNativeExemplars(opts.NativeHistogramExemplarTTL, opts.NativeHistogramMaxExemplars)
|
||||
}
|
||||
for i, upperBound := range h.upperBounds {
|
||||
if i < len(h.upperBounds)-1 {
|
||||
@@ -639,8 +682,8 @@ func (hc *histogramCounts) observe(v float64, bucket int, doSparse bool) {
|
||||
if frac == 0.5 {
|
||||
key--
|
||||
}
|
||||
div := 1 << -schema
|
||||
key = (key + div - 1) / div
|
||||
offset := (1 << -schema) - 1
|
||||
key = (key + offset) >> -schema
|
||||
}
|
||||
if isInf {
|
||||
key++
|
||||
@@ -701,9 +744,19 @@ type histogram struct {
|
||||
nativeHistogramMaxZeroThreshold float64
|
||||
nativeHistogramMaxBuckets uint32
|
||||
nativeHistogramMinResetDuration time.Duration
|
||||
lastResetTime time.Time // Protected by mtx.
|
||||
// lastResetTime is protected by mtx. It is also used as created timestamp.
|
||||
lastResetTime time.Time
|
||||
// resetScheduled is protected by mtx. It is true if a reset is
|
||||
// scheduled for a later time (when nativeHistogramMinResetDuration has
|
||||
// passed).
|
||||
resetScheduled bool
|
||||
nativeExemplars nativeExemplars
|
||||
|
||||
now func() time.Time // To mock out time.Now() for testing.
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
|
||||
// afterFunc is for testing purposes, by default it's time.AfterFunc.
|
||||
afterFunc func(time.Duration, func()) *time.Timer
|
||||
}
|
||||
|
||||
func (h *histogram) Desc() *Desc {
|
||||
@@ -714,6 +767,9 @@ func (h *histogram) Observe(v float64) {
|
||||
h.observe(v, h.findBucket(v))
|
||||
}
|
||||
|
||||
// ObserveWithExemplar should not be called in a high-frequency setting
|
||||
// for a native histogram with configured exemplars. For this case,
|
||||
// the implementation isn't lock-free and might suffer from lock contention.
|
||||
func (h *histogram) ObserveWithExemplar(v float64, e Labels) {
|
||||
i := h.findBucket(v)
|
||||
h.observe(v, i)
|
||||
@@ -742,9 +798,10 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||
waitForCooldown(count, coldCounts)
|
||||
|
||||
his := &dto.Histogram{
|
||||
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
Bucket: make([]*dto.Bucket, len(h.upperBounds)),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
CreatedTimestamp: timestamppb.New(h.lastResetTime),
|
||||
}
|
||||
out.Histogram = his
|
||||
out.Label = h.labelPairs
|
||||
@@ -782,6 +839,23 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||
his.ZeroCount = proto.Uint64(zeroBucket)
|
||||
his.NegativeSpan, his.NegativeDelta = makeBuckets(&coldCounts.nativeHistogramBucketsNegative)
|
||||
his.PositiveSpan, his.PositiveDelta = makeBuckets(&coldCounts.nativeHistogramBucketsPositive)
|
||||
|
||||
// Add a no-op span to a histogram without observations and with
|
||||
// a zero threshold of zero. Otherwise, a native histogram would
|
||||
// look like a classic histogram to scrapers.
|
||||
if *his.ZeroThreshold == 0 && *his.ZeroCount == 0 && len(his.PositiveSpan) == 0 && len(his.NegativeSpan) == 0 {
|
||||
his.PositiveSpan = []*dto.BucketSpan{{
|
||||
Offset: proto.Int32(0),
|
||||
Length: proto.Uint32(0),
|
||||
}}
|
||||
}
|
||||
|
||||
if h.nativeExemplars.isEnabled() {
|
||||
h.nativeExemplars.Lock()
|
||||
his.Exemplars = append(his.Exemplars, h.nativeExemplars.exemplars...)
|
||||
h.nativeExemplars.Unlock()
|
||||
}
|
||||
|
||||
}
|
||||
addAndResetCounts(hotCounts, coldCounts)
|
||||
return nil
|
||||
@@ -790,15 +864,35 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||
// findBucket returns the index of the bucket for the provided value, or
|
||||
// len(h.upperBounds) for the +Inf bucket.
|
||||
func (h *histogram) findBucket(v float64) int {
|
||||
// TODO(beorn7): For small numbers of buckets (<30), a linear search is
|
||||
// slightly faster than the binary search. If we really care, we could
|
||||
// switch from one search strategy to the other depending on the number
|
||||
// of buckets.
|
||||
//
|
||||
// Microbenchmarks (BenchmarkHistogramNoLabels):
|
||||
// 11 buckets: 38.3 ns/op linear - binary 48.7 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
|
||||
n := len(h.upperBounds)
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Early exit: if v is less than or equal to the first upper bound, return 0
|
||||
if v <= h.upperBounds[0] {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Early exit: if v is greater than the last upper bound, return len(h.upperBounds)
|
||||
if v > h.upperBounds[n-1] {
|
||||
return n
|
||||
}
|
||||
|
||||
// For small arrays, use simple linear search
|
||||
// "magic number" 35 is result of tests on couple different (AWS and baremetal) servers
|
||||
// see more details here: https://github.com/prometheus/client_golang/pull/1662
|
||||
if n < 35 {
|
||||
for i, bound := range h.upperBounds {
|
||||
if v <= bound {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// If v is greater than all upper bounds, return len(h.upperBounds)
|
||||
return n
|
||||
}
|
||||
|
||||
// For larger arrays, use stdlib's binary search
|
||||
return sort.SearchFloat64s(h.upperBounds, v)
|
||||
}
|
||||
|
||||
@@ -817,7 +911,7 @@ func (h *histogram) observe(v float64, bucket int) {
|
||||
}
|
||||
}
|
||||
|
||||
// limitSparsebuckets applies a strategy to limit the number of populated sparse
|
||||
// limitBuckets applies a strategy to limit the number of populated sparse
|
||||
// buckets. It's generally best effort, and there are situations where the
|
||||
// number can go higher (if even the lowest resolution isn't enough to reduce
|
||||
// the number sufficiently, or if the provided counts aren't fully updated yet
|
||||
@@ -848,26 +942,39 @@ func (h *histogram) limitBuckets(counts *histogramCounts, value float64, bucket
|
||||
if h.maybeReset(hotCounts, coldCounts, coldIdx, value, bucket) {
|
||||
return
|
||||
}
|
||||
// One of the other strategies will happen. To undo what they will do as
|
||||
// soon as enough time has passed to satisfy
|
||||
// h.nativeHistogramMinResetDuration, schedule a reset at the right time
|
||||
// if we haven't done so already.
|
||||
if h.nativeHistogramMinResetDuration > 0 && !h.resetScheduled {
|
||||
h.resetScheduled = true
|
||||
h.afterFunc(h.nativeHistogramMinResetDuration-h.now().Sub(h.lastResetTime), h.reset)
|
||||
}
|
||||
|
||||
if h.maybeWidenZeroBucket(hotCounts, coldCounts) {
|
||||
return
|
||||
}
|
||||
h.doubleBucketWidth(hotCounts, coldCounts)
|
||||
}
|
||||
|
||||
// maybeReset resests the whole histogram if at least h.nativeHistogramMinResetDuration
|
||||
// has been passed. It returns true if the histogram has been reset. The caller
|
||||
// must have locked h.mtx.
|
||||
func (h *histogram) maybeReset(hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int) bool {
|
||||
// maybeReset resets the whole histogram if at least
|
||||
// h.nativeHistogramMinResetDuration has been passed. It returns true if the
|
||||
// histogram has been reset. The caller must have locked h.mtx.
|
||||
func (h *histogram) maybeReset(
|
||||
hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int,
|
||||
) bool {
|
||||
// We are using the possibly mocked h.now() rather than
|
||||
// time.Since(h.lastResetTime) to enable testing.
|
||||
if h.nativeHistogramMinResetDuration == 0 || h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
|
||||
if h.nativeHistogramMinResetDuration == 0 || // No reset configured.
|
||||
h.resetScheduled || // Do not interefere if a reset is already scheduled.
|
||||
h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
|
||||
return false
|
||||
}
|
||||
// Completely reset coldCounts.
|
||||
h.resetCounts(cold)
|
||||
// Repeat the latest observation to not lose it completely.
|
||||
cold.observe(value, bucket, true)
|
||||
// Make coldCounts the new hot counts while ressetting countAndHotIdx.
|
||||
// Make coldCounts the new hot counts while resetting countAndHotIdx.
|
||||
n := atomic.SwapUint64(&h.countAndHotIdx, (coldIdx<<63)+1)
|
||||
count := n & ((1 << 63) - 1)
|
||||
waitForCooldown(count, hot)
|
||||
@@ -877,6 +984,29 @@ func (h *histogram) maybeReset(hot, cold *histogramCounts, coldIdx uint64, value
|
||||
return true
|
||||
}
|
||||
|
||||
// reset resets the whole histogram. It locks h.mtx itself, i.e. it has to be
|
||||
// called without having locked h.mtx.
|
||||
func (h *histogram) reset() {
|
||||
h.mtx.Lock()
|
||||
defer h.mtx.Unlock()
|
||||
|
||||
n := atomic.LoadUint64(&h.countAndHotIdx)
|
||||
hotIdx := n >> 63
|
||||
coldIdx := (^n) >> 63
|
||||
hot := h.counts[hotIdx]
|
||||
cold := h.counts[coldIdx]
|
||||
// Completely reset coldCounts.
|
||||
h.resetCounts(cold)
|
||||
// Make coldCounts the new hot counts while resetting countAndHotIdx.
|
||||
n = atomic.SwapUint64(&h.countAndHotIdx, coldIdx<<63)
|
||||
count := n & ((1 << 63) - 1)
|
||||
waitForCooldown(count, hot)
|
||||
// Finally, reset the formerly hot counts, too.
|
||||
h.resetCounts(hot)
|
||||
h.lastResetTime = h.now()
|
||||
h.resetScheduled = false
|
||||
}
|
||||
|
||||
// maybeWidenZeroBucket widens the zero bucket until it includes the existing
|
||||
// buckets closest to the zero bucket (which could be two, if an equidistant
|
||||
// negative and a positive bucket exists, but usually it's only one bucket to be
|
||||
@@ -1016,8 +1146,10 @@ func (h *histogram) resetCounts(counts *histogramCounts) {
|
||||
deleteSyncMap(&counts.nativeHistogramBucketsPositive)
|
||||
}
|
||||
|
||||
// updateExemplar replaces the exemplar for the provided bucket. With empty
|
||||
// labels, it's a no-op. It panics if any of the labels is invalid.
|
||||
// updateExemplar replaces the exemplar for the provided classic bucket.
|
||||
// With empty labels, it's a no-op. It panics if any of the labels is invalid.
|
||||
// If histogram is native, the exemplar will be cached into nativeExemplars,
|
||||
// which has a limit, and will remove one exemplar when limit is reached.
|
||||
func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
|
||||
if l == nil {
|
||||
return
|
||||
@@ -1027,6 +1159,10 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
|
||||
panic(err)
|
||||
}
|
||||
h.exemplars[bucket].Store(e)
|
||||
doSparse := h.nativeHistogramSchema > math.MinInt32 && !math.IsNaN(v)
|
||||
if doSparse {
|
||||
h.nativeExemplars.addExemplar(e)
|
||||
}
|
||||
}
|
||||
|
||||
// HistogramVec is a Collector that bundles a set of Histograms that all share the
|
||||
@@ -1176,6 +1312,7 @@ type constHistogram struct {
|
||||
sum float64
|
||||
buckets map[float64]uint64
|
||||
labelPairs []*dto.LabelPair
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (h *constHistogram) Desc() *Desc {
|
||||
@@ -1183,7 +1320,9 @@ func (h *constHistogram) Desc() *Desc {
|
||||
}
|
||||
|
||||
func (h *constHistogram) Write(out *dto.Metric) error {
|
||||
his := &dto.Histogram{}
|
||||
his := &dto.Histogram{
|
||||
CreatedTimestamp: h.createdTs,
|
||||
}
|
||||
|
||||
buckets := make([]*dto.Bucket, 0, len(h.buckets))
|
||||
|
||||
@@ -1230,7 +1369,7 @@ func NewConstHistogram(
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &constHistogram{
|
||||
@@ -1258,6 +1397,48 @@ func MustNewConstHistogram(
|
||||
return m
|
||||
}
|
||||
|
||||
// NewConstHistogramWithCreatedTimestamp does the same thing as NewConstHistogram but sets the created timestamp.
|
||||
func NewConstHistogramWithCreatedTimestamp(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
sum float64,
|
||||
buckets map[float64]uint64,
|
||||
ct time.Time,
|
||||
labelValues ...string,
|
||||
) (Metric, error) {
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &constHistogram{
|
||||
desc: desc,
|
||||
count: count,
|
||||
sum: sum,
|
||||
buckets: buckets,
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
createdTs: timestamppb.New(ct),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustNewConstHistogramWithCreatedTimestamp is a version of NewConstHistogramWithCreatedTimestamp that panics where
|
||||
// NewConstHistogramWithCreatedTimestamp would have returned an error.
|
||||
func MustNewConstHistogramWithCreatedTimestamp(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
sum float64,
|
||||
buckets map[float64]uint64,
|
||||
ct time.Time,
|
||||
labelValues ...string,
|
||||
) Metric {
|
||||
m, err := NewConstHistogramWithCreatedTimestamp(desc, count, sum, buckets, ct, labelValues...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type buckSort []*dto.Bucket
|
||||
|
||||
func (s buckSort) Len() int {
|
||||
@@ -1285,9 +1466,9 @@ func pickSchema(bucketFactor float64) int32 {
|
||||
floor := math.Floor(math.Log2(math.Log2(bucketFactor)))
|
||||
switch {
|
||||
case floor <= -8:
|
||||
return 8
|
||||
return nativeHistogramSchemaMaximum
|
||||
case floor >= 4:
|
||||
return -4
|
||||
return nativeHistogramSchemaMinimum
|
||||
default:
|
||||
return -int32(floor)
|
||||
}
|
||||
@@ -1324,7 +1505,7 @@ func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) {
|
||||
// Multiple spans with only small gaps in between are probably
|
||||
// encoded more efficiently as one larger span with a few empty
|
||||
// buckets. Needs some research to find the sweet spot. For now,
|
||||
// we assume that gaps of one ore two buckets should not create
|
||||
// we assume that gaps of one or two buckets should not create
|
||||
// a new span.
|
||||
iDelta := int32(i - nextI)
|
||||
if n == 0 || iDelta > 2 {
|
||||
@@ -1497,3 +1678,379 @@ func addAndResetCounts(hot, cold *histogramCounts) {
|
||||
atomic.AddUint64(&hot.nativeHistogramZeroBucket, atomic.LoadUint64(&cold.nativeHistogramZeroBucket))
|
||||
atomic.StoreUint64(&cold.nativeHistogramZeroBucket, 0)
|
||||
}
|
||||
|
||||
type nativeExemplars struct {
|
||||
sync.Mutex
|
||||
|
||||
// Time-to-live for exemplars, it is set to -1 if exemplars are disabled, that is NativeHistogramMaxExemplars is below 0.
|
||||
// The ttl is used on insertion to remove an exemplar that is older than ttl, if present.
|
||||
ttl time.Duration
|
||||
|
||||
exemplars []*dto.Exemplar
|
||||
}
|
||||
|
||||
func (n *nativeExemplars) isEnabled() bool {
|
||||
return n.ttl != -1
|
||||
}
|
||||
|
||||
func makeNativeExemplars(ttl time.Duration, maxCount int) nativeExemplars {
|
||||
if ttl == 0 {
|
||||
ttl = 5 * time.Minute
|
||||
}
|
||||
|
||||
if maxCount == 0 {
|
||||
maxCount = 10
|
||||
}
|
||||
|
||||
if maxCount < 0 {
|
||||
maxCount = 0
|
||||
ttl = -1
|
||||
}
|
||||
|
||||
return nativeExemplars{
|
||||
ttl: ttl,
|
||||
exemplars: make([]*dto.Exemplar, 0, maxCount),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
|
||||
if !n.isEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
// When the number of exemplars has not yet exceeded or
|
||||
// is equal to cap(n.exemplars), then
|
||||
// insert the new exemplar directly.
|
||||
if len(n.exemplars) < cap(n.exemplars) {
|
||||
var nIdx int
|
||||
for nIdx = 0; nIdx < len(n.exemplars); nIdx++ {
|
||||
if *e.Value < *n.exemplars[nIdx].Value {
|
||||
break
|
||||
}
|
||||
}
|
||||
n.exemplars = append(n.exemplars[:nIdx], append([]*dto.Exemplar{e}, n.exemplars[nIdx:]...)...)
|
||||
return
|
||||
}
|
||||
|
||||
if len(n.exemplars) == 1 {
|
||||
// When the number of exemplars is 1, then
|
||||
// replace the existing exemplar with the new exemplar.
|
||||
n.exemplars[0] = e
|
||||
return
|
||||
}
|
||||
// From this point on, the number of exemplars is greater than 1.
|
||||
|
||||
// When the number of exemplars exceeds the limit, remove one exemplar.
|
||||
var (
|
||||
ot = time.Time{} // Oldest timestamp seen. Initial value doesn't matter as we replace it due to otIdx == -1 in the loop.
|
||||
otIdx = -1 // Index of the exemplar with the oldest timestamp.
|
||||
|
||||
md = -1.0 // Logarithm of the delta of the closest pair of exemplars.
|
||||
|
||||
// The insertion point of the new exemplar in the exemplars slice after insertion.
|
||||
// This is calculated purely based on the order of the exemplars by value.
|
||||
// nIdx == len(n.exemplars) means the new exemplar is to be inserted after the end.
|
||||
nIdx = -1
|
||||
|
||||
// rIdx is ultimately the index for the exemplar that we are replacing with the new exemplar.
|
||||
// The aim is to keep a good spread of exemplars by value and not let them bunch up too much.
|
||||
// It is calculated in 3 steps:
|
||||
// 1. First we set rIdx to the index of the older exemplar within the closest pair by value.
|
||||
// That is the following will be true (on log scale):
|
||||
// either the exemplar pair on index (rIdx-1, rIdx) or (rIdx, rIdx+1) will have
|
||||
// the closest values to each other from all pairs.
|
||||
// For example, suppose the values are distributed like this:
|
||||
// |-----------x-------------x----------------x----x-----|
|
||||
// ^--rIdx as this is older.
|
||||
// Or like this:
|
||||
// |-----------x-------------x----------------x----x-----|
|
||||
// ^--rIdx as this is older.
|
||||
// 2. If there is an exemplar that expired, then we simple reset rIdx to that index.
|
||||
// 3. We check if by inserting the new exemplar we would create a closer pair at
|
||||
// (nIdx-1, nIdx) or (nIdx, nIdx+1) and set rIdx to nIdx-1 or nIdx accordingly to
|
||||
// keep the spread of exemplars by value; otherwise we keep rIdx as it is.
|
||||
rIdx = -1
|
||||
cLog float64 // Logarithm of the current exemplar.
|
||||
pLog float64 // Logarithm of the previous exemplar.
|
||||
)
|
||||
|
||||
for i, exemplar := range n.exemplars {
|
||||
// Find the exemplar with the oldest timestamp.
|
||||
if otIdx == -1 || exemplar.Timestamp.AsTime().Before(ot) {
|
||||
ot = exemplar.Timestamp.AsTime()
|
||||
otIdx = i
|
||||
}
|
||||
|
||||
// Find the index at which to insert new the exemplar.
|
||||
if nIdx == -1 && *e.Value <= *exemplar.Value {
|
||||
nIdx = i
|
||||
}
|
||||
|
||||
// Find the two closest exemplars and pick the one the with older timestamp.
|
||||
pLog = cLog
|
||||
cLog = math.Log(exemplar.GetValue())
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
diff := math.Abs(cLog - pLog)
|
||||
if md == -1 || diff < md {
|
||||
// The closest exemplar pair is at index: i-1, i.
|
||||
// Choose the exemplar with the older timestamp for replacement.
|
||||
md = diff
|
||||
if n.exemplars[i].Timestamp.AsTime().Before(n.exemplars[i-1].Timestamp.AsTime()) {
|
||||
rIdx = i
|
||||
} else {
|
||||
rIdx = i - 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If all existing exemplar are smaller than new exemplar,
|
||||
// then the exemplar should be inserted at the end.
|
||||
if nIdx == -1 {
|
||||
nIdx = len(n.exemplars)
|
||||
}
|
||||
// Here, we have the following relationships:
|
||||
// n.exemplars[nIdx-1].Value < e.Value (if nIdx > 0)
|
||||
// e.Value <= n.exemplars[nIdx].Value (if nIdx < len(n.exemplars))
|
||||
|
||||
if otIdx != -1 && e.Timestamp.AsTime().Sub(ot) > n.ttl {
|
||||
// If the oldest exemplar has expired, then replace it with the new exemplar.
|
||||
rIdx = otIdx
|
||||
} else {
|
||||
// In the previous for loop, when calculating the closest pair of exemplars,
|
||||
// we did not take into account the newly inserted exemplar.
|
||||
// So we need to calculate with the newly inserted exemplar again.
|
||||
elog := math.Log(e.GetValue())
|
||||
if nIdx > 0 {
|
||||
diff := math.Abs(elog - math.Log(n.exemplars[nIdx-1].GetValue()))
|
||||
if diff < md {
|
||||
// The value we are about to insert is closer to the previous exemplar at the insertion point than what we calculated before in rIdx.
|
||||
// v--rIdx
|
||||
// |-----------x-n-----------x----------------x----x-----|
|
||||
// nIdx-1--^ ^--new exemplar value
|
||||
// Do not make the spread worse, replace nIdx-1 and not rIdx.
|
||||
md = diff
|
||||
rIdx = nIdx - 1
|
||||
}
|
||||
}
|
||||
if nIdx < len(n.exemplars) {
|
||||
diff := math.Abs(math.Log(n.exemplars[nIdx].GetValue()) - elog)
|
||||
if diff < md {
|
||||
// The value we are about to insert is closer to the next exemplar at the insertion point than what we calculated before in rIdx.
|
||||
// v--rIdx
|
||||
// |-----------x-----------n-x----------------x----x-----|
|
||||
// new exemplar value--^ ^--nIdx
|
||||
// Do not make the spread worse, replace nIdx-1 and not rIdx.
|
||||
rIdx = nIdx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the slice according to rIdx and nIdx.
|
||||
switch {
|
||||
case rIdx == nIdx:
|
||||
n.exemplars[nIdx] = e
|
||||
case rIdx < nIdx:
|
||||
n.exemplars = append(n.exemplars[:rIdx], append(n.exemplars[rIdx+1:nIdx], append([]*dto.Exemplar{e}, n.exemplars[nIdx:]...)...)...)
|
||||
case rIdx > nIdx:
|
||||
n.exemplars = append(n.exemplars[:nIdx], append([]*dto.Exemplar{e}, append(n.exemplars[nIdx:rIdx], n.exemplars[rIdx+1:]...)...)...)
|
||||
}
|
||||
}
|
||||
|
||||
type constNativeHistogram struct {
|
||||
desc *Desc
|
||||
dto.Histogram
|
||||
labelPairs []*dto.LabelPair
|
||||
}
|
||||
|
||||
func validateCount(sum float64, count uint64, negativeBuckets, positiveBuckets map[int]int64, zeroBucket uint64) error {
|
||||
var bucketPopulationSum int64
|
||||
for _, v := range positiveBuckets {
|
||||
bucketPopulationSum += v
|
||||
}
|
||||
for _, v := range negativeBuckets {
|
||||
bucketPopulationSum += v
|
||||
}
|
||||
bucketPopulationSum += int64(zeroBucket)
|
||||
|
||||
// If the sum of observations is NaN, the number of observations must be greater or equal to the sum of all bucket counts.
|
||||
// Otherwise, the number of observations must be equal to the sum of all bucket counts .
|
||||
|
||||
if math.IsNaN(sum) && bucketPopulationSum > int64(count) ||
|
||||
!math.IsNaN(sum) && bucketPopulationSum != int64(count) {
|
||||
return errors.New("the sum of all bucket populations exceeds the count of observations")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewConstNativeHistogram returns a metric representing a Prometheus native histogram with
|
||||
// fixed values for the count, sum, and positive/negative/zero bucket counts. As those parameters
|
||||
// cannot be changed, the returned value does not implement the Histogram
|
||||
// interface (but only the Metric interface). Users of this package will not
|
||||
// have much use for it in regular operations. However, when implementing custom
|
||||
// OpenTelemetry Collectors, it is useful as a throw-away metric that is generated on the fly
|
||||
// to send it to Prometheus in the Collect method.
|
||||
//
|
||||
// zeroBucket counts all (positive and negative)
|
||||
// observations in the zero bucket (with an absolute value less or equal
|
||||
// the current threshold).
|
||||
// positiveBuckets and negativeBuckets are separate maps for negative and positive
|
||||
// observations. The map's value is an int64, counting observations in
|
||||
// that bucket. The map's key is the
|
||||
// index of the bucket according to the used
|
||||
// Schema. Index 0 is for an upper bound of 1 in positive buckets and for a lower bound of -1 in negative buckets.
|
||||
// NewConstNativeHistogram returns an error if
|
||||
// - the length of labelValues is not consistent with the variable labels in Desc or if Desc is invalid.
|
||||
// - the schema passed is not between 8 and -4
|
||||
// - the sum of counts in all buckets including the zero bucket does not equal the count if sum is not NaN (or exceeds the count if sum is NaN)
|
||||
//
|
||||
// See https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#exponential-histograms for more details about the conversion from OTel to Prometheus.
|
||||
func NewConstNativeHistogram(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
sum float64,
|
||||
positiveBuckets, negativeBuckets map[int]int64,
|
||||
zeroBucket uint64,
|
||||
schema int32,
|
||||
zeroThreshold float64,
|
||||
createdTimestamp time.Time,
|
||||
labelValues ...string,
|
||||
) (Metric, error) {
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if schema > nativeHistogramSchemaMaximum || schema < nativeHistogramSchemaMinimum {
|
||||
return nil, errors.New("invalid native histogram schema")
|
||||
}
|
||||
if err := validateCount(sum, count, negativeBuckets, positiveBuckets, zeroBucket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
NegativeSpan, NegativeDelta := makeBucketsFromMap(negativeBuckets)
|
||||
PositiveSpan, PositiveDelta := makeBucketsFromMap(positiveBuckets)
|
||||
ret := &constNativeHistogram{
|
||||
desc: desc,
|
||||
Histogram: dto.Histogram{
|
||||
CreatedTimestamp: timestamppb.New(createdTimestamp),
|
||||
Schema: &schema,
|
||||
ZeroThreshold: &zeroThreshold,
|
||||
SampleCount: &count,
|
||||
SampleSum: &sum,
|
||||
|
||||
NegativeSpan: NegativeSpan,
|
||||
NegativeDelta: NegativeDelta,
|
||||
|
||||
PositiveSpan: PositiveSpan,
|
||||
PositiveDelta: PositiveDelta,
|
||||
|
||||
ZeroCount: proto.Uint64(zeroBucket),
|
||||
},
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}
|
||||
if *ret.ZeroThreshold == 0 && *ret.ZeroCount == 0 && len(ret.PositiveSpan) == 0 && len(ret.NegativeSpan) == 0 {
|
||||
ret.PositiveSpan = []*dto.BucketSpan{{
|
||||
Offset: proto.Int32(0),
|
||||
Length: proto.Uint32(0),
|
||||
}}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MustNewConstNativeHistogram is a version of NewConstNativeHistogram that panics where
|
||||
// NewConstNativeHistogram would have returned an error.
|
||||
func MustNewConstNativeHistogram(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
sum float64,
|
||||
positiveBuckets, negativeBuckets map[int]int64,
|
||||
zeroBucket uint64,
|
||||
nativeHistogramSchema int32,
|
||||
nativeHistogramZeroThreshold float64,
|
||||
createdTimestamp time.Time,
|
||||
labelValues ...string,
|
||||
) Metric {
|
||||
nativehistogram, err := NewConstNativeHistogram(desc,
|
||||
count,
|
||||
sum,
|
||||
positiveBuckets,
|
||||
negativeBuckets,
|
||||
zeroBucket,
|
||||
nativeHistogramSchema,
|
||||
nativeHistogramZeroThreshold,
|
||||
createdTimestamp,
|
||||
labelValues...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return nativehistogram
|
||||
}
|
||||
|
||||
func (h *constNativeHistogram) Desc() *Desc {
|
||||
return h.desc
|
||||
}
|
||||
|
||||
func (h *constNativeHistogram) Write(out *dto.Metric) error {
|
||||
out.Histogram = &h.Histogram
|
||||
out.Label = h.labelPairs
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeBucketsFromMap(buckets map[int]int64) ([]*dto.BucketSpan, []int64) {
|
||||
if len(buckets) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var ii []int
|
||||
for k := range buckets {
|
||||
ii = append(ii, k)
|
||||
}
|
||||
sort.Ints(ii)
|
||||
|
||||
var (
|
||||
spans []*dto.BucketSpan
|
||||
deltas []int64
|
||||
prevCount int64
|
||||
nextI int
|
||||
)
|
||||
|
||||
appendDelta := func(count int64) {
|
||||
*spans[len(spans)-1].Length++
|
||||
deltas = append(deltas, count-prevCount)
|
||||
prevCount = count
|
||||
}
|
||||
|
||||
for n, i := range ii {
|
||||
count := buckets[i]
|
||||
// Multiple spans with only small gaps in between are probably
|
||||
// encoded more efficiently as one larger span with a few empty
|
||||
// buckets. Needs some research to find the sweet spot. For now,
|
||||
// we assume that gaps of one or two buckets should not create
|
||||
// a new span.
|
||||
iDelta := int32(i - nextI)
|
||||
if n == 0 || iDelta > 2 {
|
||||
// We have to create a new span, either because we are
|
||||
// at the very beginning, or because we have found a gap
|
||||
// of more than two buckets.
|
||||
spans = append(spans, &dto.BucketSpan{
|
||||
Offset: proto.Int32(iDelta),
|
||||
Length: proto.Uint32(0),
|
||||
})
|
||||
} else {
|
||||
// We have found a small gap (or no gap at all).
|
||||
// Insert empty buckets as needed.
|
||||
for j := int32(0); j < iDelta; j++ {
|
||||
appendDelta(0)
|
||||
}
|
||||
}
|
||||
appendDelta(count)
|
||||
nextI = i + 1
|
||||
}
|
||||
return spans, deltas
|
||||
}
|
||||
|
||||
25
vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go
generated
vendored
25
vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go
generated
vendored
@@ -14,7 +14,7 @@
|
||||
// It provides tools to compare sequences of strings and generate textual diffs.
|
||||
//
|
||||
// Maintaining `GetUnifiedDiffString` here because original repository
|
||||
// (https://github.com/pmezard/go-difflib) is no loger maintained.
|
||||
// (https://github.com/pmezard/go-difflib) is no longer maintained.
|
||||
package internal
|
||||
|
||||
import (
|
||||
@@ -22,17 +22,18 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func min(a, b int) int {
|
||||
func minInt(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
func maxInt(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
@@ -427,12 +428,12 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
|
||||
if codes[0].Tag == 'e' {
|
||||
c := codes[0]
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
|
||||
codes[0] = OpCode{c.Tag, maxInt(i1, i2-n), i2, maxInt(j1, j2-n), j2}
|
||||
}
|
||||
if codes[len(codes)-1].Tag == 'e' {
|
||||
c := codes[len(codes)-1]
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
|
||||
codes[len(codes)-1] = OpCode{c.Tag, i1, minInt(i2, i1+n), j1, minInt(j2, j1+n)}
|
||||
}
|
||||
nn := n + n
|
||||
groups := [][]OpCode{}
|
||||
@@ -443,16 +444,16 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
|
||||
// there is a large range with no changes.
|
||||
if c.Tag == 'e' && i2-i1 > nn {
|
||||
group = append(group, OpCode{
|
||||
c.Tag, i1, min(i2, i1+n),
|
||||
j1, min(j2, j1+n),
|
||||
c.Tag, i1, minInt(i2, i1+n),
|
||||
j1, minInt(j2, j1+n),
|
||||
})
|
||||
groups = append(groups, group)
|
||||
group = []OpCode{}
|
||||
i1, j1 = max(i1, i2-n), max(j1, j2-n)
|
||||
i1, j1 = maxInt(i1, i2-n), maxInt(j1, j2-n)
|
||||
}
|
||||
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
|
||||
}
|
||||
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
|
||||
if len(group) > 0 && (len(group) != 1 || group[0].Tag != 'e') {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
return groups
|
||||
@@ -515,7 +516,7 @@ func (m *SequenceMatcher) QuickRatio() float64 {
|
||||
// is faster to compute than either .Ratio() or .QuickRatio().
|
||||
func (m *SequenceMatcher) RealQuickRatio() float64 {
|
||||
la, lb := len(m.a), len(m.b)
|
||||
return calculateRatio(min(la, lb), la+lb)
|
||||
return calculateRatio(minInt(la, lb), la+lb)
|
||||
}
|
||||
|
||||
// Convert range to the "ed" format
|
||||
@@ -524,7 +525,7 @@ func formatRangeUnified(start, stop int) string {
|
||||
beginning := start + 1 // lines start numbering with one
|
||||
length := stop - start
|
||||
if length == 1 {
|
||||
return fmt.Sprintf("%d", beginning)
|
||||
return strconv.Itoa(beginning)
|
||||
}
|
||||
if length == 0 {
|
||||
beginning-- // empty ranges begin at line just before the range
|
||||
@@ -567,7 +568,7 @@ func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
||||
buf := bufio.NewWriter(writer)
|
||||
defer buf.Flush()
|
||||
wf := func(format string, args ...interface{}) error {
|
||||
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||
_, err := fmt.Fprintf(buf, format, args...)
|
||||
return err
|
||||
}
|
||||
ws := func(s string) error {
|
||||
|
||||
@@ -30,3 +30,5 @@ type GoCollectorOptions struct {
|
||||
RuntimeMetricSumForHist map[string]string
|
||||
RuntimeMetricRules []GoCollectorRule
|
||||
}
|
||||
|
||||
var GoCollectorDefaultRuntimeMetrics = regexp.MustCompile(`/gc/gogc:percent|/gc/gomemlimit:bytes|/sched/gomaxprocs:threads`)
|
||||
|
||||
3
vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go
generated
vendored
3
vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go
generated
vendored
@@ -66,7 +66,8 @@ func RuntimeMetricsToProm(d *metrics.Description) (string, string, string, bool)
|
||||
name += "_total"
|
||||
}
|
||||
|
||||
valid := model.IsValidMetricName(model.LabelValue(namespace + "_" + subsystem + "_" + name))
|
||||
// Our current conversion moves to legacy naming, so use legacy validation.
|
||||
valid := model.LegacyValidation.IsValidMetricName(namespace + "_" + subsystem + "_" + name)
|
||||
switch d.Kind {
|
||||
case metrics.KindUint64:
|
||||
case metrics.KindFloat64:
|
||||
|
||||
63
vendor/github.com/prometheus/client_golang/prometheus/labels.go
generated
vendored
63
vendor/github.com/prometheus/client_golang/prometheus/labels.go
generated
vendored
@@ -32,19 +32,15 @@ import (
|
||||
// create a Desc.
|
||||
type Labels map[string]string
|
||||
|
||||
// LabelConstraint normalizes label values.
|
||||
type LabelConstraint func(string) string
|
||||
|
||||
// ConstrainedLabels represents a label name and its constrain function
|
||||
// to normalize label values. This type is commonly used when constructing
|
||||
// metric vector Collectors.
|
||||
type ConstrainedLabel struct {
|
||||
Name string
|
||||
Constraint func(string) string
|
||||
}
|
||||
|
||||
func (cl ConstrainedLabel) Constrain(v string) string {
|
||||
if cl.Constraint == nil {
|
||||
return v
|
||||
}
|
||||
return cl.Constraint(v)
|
||||
Constraint LabelConstraint
|
||||
}
|
||||
|
||||
// ConstrainableLabels is an interface that allows creating of labels that can
|
||||
@@ -58,7 +54,7 @@ func (cl ConstrainedLabel) Constrain(v string) string {
|
||||
// },
|
||||
// })
|
||||
type ConstrainableLabels interface {
|
||||
constrainedLabels() ConstrainedLabels
|
||||
compile() *compiledLabels
|
||||
labelNames() []string
|
||||
}
|
||||
|
||||
@@ -67,8 +63,20 @@ type ConstrainableLabels interface {
|
||||
// metric vector Collectors.
|
||||
type ConstrainedLabels []ConstrainedLabel
|
||||
|
||||
func (cls ConstrainedLabels) constrainedLabels() ConstrainedLabels {
|
||||
return cls
|
||||
func (cls ConstrainedLabels) compile() *compiledLabels {
|
||||
compiled := &compiledLabels{
|
||||
names: make([]string, len(cls)),
|
||||
labelConstraints: map[string]LabelConstraint{},
|
||||
}
|
||||
|
||||
for i, label := range cls {
|
||||
compiled.names[i] = label.Name
|
||||
if label.Constraint != nil {
|
||||
compiled.labelConstraints[label.Name] = label.Constraint
|
||||
}
|
||||
}
|
||||
|
||||
return compiled
|
||||
}
|
||||
|
||||
func (cls ConstrainedLabels) labelNames() []string {
|
||||
@@ -92,18 +100,36 @@ func (cls ConstrainedLabels) labelNames() []string {
|
||||
// }
|
||||
type UnconstrainedLabels []string
|
||||
|
||||
func (uls UnconstrainedLabels) constrainedLabels() ConstrainedLabels {
|
||||
constrainedLabels := make([]ConstrainedLabel, len(uls))
|
||||
for i, l := range uls {
|
||||
constrainedLabels[i] = ConstrainedLabel{Name: l}
|
||||
func (uls UnconstrainedLabels) compile() *compiledLabels {
|
||||
return &compiledLabels{
|
||||
names: uls,
|
||||
}
|
||||
return constrainedLabels
|
||||
}
|
||||
|
||||
func (uls UnconstrainedLabels) labelNames() []string {
|
||||
return uls
|
||||
}
|
||||
|
||||
type compiledLabels struct {
|
||||
names []string
|
||||
labelConstraints map[string]LabelConstraint
|
||||
}
|
||||
|
||||
func (cls *compiledLabels) compile() *compiledLabels {
|
||||
return cls
|
||||
}
|
||||
|
||||
func (cls *compiledLabels) labelNames() []string {
|
||||
return cls.names
|
||||
}
|
||||
|
||||
func (cls *compiledLabels) constrain(labelName, value string) string {
|
||||
if fn, ok := cls.labelConstraints[labelName]; ok && fn != nil {
|
||||
return fn(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||
// label names.
|
||||
const reservedLabelPrefix = "__"
|
||||
@@ -139,6 +165,8 @@ func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
|
||||
|
||||
func validateLabelValues(vals []string, expectedNumberOfValues int) error {
|
||||
if len(vals) != expectedNumberOfValues {
|
||||
// The call below makes vals escape, copy them to avoid that.
|
||||
vals := append([]string(nil), vals...)
|
||||
return fmt.Errorf(
|
||||
"%w: expected %d label values but got %d in %#v",
|
||||
errInconsistentCardinality, expectedNumberOfValues,
|
||||
@@ -156,5 +184,6 @@ func validateLabelValues(vals []string, expectedNumberOfValues int) error {
|
||||
}
|
||||
|
||||
func checkLabelName(l string) bool {
|
||||
return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix)
|
||||
//nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme.
|
||||
return model.NameValidationScheme.IsValidLabelName(l) && !strings.HasPrefix(l, reservedLabelPrefix)
|
||||
}
|
||||
|
||||
54
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
54
vendor/github.com/prometheus/client_golang/prometheus/metric.go
generated
vendored
@@ -92,6 +92,9 @@ type Opts struct {
|
||||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// BuildFQName joins the given three name components by "_". Empty name
|
||||
@@ -105,15 +108,23 @@ func BuildFQName(namespace, subsystem, name string) string {
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
switch {
|
||||
case namespace != "" && subsystem != "":
|
||||
return strings.Join([]string{namespace, subsystem, name}, "_")
|
||||
case namespace != "":
|
||||
return strings.Join([]string{namespace, name}, "_")
|
||||
case subsystem != "":
|
||||
return strings.Join([]string{subsystem, name}, "_")
|
||||
|
||||
sb := strings.Builder{}
|
||||
sb.Grow(len(namespace) + len(subsystem) + len(name) + 2)
|
||||
|
||||
if namespace != "" {
|
||||
sb.WriteString(namespace)
|
||||
sb.WriteString("_")
|
||||
}
|
||||
return name
|
||||
|
||||
if subsystem != "" {
|
||||
sb.WriteString(subsystem)
|
||||
sb.WriteString("_")
|
||||
}
|
||||
|
||||
sb.WriteString(name)
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
type invalidMetric struct {
|
||||
@@ -175,21 +186,31 @@ func (m *withExemplarsMetric) Write(pb *dto.Metric) error {
|
||||
case pb.Counter != nil:
|
||||
pb.Counter.Exemplar = m.exemplars[len(m.exemplars)-1]
|
||||
case pb.Histogram != nil:
|
||||
h := pb.Histogram
|
||||
for _, e := range m.exemplars {
|
||||
// pb.Histogram.Bucket are sorted by UpperBound.
|
||||
i := sort.Search(len(pb.Histogram.Bucket), func(i int) bool {
|
||||
return pb.Histogram.Bucket[i].GetUpperBound() >= e.GetValue()
|
||||
if (h.GetZeroThreshold() != 0 || h.GetZeroCount() != 0 ||
|
||||
len(h.PositiveSpan) != 0 || len(h.NegativeSpan) != 0) &&
|
||||
e.GetTimestamp() != nil {
|
||||
h.Exemplars = append(h.Exemplars, e)
|
||||
if len(h.Bucket) == 0 {
|
||||
// Don't proceed to classic buckets if there are none.
|
||||
continue
|
||||
}
|
||||
}
|
||||
// h.Bucket are sorted by UpperBound.
|
||||
i := sort.Search(len(h.Bucket), func(i int) bool {
|
||||
return h.Bucket[i].GetUpperBound() >= e.GetValue()
|
||||
})
|
||||
if i < len(pb.Histogram.Bucket) {
|
||||
pb.Histogram.Bucket[i].Exemplar = e
|
||||
if i < len(h.Bucket) {
|
||||
h.Bucket[i].Exemplar = e
|
||||
} else {
|
||||
// The +Inf bucket should be explicitly added if there is an exemplar for it, similar to non-const histogram logic in https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go#L357-L365.
|
||||
b := &dto.Bucket{
|
||||
CumulativeCount: proto.Uint64(pb.Histogram.GetSampleCount()),
|
||||
CumulativeCount: proto.Uint64(h.GetSampleCount()),
|
||||
UpperBound: proto.Float64(math.Inf(1)),
|
||||
Exemplar: e,
|
||||
}
|
||||
pb.Histogram.Bucket = append(pb.Histogram.Bucket, b)
|
||||
h.Bucket = append(h.Bucket, b)
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -216,6 +237,7 @@ type Exemplar struct {
|
||||
// Only last applicable exemplar is injected from the list.
|
||||
// For example for Counter it means last exemplar is injected.
|
||||
// For Histogram, it means last applicable exemplar for each bucket is injected.
|
||||
// For a Native Histogram, all valid exemplars are injected.
|
||||
//
|
||||
// NewMetricWithExemplars works best with MustNewConstMetric and
|
||||
// MustNewConstHistogram, see example.
|
||||
@@ -231,7 +253,7 @@ func NewMetricWithExemplars(m Metric, exemplars ...Exemplar) (Metric, error) {
|
||||
)
|
||||
for i, e := range exemplars {
|
||||
ts := e.Timestamp
|
||||
if ts == (time.Time{}) {
|
||||
if ts.IsZero() {
|
||||
ts = now
|
||||
}
|
||||
exs[i], err = newExemplar(e.Value, ts, e.Labels)
|
||||
|
||||
56
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
56
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
@@ -22,14 +22,16 @@ import (
|
||||
)
|
||||
|
||||
type processCollector struct {
|
||||
collectFn func(chan<- Metric)
|
||||
pidFn func() (int, error)
|
||||
reportErrors bool
|
||||
cpuTotal *Desc
|
||||
openFDs, maxFDs *Desc
|
||||
vsize, maxVsize *Desc
|
||||
rss *Desc
|
||||
startTime *Desc
|
||||
collectFn func(chan<- Metric)
|
||||
describeFn func(chan<- *Desc)
|
||||
pidFn func() (int, error)
|
||||
reportErrors bool
|
||||
cpuTotal *Desc
|
||||
openFDs, maxFDs *Desc
|
||||
vsize, maxVsize *Desc
|
||||
rss *Desc
|
||||
startTime *Desc
|
||||
inBytes, outBytes *Desc
|
||||
}
|
||||
|
||||
// ProcessCollectorOpts defines the behavior of a process metrics collector
|
||||
@@ -100,6 +102,16 @@ func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
||||
"Start time of the process since unix epoch in seconds.",
|
||||
nil, nil,
|
||||
),
|
||||
inBytes: NewDesc(
|
||||
ns+"process_network_receive_bytes_total",
|
||||
"Number of bytes received by the process over the network.",
|
||||
nil, nil,
|
||||
),
|
||||
outBytes: NewDesc(
|
||||
ns+"process_network_transmit_bytes_total",
|
||||
"Number of bytes sent by the process over the network.",
|
||||
nil, nil,
|
||||
),
|
||||
}
|
||||
|
||||
if opts.PidFn == nil {
|
||||
@@ -111,24 +123,23 @@ func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
||||
// Set up process metric collection if supported by the runtime.
|
||||
if canCollectProcess() {
|
||||
c.collectFn = c.processCollect
|
||||
c.describeFn = c.describe
|
||||
} else {
|
||||
c.collectFn = func(ch chan<- Metric) {
|
||||
c.reportError(ch, nil, errors.New("process metrics not supported on this platform"))
|
||||
}
|
||||
c.collectFn = c.errorCollectFn
|
||||
c.describeFn = c.errorDescribeFn
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Describe returns all descriptions of the collector.
|
||||
func (c *processCollector) Describe(ch chan<- *Desc) {
|
||||
ch <- c.cpuTotal
|
||||
ch <- c.openFDs
|
||||
ch <- c.maxFDs
|
||||
ch <- c.vsize
|
||||
ch <- c.maxVsize
|
||||
ch <- c.rss
|
||||
ch <- c.startTime
|
||||
func (c *processCollector) errorCollectFn(ch chan<- Metric) {
|
||||
c.reportError(ch, nil, errors.New("process metrics not supported on this platform"))
|
||||
}
|
||||
|
||||
func (c *processCollector) errorDescribeFn(ch chan<- *Desc) {
|
||||
if c.reportErrors {
|
||||
ch <- NewInvalidDesc(errors.New("process metrics not supported on this platform"))
|
||||
}
|
||||
}
|
||||
|
||||
// Collect returns the current state of all metrics of the collector.
|
||||
@@ -136,6 +147,11 @@ func (c *processCollector) Collect(ch chan<- Metric) {
|
||||
c.collectFn(ch)
|
||||
}
|
||||
|
||||
// Describe returns all descriptions of the collector.
|
||||
func (c *processCollector) Describe(ch chan<- *Desc) {
|
||||
c.describeFn(ch)
|
||||
}
|
||||
|
||||
func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
|
||||
if !c.reportErrors {
|
||||
return
|
||||
|
||||
130
vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go
generated
vendored
Normal file
130
vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build darwin && !ios
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// errNotImplemented is returned by stub functions that replace cgo functions, when cgo
|
||||
// isn't available.
|
||||
var errNotImplemented = errors.New("not implemented")
|
||||
|
||||
type memoryInfo struct {
|
||||
vsize uint64 // Virtual memory size in bytes
|
||||
rss uint64 // Resident memory size in bytes
|
||||
}
|
||||
|
||||
func canCollectProcess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func getSoftLimit(which int) (uint64, error) {
|
||||
rlimit := syscall.Rlimit{}
|
||||
|
||||
if err := syscall.Getrlimit(which, &rlimit); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return rlimit.Cur, nil
|
||||
}
|
||||
|
||||
func getOpenFileCount() (float64, error) {
|
||||
// Alternately, the undocumented proc_pidinfo(PROC_PIDLISTFDS) can be used to
|
||||
// return a list of open fds, but that requires a way to call C APIs. The
|
||||
// benefits, however, include fewer system calls and not failing when at the
|
||||
// open file soft limit.
|
||||
|
||||
if dir, err := os.Open("/dev/fd"); err != nil {
|
||||
return 0.0, err
|
||||
} else {
|
||||
defer dir.Close()
|
||||
|
||||
// Avoid ReadDir(), as it calls stat(2) on each descriptor. Not only is
|
||||
// that info not used, but KQUEUE descriptors fail stat(2), which causes
|
||||
// the whole method to fail.
|
||||
if names, err := dir.Readdirnames(0); err != nil {
|
||||
return 0.0, err
|
||||
} else {
|
||||
// Subtract 1 to ignore the open /dev/fd descriptor above.
|
||||
return float64(len(names) - 1), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||
if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", os.Getpid()); err == nil {
|
||||
if len(procs) == 1 {
|
||||
startTime := float64(procs[0].Proc.P_starttime.Nano() / 1e9)
|
||||
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
|
||||
} else {
|
||||
err = fmt.Errorf("sysctl() returned %d proc structs (expected 1)", len(procs))
|
||||
c.reportError(ch, c.startTime, err)
|
||||
}
|
||||
} else {
|
||||
c.reportError(ch, c.startTime, err)
|
||||
}
|
||||
|
||||
// The proc structure returned by kern.proc.pid above has an Rusage member,
|
||||
// but it is not filled in, so it needs to be fetched by getrusage(2). For
|
||||
// that call, the UTime, STime, and Maxrss members are filled out, but not
|
||||
// Ixrss, Idrss, or Isrss for the memory usage. Memory stats will require
|
||||
// access to the C API to call task_info(TASK_BASIC_INFO).
|
||||
rusage := unix.Rusage{}
|
||||
|
||||
if err := unix.Getrusage(syscall.RUSAGE_SELF, &rusage); err == nil {
|
||||
cpuTime := time.Duration(rusage.Stime.Nano() + rusage.Utime.Nano()).Seconds()
|
||||
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, cpuTime)
|
||||
} else {
|
||||
c.reportError(ch, c.cpuTotal, err)
|
||||
}
|
||||
|
||||
if memInfo, err := getMemory(); err == nil {
|
||||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(memInfo.rss))
|
||||
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(memInfo.vsize))
|
||||
} else if !errors.Is(err, errNotImplemented) {
|
||||
// Don't report an error when support is not compiled in.
|
||||
c.reportError(ch, c.rss, err)
|
||||
c.reportError(ch, c.vsize, err)
|
||||
}
|
||||
|
||||
if fds, err := getOpenFileCount(); err == nil {
|
||||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, fds)
|
||||
} else {
|
||||
c.reportError(ch, c.openFDs, err)
|
||||
}
|
||||
|
||||
if openFiles, err := getSoftLimit(syscall.RLIMIT_NOFILE); err == nil {
|
||||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(openFiles))
|
||||
} else {
|
||||
c.reportError(ch, c.maxFDs, err)
|
||||
}
|
||||
|
||||
if addressSpace, err := getSoftLimit(syscall.RLIMIT_AS); err == nil {
|
||||
ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(addressSpace))
|
||||
} else {
|
||||
c.reportError(ch, c.maxVsize, err)
|
||||
}
|
||||
|
||||
// TODO: socket(PF_SYSTEM) to fetch "com.apple.network.statistics" might
|
||||
// be able to get the per-process network send/receive counts.
|
||||
}
|
||||
84
vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.c
generated
vendored
Normal file
84
vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.c
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build darwin && !ios && cgo
|
||||
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/mach_vm.h>
|
||||
|
||||
// The compiler warns that mach/shared_memory_server.h is deprecated, and to use
|
||||
// mach/shared_region.h instead. But that doesn't define
|
||||
// SHARED_DATA_REGION_SIZE or SHARED_TEXT_REGION_SIZE, so redefine them here and
|
||||
// avoid a warning message when running tests.
|
||||
#define GLOBAL_SHARED_TEXT_SEGMENT 0x90000000U
|
||||
#define SHARED_DATA_REGION_SIZE 0x10000000
|
||||
#define SHARED_TEXT_REGION_SIZE 0x10000000
|
||||
|
||||
|
||||
int get_memory_info(unsigned long long *rss, unsigned long long *vsize)
|
||||
{
|
||||
// This is lightly adapted from how ps(1) obtains its memory info.
|
||||
// https://github.com/apple-oss-distributions/adv_cmds/blob/8744084ea0ff41ca4bb96b0f9c22407d0e48e9b7/ps/tasks.c#L109
|
||||
|
||||
kern_return_t error;
|
||||
task_t task = MACH_PORT_NULL;
|
||||
mach_task_basic_info_data_t info;
|
||||
mach_msg_type_number_t info_count = MACH_TASK_BASIC_INFO_COUNT;
|
||||
|
||||
error = task_info(
|
||||
mach_task_self(),
|
||||
MACH_TASK_BASIC_INFO,
|
||||
(task_info_t) &info,
|
||||
&info_count );
|
||||
|
||||
if( error != KERN_SUCCESS )
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
*rss = info.resident_size;
|
||||
*vsize = info.virtual_size;
|
||||
|
||||
{
|
||||
vm_region_basic_info_data_64_t b_info;
|
||||
mach_vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT;
|
||||
mach_vm_size_t size;
|
||||
mach_port_t object_name;
|
||||
|
||||
/*
|
||||
* try to determine if this task has the split libraries
|
||||
* mapped in... if so, adjust its virtual size down by
|
||||
* the 2 segments that are used for split libraries
|
||||
*/
|
||||
info_count = VM_REGION_BASIC_INFO_COUNT_64;
|
||||
|
||||
error = mach_vm_region(
|
||||
mach_task_self(),
|
||||
&address,
|
||||
&size,
|
||||
VM_REGION_BASIC_INFO_64,
|
||||
(vm_region_info_t) &b_info,
|
||||
&info_count,
|
||||
&object_name);
|
||||
|
||||
if (error == KERN_SUCCESS) {
|
||||
if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) &&
|
||||
*vsize > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE)) {
|
||||
*vsize -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
51
vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.go
generated
vendored
Normal file
51
vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build darwin && !ios && cgo
|
||||
|
||||
package prometheus
|
||||
|
||||
/*
|
||||
int get_memory_info(unsigned long long *rss, unsigned long long *vs);
|
||||
*/
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
func getMemory() (*memoryInfo, error) {
|
||||
var rss, vsize C.ulonglong
|
||||
|
||||
if err := C.get_memory_info(&rss, &vsize); err != 0 {
|
||||
return nil, fmt.Errorf("task_info() failed with 0x%x", int(err))
|
||||
}
|
||||
|
||||
return &memoryInfo{vsize: uint64(vsize), rss: uint64(rss)}, nil
|
||||
}
|
||||
|
||||
// describe returns all descriptions of the collector for Darwin.
|
||||
// Ensure that this list of descriptors is kept in sync with the metrics collected
|
||||
// in the processCollect method. Any changes to the metrics in processCollect
|
||||
// (such as adding or removing metrics) should be reflected in this list of descriptors.
|
||||
func (c *processCollector) describe(ch chan<- *Desc) {
|
||||
ch <- c.cpuTotal
|
||||
ch <- c.openFDs
|
||||
ch <- c.maxFDs
|
||||
ch <- c.maxVsize
|
||||
ch <- c.startTime
|
||||
ch <- c.rss
|
||||
ch <- c.vsize
|
||||
|
||||
/* the process could be collected but not implemented yet
|
||||
ch <- c.inBytes
|
||||
ch <- c.outBytes
|
||||
*/
|
||||
}
|
||||
39
vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_nocgo_darwin.go
generated
vendored
Normal file
39
vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_nocgo_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build darwin && !ios && !cgo
|
||||
|
||||
package prometheus
|
||||
|
||||
func getMemory() (*memoryInfo, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// describe returns all descriptions of the collector for Darwin.
|
||||
// Ensure that this list of descriptors is kept in sync with the metrics collected
|
||||
// in the processCollect method. Any changes to the metrics in processCollect
|
||||
// (such as adding or removing metrics) should be reflected in this list of descriptors.
|
||||
func (c *processCollector) describe(ch chan<- *Desc) {
|
||||
ch <- c.cpuTotal
|
||||
ch <- c.openFDs
|
||||
ch <- c.maxFDs
|
||||
ch <- c.maxVsize
|
||||
ch <- c.startTime
|
||||
|
||||
/* the process could be collected but not implemented yet
|
||||
ch <- c.rss
|
||||
ch <- c.vsize
|
||||
ch <- c.inBytes
|
||||
ch <- c.outBytes
|
||||
*/
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Copyright 2023 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
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build js
|
||||
// +build js
|
||||
//go:build wasip1 || js || ios
|
||||
// +build wasip1 js ios
|
||||
|
||||
package prometheus
|
||||
|
||||
@@ -21,6 +21,13 @@ func canCollectProcess() bool {
|
||||
}
|
||||
|
||||
func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||
// noop on this platform
|
||||
return
|
||||
c.errorCollectFn(ch)
|
||||
}
|
||||
|
||||
// describe returns all descriptions of the collector for wasip1 and js.
|
||||
// Ensure that this list of descriptors is kept in sync with the metrics collected
|
||||
// in the processCollect method. Any changes to the metrics in processCollect
|
||||
// (such as adding or removing metrics) should be reflected in this list of descriptors.
|
||||
func (c *processCollector) describe(ch chan<- *Desc) {
|
||||
c.errorDescribeFn(ch)
|
||||
}
|
||||
@@ -11,8 +11,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows && !js
|
||||
// +build !windows,!js
|
||||
//go:build !windows && !js && !wasip1 && !darwin
|
||||
// +build !windows,!js,!wasip1,!darwin
|
||||
|
||||
package prometheus
|
||||
|
||||
@@ -63,4 +63,34 @@ func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||
} else {
|
||||
c.reportError(ch, nil, err)
|
||||
}
|
||||
|
||||
if netstat, err := p.Netstat(); err == nil {
|
||||
var inOctets, outOctets float64
|
||||
if netstat.InOctets != nil {
|
||||
inOctets = *netstat.InOctets
|
||||
}
|
||||
if netstat.OutOctets != nil {
|
||||
outOctets = *netstat.OutOctets
|
||||
}
|
||||
ch <- MustNewConstMetric(c.inBytes, CounterValue, inOctets)
|
||||
ch <- MustNewConstMetric(c.outBytes, CounterValue, outOctets)
|
||||
} else {
|
||||
c.reportError(ch, nil, err)
|
||||
}
|
||||
}
|
||||
|
||||
// describe returns all descriptions of the collector for others than windows, js, wasip1 and darwin.
|
||||
// Ensure that this list of descriptors is kept in sync with the metrics collected
|
||||
// in the processCollect method. Any changes to the metrics in processCollect
|
||||
// (such as adding or removing metrics) should be reflected in this list of descriptors.
|
||||
func (c *processCollector) describe(ch chan<- *Desc) {
|
||||
ch <- c.cpuTotal
|
||||
ch <- c.openFDs
|
||||
ch <- c.maxFDs
|
||||
ch <- c.vsize
|
||||
ch <- c.maxVsize
|
||||
ch <- c.rss
|
||||
ch <- c.startTime
|
||||
ch <- c.inBytes
|
||||
ch <- c.outBytes
|
||||
}
|
||||
21
vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go
generated
vendored
21
vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go
generated
vendored
@@ -79,14 +79,10 @@ func getProcessHandleCount(handle windows.Handle) (uint32, error) {
|
||||
}
|
||||
|
||||
func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||
h, err := windows.GetCurrentProcess()
|
||||
if err != nil {
|
||||
c.reportError(ch, nil, err)
|
||||
return
|
||||
}
|
||||
h := windows.CurrentProcess()
|
||||
|
||||
var startTime, exitTime, kernelTime, userTime windows.Filetime
|
||||
err = windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime)
|
||||
err := windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime)
|
||||
if err != nil {
|
||||
c.reportError(ch, nil, err)
|
||||
return
|
||||
@@ -111,6 +107,19 @@ func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(16*1024*1024)) // Windows has a hard-coded max limit, not per-process.
|
||||
}
|
||||
|
||||
// describe returns all descriptions of the collector for windows.
|
||||
// Ensure that this list of descriptors is kept in sync with the metrics collected
|
||||
// in the processCollect method. Any changes to the metrics in processCollect
|
||||
// (such as adding or removing metrics) should be reflected in this list of descriptors.
|
||||
func (c *processCollector) describe(ch chan<- *Desc) {
|
||||
ch <- c.cpuTotal
|
||||
ch <- c.openFDs
|
||||
ch <- c.maxFDs
|
||||
ch <- c.vsize
|
||||
ch <- c.rss
|
||||
ch <- c.startTime
|
||||
}
|
||||
|
||||
func fileTimeToSeconds(ft windows.Filetime) float64 {
|
||||
return float64(uint64(ft.HighDateTime)<<32+uint64(ft.LowDateTime)) / 1e7
|
||||
}
|
||||
|
||||
25
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
25
vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
@@ -314,16 +314,17 @@ func (r *Registry) Register(c Collector) error {
|
||||
if dimHash != desc.dimHash {
|
||||
return fmt.Errorf("a previously registered descriptor with the same fully-qualified name as %s has different label names or a different help string", desc)
|
||||
}
|
||||
} else {
|
||||
// ...then check the new descriptors already seen.
|
||||
if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
|
||||
if dimHash != desc.dimHash {
|
||||
return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
|
||||
}
|
||||
} else {
|
||||
newDimHashesByName[desc.fqName] = desc.dimHash
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// ...then check the new descriptors already seen.
|
||||
if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
|
||||
if dimHash != desc.dimHash {
|
||||
return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
|
||||
}
|
||||
continue
|
||||
}
|
||||
newDimHashesByName[desc.fqName] = desc.dimHash
|
||||
}
|
||||
// A Collector yielding no Desc at all is considered unchecked.
|
||||
if len(newDescIDs) == 0 {
|
||||
@@ -548,7 +549,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||
goroutineBudget--
|
||||
runtime.Gosched()
|
||||
}
|
||||
// Once both checkedMetricChan and uncheckdMetricChan are closed
|
||||
// Once both checkedMetricChan and uncheckedMetricChan are closed
|
||||
// and drained, the contraption above will nil out cmc and umc,
|
||||
// and then we can leave the collect loop here.
|
||||
if cmc == nil && umc == nil {
|
||||
@@ -963,9 +964,9 @@ func checkDescConsistency(
|
||||
// Is the desc consistent with the content of the metric?
|
||||
lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
|
||||
copy(lpsFromDesc, desc.constLabelPairs)
|
||||
for _, l := range desc.variableLabels {
|
||||
for _, l := range desc.variableLabels.names {
|
||||
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
||||
Name: proto.String(l.Name),
|
||||
Name: proto.String(l),
|
||||
})
|
||||
}
|
||||
if len(lpsFromDesc) != len(dtoMetric.Label) {
|
||||
|
||||
90
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
90
vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
@@ -26,6 +26,7 @@ import (
|
||||
|
||||
"github.com/beorn7/perks/quantile"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// quantileLabel is used for the label that defines the quantile in a
|
||||
@@ -145,6 +146,9 @@ type SummaryOpts struct {
|
||||
// is the internal buffer size of the underlying package
|
||||
// "github.com/bmizerany/perks/quantile").
|
||||
BufCap uint32
|
||||
|
||||
// now is for testing purposes, by default it's time.Now.
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// SummaryVecOpts bundles the options to create a SummaryVec metric.
|
||||
@@ -154,7 +158,7 @@ type SummaryVecOpts struct {
|
||||
SummaryOpts
|
||||
|
||||
// VariableLabels are used to partition the metric vector by the given set
|
||||
// of labels. Each label value will be constrained with the optional Contraint
|
||||
// of labels. Each label value will be constrained with the optional Constraint
|
||||
// function, if provided.
|
||||
VariableLabels ConstrainableLabels
|
||||
}
|
||||
@@ -188,12 +192,12 @@ func NewSummary(opts SummaryOpts) Summary {
|
||||
}
|
||||
|
||||
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
if len(desc.variableLabels) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
|
||||
if len(desc.variableLabels.names) != len(labelValues) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
|
||||
}
|
||||
|
||||
for _, n := range desc.variableLabels {
|
||||
if n.Name == quantileLabel {
|
||||
for _, n := range desc.variableLabels.names {
|
||||
if n == quantileLabel {
|
||||
panic(errQuantileLabelNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -222,6 +226,9 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
opts.BufCap = DefBufCap
|
||||
}
|
||||
|
||||
if opts.now == nil {
|
||||
opts.now = time.Now
|
||||
}
|
||||
if len(opts.Objectives) == 0 {
|
||||
// Use the lock-free implementation of a Summary without objectives.
|
||||
s := &noObjectivesSummary{
|
||||
@@ -230,11 +237,13 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
counts: [2]*summaryCounts{{}, {}},
|
||||
}
|
||||
s.init(s) // Init self-collection.
|
||||
s.createdTs = timestamppb.New(opts.now())
|
||||
return s
|
||||
}
|
||||
|
||||
s := &summary{
|
||||
desc: desc,
|
||||
now: opts.now,
|
||||
|
||||
objectives: opts.Objectives,
|
||||
sortedObjectives: make([]float64, 0, len(opts.Objectives)),
|
||||
@@ -245,7 +254,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
coldBuf: make([]float64, 0, opts.BufCap),
|
||||
streamDuration: opts.MaxAge / time.Duration(opts.AgeBuckets),
|
||||
}
|
||||
s.headStreamExpTime = time.Now().Add(s.streamDuration)
|
||||
s.headStreamExpTime = opts.now().Add(s.streamDuration)
|
||||
s.hotBufExpTime = s.headStreamExpTime
|
||||
|
||||
for i := uint32(0); i < opts.AgeBuckets; i++ {
|
||||
@@ -259,6 +268,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
sort.Float64s(s.sortedObjectives)
|
||||
|
||||
s.init(s) // Init self-collection.
|
||||
s.createdTs = timestamppb.New(opts.now())
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -271,6 +281,8 @@ type summary struct {
|
||||
|
||||
desc *Desc
|
||||
|
||||
now func() time.Time
|
||||
|
||||
objectives map[float64]float64
|
||||
sortedObjectives []float64
|
||||
|
||||
@@ -286,6 +298,8 @@ type summary struct {
|
||||
headStream *quantile.Stream
|
||||
headStreamIdx int
|
||||
headStreamExpTime, hotBufExpTime time.Time
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *summary) Desc() *Desc {
|
||||
@@ -296,7 +310,7 @@ func (s *summary) Observe(v float64) {
|
||||
s.bufMtx.Lock()
|
||||
defer s.bufMtx.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
now := s.now()
|
||||
if now.After(s.hotBufExpTime) {
|
||||
s.asyncFlush(now)
|
||||
}
|
||||
@@ -307,13 +321,15 @@ func (s *summary) Observe(v float64) {
|
||||
}
|
||||
|
||||
func (s *summary) Write(out *dto.Metric) error {
|
||||
sum := &dto.Summary{}
|
||||
sum := &dto.Summary{
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
qs := make([]*dto.Quantile, 0, len(s.objectives))
|
||||
|
||||
s.bufMtx.Lock()
|
||||
s.mtx.Lock()
|
||||
// Swap bufs even if hotBuf is empty to set new hotBufExpTime.
|
||||
s.swapBufs(time.Now())
|
||||
s.swapBufs(s.now())
|
||||
s.bufMtx.Unlock()
|
||||
|
||||
s.flushColdBuf()
|
||||
@@ -440,6 +456,8 @@ type noObjectivesSummary struct {
|
||||
counts [2]*summaryCounts
|
||||
|
||||
labelPairs []*dto.LabelPair
|
||||
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *noObjectivesSummary) Desc() *Desc {
|
||||
@@ -490,8 +508,9 @@ func (s *noObjectivesSummary) Write(out *dto.Metric) error {
|
||||
}
|
||||
|
||||
sum := &dto.Summary{
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
SampleCount: proto.Uint64(count),
|
||||
SampleSum: proto.Float64(math.Float64frombits(atomic.LoadUint64(&coldCounts.sumBits))),
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
|
||||
out.Summary = sum
|
||||
@@ -681,6 +700,7 @@ type constSummary struct {
|
||||
sum float64
|
||||
quantiles map[float64]float64
|
||||
labelPairs []*dto.LabelPair
|
||||
createdTs *timestamppb.Timestamp
|
||||
}
|
||||
|
||||
func (s *constSummary) Desc() *Desc {
|
||||
@@ -688,7 +708,9 @@ func (s *constSummary) Desc() *Desc {
|
||||
}
|
||||
|
||||
func (s *constSummary) Write(out *dto.Metric) error {
|
||||
sum := &dto.Summary{}
|
||||
sum := &dto.Summary{
|
||||
CreatedTimestamp: s.createdTs,
|
||||
}
|
||||
qs := make([]*dto.Quantile, 0, len(s.quantiles))
|
||||
|
||||
sum.SampleCount = proto.Uint64(s.count)
|
||||
@@ -737,7 +759,7 @@ func NewConstSummary(
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &constSummary{
|
||||
@@ -764,3 +786,45 @@ func MustNewConstSummary(
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewConstSummaryWithCreatedTimestamp does the same thing as NewConstSummary but sets the created timestamp.
|
||||
func NewConstSummaryWithCreatedTimestamp(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
sum float64,
|
||||
quantiles map[float64]float64,
|
||||
ct time.Time,
|
||||
labelValues ...string,
|
||||
) (Metric, error) {
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &constSummary{
|
||||
desc: desc,
|
||||
count: count,
|
||||
sum: sum,
|
||||
quantiles: quantiles,
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
createdTs: timestamppb.New(ct),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustNewConstSummaryWithCreatedTimestamp is a version of NewConstSummaryWithCreatedTimestamp that panics where
|
||||
// NewConstSummaryWithCreatedTimestamp would have returned an error.
|
||||
func MustNewConstSummaryWithCreatedTimestamp(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
sum float64,
|
||||
quantiles map[float64]float64,
|
||||
ct time.Time,
|
||||
labelValues ...string,
|
||||
) Metric {
|
||||
m, err := NewConstSummaryWithCreatedTimestamp(desc, count, sum, quantiles, ct, labelValues...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
55
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
55
vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
@@ -14,6 +14,7 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -91,7 +92,7 @@ func (v *valueFunc) Desc() *Desc {
|
||||
}
|
||||
|
||||
func (v *valueFunc) Write(out *dto.Metric) error {
|
||||
return populateMetric(v.valType, v.function(), v.labelPairs, nil, out)
|
||||
return populateMetric(v.valType, v.function(), v.labelPairs, nil, out, nil)
|
||||
}
|
||||
|
||||
// NewConstMetric returns a metric with one fixed value that cannot be
|
||||
@@ -105,12 +106,12 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
metric := &dto.Metric{}
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric); err != nil {
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -130,6 +131,43 @@ func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelVal
|
||||
return m
|
||||
}
|
||||
|
||||
// NewConstMetricWithCreatedTimestamp does the same thing as NewConstMetric, but generates Counters
|
||||
// with created timestamp set and returns an error for other metric types.
|
||||
func NewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) (Metric, error) {
|
||||
if desc.err != nil {
|
||||
return nil, desc.err
|
||||
}
|
||||
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch valueType {
|
||||
case CounterValue:
|
||||
break
|
||||
default:
|
||||
return nil, errors.New("created timestamps are only supported for counters")
|
||||
}
|
||||
|
||||
metric := &dto.Metric{}
|
||||
if err := populateMetric(valueType, value, MakeLabelPairs(desc, labelValues), nil, metric, timestamppb.New(ct)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &constMetric{
|
||||
desc: desc,
|
||||
metric: metric,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustNewConstMetricWithCreatedTimestamp is a version of NewConstMetricWithCreatedTimestamp that panics where
|
||||
// NewConstMetricWithCreatedTimestamp would have returned an error.
|
||||
func MustNewConstMetricWithCreatedTimestamp(desc *Desc, valueType ValueType, value float64, ct time.Time, labelValues ...string) Metric {
|
||||
m, err := NewConstMetricWithCreatedTimestamp(desc, valueType, value, ct, labelValues...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type constMetric struct {
|
||||
desc *Desc
|
||||
metric *dto.Metric
|
||||
@@ -153,11 +191,12 @@ func populateMetric(
|
||||
labelPairs []*dto.LabelPair,
|
||||
e *dto.Exemplar,
|
||||
m *dto.Metric,
|
||||
ct *timestamppb.Timestamp,
|
||||
) error {
|
||||
m.Label = labelPairs
|
||||
switch t {
|
||||
case CounterValue:
|
||||
m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e}
|
||||
m.Counter = &dto.Counter{Value: proto.Float64(v), Exemplar: e, CreatedTimestamp: ct}
|
||||
case GaugeValue:
|
||||
m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
|
||||
case UntypedValue:
|
||||
@@ -176,19 +215,19 @@ func populateMetric(
|
||||
// This function is only needed for custom Metric implementations. See MetricVec
|
||||
// example.
|
||||
func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
|
||||
totalLen := len(desc.variableLabels.names) + len(desc.constLabelPairs)
|
||||
if totalLen == 0 {
|
||||
// Super fast path.
|
||||
return nil
|
||||
}
|
||||
if len(desc.variableLabels) == 0 {
|
||||
if len(desc.variableLabels.names) == 0 {
|
||||
// Moderately fast path.
|
||||
return desc.constLabelPairs
|
||||
}
|
||||
labelPairs := make([]*dto.LabelPair, 0, totalLen)
|
||||
for i, l := range desc.variableLabels {
|
||||
for i, l := range desc.variableLabels.names {
|
||||
labelPairs = append(labelPairs, &dto.LabelPair{
|
||||
Name: proto.String(l.Name),
|
||||
Name: proto.String(l),
|
||||
Value: proto.String(labelValues[i]),
|
||||
})
|
||||
}
|
||||
|
||||
103
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
103
vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
@@ -73,12 +73,13 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
||||
// See also the CounterVec example.
|
||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
lvs = constrainLabelValues(m.desc, lvs, m.curry)
|
||||
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry)
|
||||
return m.deleteByHashWithLabelValues(h, lvs, m.curry)
|
||||
}
|
||||
|
||||
// Delete deletes the metric where the variable labels are the same as those
|
||||
@@ -92,13 +93,15 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
// 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 {
|
||||
labels = constrainLabels(m.desc, labels)
|
||||
labels, closer := constrainLabels(m.desc, labels)
|
||||
defer closer()
|
||||
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return m.metricMap.deleteByHashWithLabels(h, labels, m.curry)
|
||||
return m.deleteByHashWithLabels(h, labels, m.curry)
|
||||
}
|
||||
|
||||
// DeletePartialMatch deletes all metrics where the variable labels contain all of those
|
||||
@@ -108,8 +111,10 @@ func (m *MetricVec) Delete(labels Labels) bool {
|
||||
// Note that curried labels will never be matched if deleting from the curried vector.
|
||||
// To match curried labels with DeletePartialMatch, it must be called on the base vector.
|
||||
func (m *MetricVec) DeletePartialMatch(labels Labels) int {
|
||||
labels = constrainLabels(m.desc, labels)
|
||||
return m.metricMap.deleteByLabels(labels, m.curry)
|
||||
labels, closer := constrainLabels(m.desc, labels)
|
||||
defer closer()
|
||||
|
||||
return m.deleteByLabels(labels, m.curry)
|
||||
}
|
||||
|
||||
// Without explicit forwarding of Describe, Collect, Reset, those methods won't
|
||||
@@ -147,11 +152,11 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||
oldCurry = m.curry
|
||||
iCurry int
|
||||
)
|
||||
for i, label := range m.desc.variableLabels {
|
||||
val, ok := labels[label.Name]
|
||||
for i, labelName := range m.desc.variableLabels.names {
|
||||
val, ok := labels[labelName]
|
||||
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
|
||||
if ok {
|
||||
return nil, fmt.Errorf("label name %q is already curried", label.Name)
|
||||
return nil, fmt.Errorf("label name %q is already curried", labelName)
|
||||
}
|
||||
newCurry = append(newCurry, oldCurry[iCurry])
|
||||
iCurry++
|
||||
@@ -159,7 +164,10 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||
if !ok {
|
||||
continue // Label stays uncurried.
|
||||
}
|
||||
newCurry = append(newCurry, curriedLabelValue{i, label.Constrain(val)})
|
||||
newCurry = append(newCurry, curriedLabelValue{
|
||||
i,
|
||||
m.desc.variableLabels.constrain(labelName, val),
|
||||
})
|
||||
}
|
||||
}
|
||||
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
|
||||
@@ -208,7 +216,7 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
|
||||
return m.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Metric for the given Labels map (the label names
|
||||
@@ -228,17 +236,19 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
// around MetricVec, implementing a vector for a specific Metric implementation,
|
||||
// for example GaugeVec.
|
||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||
labels = constrainLabels(m.desc, labels)
|
||||
labels, closer := constrainLabels(m.desc, labels)
|
||||
defer closer()
|
||||
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
|
||||
return m.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 {
|
||||
if err := validateLabelValues(vals, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -247,7 +257,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
curry = m.curry
|
||||
iVals, iCurry int
|
||||
)
|
||||
for i := 0; i < len(m.desc.variableLabels); i++ {
|
||||
for i := 0; i < len(m.desc.variableLabels.names); i++ {
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
h = m.hashAdd(h, curry[iCurry].value)
|
||||
iCurry++
|
||||
@@ -261,7 +271,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
}
|
||||
|
||||
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
if err := validateValuesInLabels(labels, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -270,17 +280,17 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
curry = m.curry
|
||||
iCurry int
|
||||
)
|
||||
for i, label := range m.desc.variableLabels {
|
||||
val, ok := labels[label.Name]
|
||||
for i, labelName := range m.desc.variableLabels.names {
|
||||
val, ok := labels[labelName]
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
if ok {
|
||||
return 0, fmt.Errorf("label name %q is already curried", label.Name)
|
||||
return 0, fmt.Errorf("label name %q is already curried", labelName)
|
||||
}
|
||||
h = m.hashAdd(h, curry[iCurry].value)
|
||||
iCurry++
|
||||
} else {
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("label name %q missing in label map", label.Name)
|
||||
return 0, fmt.Errorf("label name %q missing in label map", labelName)
|
||||
}
|
||||
h = m.hashAdd(h, val)
|
||||
}
|
||||
@@ -458,7 +468,7 @@ func valueMatchesVariableOrCurriedValue(targetValue string, index int, values []
|
||||
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
|
||||
for l, v := range labels {
|
||||
// Check if the target label exists in our metrics and get the index.
|
||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.labelNames())
|
||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.names)
|
||||
if validLabel {
|
||||
// Check the value of that label against the target value.
|
||||
// We don't consider curried values in partial matches.
|
||||
@@ -497,7 +507,7 @@ func (m *metricMap) getOrCreateMetricWithLabelValues(
|
||||
return metric
|
||||
}
|
||||
|
||||
// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
|
||||
// getOrCreateMetricWithLabels retrieves the metric by hash and label value
|
||||
// or creates it and returns the new one.
|
||||
//
|
||||
// This function holds the mutex.
|
||||
@@ -602,7 +612,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||
return false
|
||||
}
|
||||
iCurry := 0
|
||||
for i, k := range desc.variableLabels {
|
||||
for i, k := range desc.variableLabels.names {
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
if values[i] != curry[iCurry].value {
|
||||
return false
|
||||
@@ -610,7 +620,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||
iCurry++
|
||||
continue
|
||||
}
|
||||
if values[i] != labels[k.Name] {
|
||||
if values[i] != labels[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -620,13 +630,13 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
|
||||
labelValues := make([]string, len(labels)+len(curry))
|
||||
iCurry := 0
|
||||
for i, k := range desc.variableLabels {
|
||||
for i, k := range desc.variableLabels.names {
|
||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||
labelValues[i] = curry[iCurry].value
|
||||
iCurry++
|
||||
continue
|
||||
}
|
||||
labelValues[i] = labels[k.Name]
|
||||
labelValues[i] = labels[k]
|
||||
}
|
||||
return labelValues
|
||||
}
|
||||
@@ -646,19 +656,37 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
|
||||
return labelValues
|
||||
}
|
||||
|
||||
func constrainLabels(desc *Desc, labels Labels) Labels {
|
||||
constrainedValues := make(Labels, len(labels))
|
||||
for l, v := range labels {
|
||||
if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok {
|
||||
constrainedValues[l] = desc.variableLabels[i].Constrain(v)
|
||||
continue
|
||||
}
|
||||
constrainedValues[l] = v
|
||||
var labelsPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make(Labels)
|
||||
},
|
||||
}
|
||||
|
||||
func constrainLabels(desc *Desc, labels Labels) (Labels, func()) {
|
||||
if len(desc.variableLabels.labelConstraints) == 0 {
|
||||
// Fast path when there's no constraints
|
||||
return labels, func() {}
|
||||
}
|
||||
|
||||
constrainedLabels := labelsPool.Get().(Labels)
|
||||
for l, v := range labels {
|
||||
constrainedLabels[l] = desc.variableLabels.constrain(l, v)
|
||||
}
|
||||
|
||||
return constrainedLabels, func() {
|
||||
for k := range constrainedLabels {
|
||||
delete(constrainedLabels, k)
|
||||
}
|
||||
labelsPool.Put(constrainedLabels)
|
||||
}
|
||||
return constrainedValues
|
||||
}
|
||||
|
||||
func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {
|
||||
if len(desc.variableLabels.labelConstraints) == 0 {
|
||||
// Fast path when there's no constraints
|
||||
return lvs
|
||||
}
|
||||
|
||||
constrainedValues := make([]string, len(lvs))
|
||||
var iCurry, iLVs int
|
||||
for i := 0; i < len(lvs)+len(curry); i++ {
|
||||
@@ -667,8 +695,11 @@ func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) [
|
||||
continue
|
||||
}
|
||||
|
||||
if i < len(desc.variableLabels) {
|
||||
constrainedValues[iLVs] = desc.variableLabels[i].Constrain(lvs[iLVs])
|
||||
if i < len(desc.variableLabels.names) {
|
||||
constrainedValues[iLVs] = desc.variableLabels.constrain(
|
||||
desc.variableLabels.names[i],
|
||||
lvs[iLVs],
|
||||
)
|
||||
} else {
|
||||
constrainedValues[iLVs] = lvs[iLVs]
|
||||
}
|
||||
|
||||
36
vendor/github.com/prometheus/client_golang/prometheus/wrap.go
generated
vendored
36
vendor/github.com/prometheus/client_golang/prometheus/wrap.go
generated
vendored
@@ -63,7 +63,7 @@ func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
|
||||
// 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_”,
|
||||
// fact, those metrics are already prefixed with "go_" or "process_",
|
||||
// respectively.)
|
||||
//
|
||||
// Conflicts between Collectors registered through the original Registerer with
|
||||
@@ -78,6 +78,40 @@ func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCollectorWith returns a Collector wrapping the provided Collector. The
|
||||
// wrapped Collector will add the provided Labels to all Metrics it collects (as
|
||||
// ConstLabels). The Metrics collected by the unmodified Collector must not
|
||||
// duplicate any of those labels.
|
||||
//
|
||||
// WrapCollectorWith can be useful to work with multiple instances of a third
|
||||
// party library that does not expose enough flexibility on the lifecycle of its
|
||||
// registered metrics.
|
||||
// For example, let's say you have a foo.New(reg Registerer) constructor that
|
||||
// registers metrics but never unregisters them, and you want to create multiple
|
||||
// instances of foo.Foo with different labels.
|
||||
// The way to achieve that, is to create a new Registry, pass it to foo.New,
|
||||
// then use WrapCollectorWith to wrap that Registry with the desired labels and
|
||||
// register that as a collector in your main Registry.
|
||||
// Then you can un-register the wrapped collector effectively un-registering the
|
||||
// metrics registered by foo.New.
|
||||
func WrapCollectorWith(labels Labels, c Collector) Collector {
|
||||
return &wrappingCollector{
|
||||
wrappedCollector: c,
|
||||
labels: labels,
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCollectorWithPrefix returns a Collector wrapping the provided Collector. The
|
||||
// wrapped Collector will add the provided prefix to the name of all Metrics it collects.
|
||||
//
|
||||
// See the documentation of WrapCollectorWith for more details on the use case.
|
||||
func WrapCollectorWithPrefix(prefix string, c Collector) Collector {
|
||||
return &wrappingCollector{
|
||||
wrappedCollector: c,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
type wrappingRegisterer struct {
|
||||
wrappedRegisterer Registerer
|
||||
prefix string
|
||||
|
||||
Reference in New Issue
Block a user