web: replace deprecated InstrumentHandler() (#3862)
* web: replace deprecated InstrumentHandler() This change replaces the deprecated InstrumentHandler function by the equivalent functions from the promhttp package. The following metrics are removed: * http_request_duration_microseconds (Summary). * http_request_size_bytes (Summary). * http_requests_total (Counter). And the following metrics are added instead: * prometheus_http_request_duration_seconds (Histogram). * prometheus_http_response_size_bytes (Histogram). * promhttp_metric_handler_requests_in_flight (Gauge). * promhttp_metric_handler_requests_total (Counter). * Update github.com/prometheus/common/route package * web: refactor using the new prometheus/common/route package
This commit is contained in:
		
							parent
							
								
									ba5273a0ab
								
							
						
					
					
						commit
						83325c8d82
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							|  | @ -37,8 +37,7 @@ STATICCHECK_IGNORE = \ | |||
|   github.com/prometheus/prometheus/documentation/examples/remote_storage/remote_storage_adapter/main.go:SA1019 \
 | ||||
|   github.com/prometheus/prometheus/pkg/textparse/lex.l.go:SA4006 \
 | ||||
|   github.com/prometheus/prometheus/pkg/pool/pool.go:SA6002 \
 | ||||
|   github.com/prometheus/prometheus/promql/engine.go:SA6002 \
 | ||||
|   github.com/prometheus/prometheus/web/web.go:SA1019 | ||||
|   github.com/prometheus/prometheus/promql/engine.go:SA6002 | ||||
| 
 | ||||
| all: format staticcheck unused build test | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,10 @@ package prometheus | |||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"math" | ||||
| 	"sync/atomic" | ||||
| 
 | ||||
| 	dto "github.com/prometheus/client_model/go" | ||||
| ) | ||||
| 
 | ||||
| // Counter is a Metric that represents a single numerical value that only ever
 | ||||
|  | @ -42,6 +46,14 @@ type Counter interface { | |||
| type CounterOpts Opts | ||||
| 
 | ||||
| // NewCounter creates a new Counter based on the provided CounterOpts.
 | ||||
| //
 | ||||
| // The returned implementation tracks the counter value in two separate
 | ||||
| // variables, a float64 and a uint64. The latter is used to track calls of the
 | ||||
| // Inc method and calls of the Add method with a value that can be represented
 | ||||
| // as a uint64. This allows atomic increments of the counter with optimal
 | ||||
| // performance. (It is common to have an Inc call in very hot execution paths.)
 | ||||
| // Both internal tracking values are added up in the Write method. This has to
 | ||||
| // be taken into account when it comes to precision and overflow behavior.
 | ||||
| func NewCounter(opts CounterOpts) Counter { | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
|  | @ -49,20 +61,58 @@ func NewCounter(opts CounterOpts) Counter { | |||
| 		nil, | ||||
| 		opts.ConstLabels, | ||||
| 	) | ||||
| 	result := &counter{value: value{desc: desc, valType: CounterValue, labelPairs: desc.constLabelPairs}} | ||||
| 	result := &counter{desc: desc, labelPairs: desc.constLabelPairs} | ||||
| 	result.init(result) // Init self-collection.
 | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| type counter struct { | ||||
| 	value | ||||
| 	// valBits contains the bits of the represented float64 value, while
 | ||||
| 	// valInt stores values that are exact integers. Both have to go first
 | ||||
| 	// in the struct to guarantee alignment for atomic operations.
 | ||||
| 	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | ||||
| 	valBits uint64 | ||||
| 	valInt  uint64 | ||||
| 
 | ||||
| 	selfCollector | ||||
| 	desc *Desc | ||||
| 
 | ||||
| 	labelPairs []*dto.LabelPair | ||||
| } | ||||
| 
 | ||||
| func (c *counter) Desc() *Desc { | ||||
| 	return c.desc | ||||
| } | ||||
| 
 | ||||
| func (c *counter) Add(v float64) { | ||||
| 	if v < 0 { | ||||
| 		panic(errors.New("counter cannot decrease in value")) | ||||
| 	} | ||||
| 	c.value.Add(v) | ||||
| 	ival := uint64(v) | ||||
| 	if float64(ival) == v { | ||||
| 		atomic.AddUint64(&c.valInt, ival) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	for { | ||||
| 		oldBits := atomic.LoadUint64(&c.valBits) | ||||
| 		newBits := math.Float64bits(math.Float64frombits(oldBits) + v) | ||||
| 		if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *counter) Inc() { | ||||
| 	atomic.AddUint64(&c.valInt, 1) | ||||
| } | ||||
| 
 | ||||
| func (c *counter) Write(out *dto.Metric) error { | ||||
| 	fval := math.Float64frombits(atomic.LoadUint64(&c.valBits)) | ||||
| 	ival := atomic.LoadUint64(&c.valInt) | ||||
| 	val := fval + float64(ival) | ||||
| 
 | ||||
| 	return populateMetric(CounterValue, val, c.labelPairs, out) | ||||
| } | ||||
| 
 | ||||
| // CounterVec is a Collector that bundles a set of Counters that all share the
 | ||||
|  | @ -70,16 +120,12 @@ func (c *counter) Add(v float64) { | |||
| // if you want to count the same thing partitioned by various dimensions
 | ||||
| // (e.g. number of HTTP requests, partitioned by response code and
 | ||||
| // method). Create instances with NewCounterVec.
 | ||||
| //
 | ||||
| // CounterVec embeds MetricVec. See there for a full list of methods with
 | ||||
| // detailed documentation.
 | ||||
| type CounterVec struct { | ||||
| 	*MetricVec | ||||
| 	*metricVec | ||||
| } | ||||
| 
 | ||||
| // NewCounterVec creates a new CounterVec based on the provided CounterOpts and
 | ||||
| // partitioned by the given label names. At least one label name must be
 | ||||
| // provided.
 | ||||
| // partitioned by the given label names.
 | ||||
| func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
|  | @ -88,34 +134,62 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { | |||
| 		opts.ConstLabels, | ||||
| 	) | ||||
| 	return &CounterVec{ | ||||
| 		MetricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			result := &counter{value: value{ | ||||
| 				desc:       desc, | ||||
| 				valType:    CounterValue, | ||||
| 				labelPairs: makeLabelPairs(desc, lvs), | ||||
| 			}} | ||||
| 		metricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			if len(lvs) != len(desc.variableLabels) { | ||||
| 				panic(errInconsistentCardinality) | ||||
| 			} | ||||
| 			result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} | ||||
| 			result.init(result) // Init self-collection.
 | ||||
| 			return result | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetMetricWithLabelValues replaces the method of the same name in
 | ||||
| // MetricVec. The difference is that this method returns a Counter and not a
 | ||||
| // Metric so that no type conversion is required.
 | ||||
| func (m *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) | ||||
| // GetMetricWithLabelValues returns the Counter for the given slice of label
 | ||||
| // values (same order as the VariableLabels in Desc). If that combination of
 | ||||
| // label values is accessed for the first time, a new Counter is created.
 | ||||
| //
 | ||||
| // It is possible to call this method without using the returned Counter to only
 | ||||
| // create the new Counter but leave it at its starting value 0. See also the
 | ||||
| // SummaryVec example.
 | ||||
| //
 | ||||
| // Keeping the Counter for later use is possible (and should be considered if
 | ||||
| // performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | ||||
| // Delete can be used to delete the Counter from the CounterVec. In that case,
 | ||||
| // the Counter will still exist, but it will not be exported anymore, even if a
 | ||||
| // Counter with the same label values is created later.
 | ||||
| //
 | ||||
| // An error is returned if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | ||||
| // an alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| // See also the GaugeVec example.
 | ||||
| func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { | ||||
| 	metric, err := v.metricVec.getMetricWithLabelValues(lvs...) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Counter), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // GetMetricWith replaces the method of the same name in MetricVec. The
 | ||||
| // difference is that this method returns a Counter and not a Metric so that no
 | ||||
| // type conversion is required.
 | ||||
| func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWith(labels) | ||||
| // GetMetricWith returns the Counter for the given Labels map (the label names
 | ||||
| // must match those of the VariableLabels in Desc). If that label map is
 | ||||
| // accessed for the first time, a new Counter is created. Implications of
 | ||||
| // creating a Counter without using it and keeping the Counter for later use are
 | ||||
| // the same as for GetMetricWithLabelValues.
 | ||||
| //
 | ||||
| // An error is returned if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // This method is used for the same purpose as
 | ||||
| // GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | ||||
| // methods.
 | ||||
| func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { | ||||
| 	metric, err := v.metricVec.getMetricWith(labels) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Counter), err | ||||
| 	} | ||||
|  | @ -123,18 +197,57 @@ func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) { | |||
| } | ||||
| 
 | ||||
| // WithLabelValues works as GetMetricWithLabelValues, but panics where
 | ||||
| // GetMetricWithLabelValues would have returned an error. By not returning an
 | ||||
| // error, WithLabelValues allows shortcuts like
 | ||||
| // GetMetricWithLabelValues would have returned an error. Not returning an
 | ||||
| // error allows shortcuts like
 | ||||
| //     myVec.WithLabelValues("404", "GET").Add(42)
 | ||||
| func (m *CounterVec) WithLabelValues(lvs ...string) Counter { | ||||
| 	return m.MetricVec.WithLabelValues(lvs...).(Counter) | ||||
| func (v *CounterVec) WithLabelValues(lvs ...string) Counter { | ||||
| 	c, err := v.GetMetricWithLabelValues(lvs...) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | ||||
| // returned an error. By not returning an error, With allows shortcuts like
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| func (m *CounterVec) With(labels Labels) Counter { | ||||
| 	return m.MetricVec.With(labels).(Counter) | ||||
| // returned an error. Not returning an error allows shortcuts like
 | ||||
| //     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| func (v *CounterVec) With(labels Labels) Counter { | ||||
| 	c, err := v.GetMetricWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return c | ||||
| } | ||||
| 
 | ||||
| // CurryWith returns a vector curried with the provided labels, i.e. the
 | ||||
| // returned vector has those labels pre-set for all labeled operations performed
 | ||||
| // on it. The cardinality of the curried vector is reduced accordingly. The
 | ||||
| // order of the remaining labels stays the same (just with the curried labels
 | ||||
| // taken out of the sequence – which is relevant for the
 | ||||
| // (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | ||||
| // vector, but only with labels not yet used for currying before.
 | ||||
| //
 | ||||
| // The metrics contained in the CounterVec are shared between the curried and
 | ||||
| // uncurried vectors. They are just accessed differently. Curried and uncurried
 | ||||
| // vectors behave identically in terms of collection. Only one must be
 | ||||
| // registered with a given registry (usually the uncurried version). The Reset
 | ||||
| // method deletes all metrics, even if called on a curried vector.
 | ||||
| func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) { | ||||
| 	vec, err := v.curryWith(labels) | ||||
| 	if vec != nil { | ||||
| 		return &CounterVec{vec}, err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // MustCurryWith works as CurryWith but panics where CurryWith would have
 | ||||
| // returned an error.
 | ||||
| func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec { | ||||
| 	vec, err := v.CurryWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return vec | ||||
| } | ||||
| 
 | ||||
| // CounterFunc is a Counter whose value is determined at collect time by calling a
 | ||||
|  |  | |||
|  | @ -25,19 +25,6 @@ import ( | |||
| 	dto "github.com/prometheus/client_model/go" | ||||
| ) | ||||
| 
 | ||||
| // reservedLabelPrefix is a prefix which is not legal in user-supplied
 | ||||
| // label names.
 | ||||
| const reservedLabelPrefix = "__" | ||||
| 
 | ||||
| // Labels represents a collection of label name -> value mappings. This type is
 | ||||
| // commonly used with the With(Labels) and GetMetricWith(Labels) methods of
 | ||||
| // metric vector Collectors, e.g.:
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| //
 | ||||
| // The other use-case is the specification of constant label pairs in Opts or to
 | ||||
| // create a Desc.
 | ||||
| type Labels map[string]string | ||||
| 
 | ||||
| // Desc is the descriptor used by every Prometheus Metric. It is essentially
 | ||||
| // the immutable meta-data of a Metric. The normal Metric implementations
 | ||||
| // included in this package manage their Desc under the hood. Users only have to
 | ||||
|  | @ -86,8 +73,7 @@ type Desc struct { | |||
| // and therefore not part of the Desc. (They are managed within the Metric.)
 | ||||
| //
 | ||||
| // For constLabels, the label values are constant. Therefore, they are fully
 | ||||
| // specified in the Desc. See the Opts documentation for the implications of
 | ||||
| // constant labels.
 | ||||
| // specified in the Desc. See the Collector example for a usage pattern.
 | ||||
| func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc { | ||||
| 	d := &Desc{ | ||||
| 		fqName:         fqName, | ||||
|  | @ -122,6 +108,12 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * | |||
| 	for _, labelName := range labelNames { | ||||
| 		labelValues = append(labelValues, constLabels[labelName]) | ||||
| 	} | ||||
| 	// Validate the const label values. They can't have a wrong cardinality, so
 | ||||
| 	// use in len(labelValues) as expectedNumberOfValues.
 | ||||
| 	if err := validateLabelValues(labelValues, len(labelValues)); err != nil { | ||||
| 		d.err = err | ||||
| 		return d | ||||
| 	} | ||||
| 	// Now add the variable label names, but prefix them with something that
 | ||||
| 	// cannot be in a regular label name. That prevents matching the label
 | ||||
| 	// dimension with a different mix between preset and variable labels.
 | ||||
|  | @ -137,6 +129,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * | |||
| 		d.err = errors.New("duplicate label names") | ||||
| 		return d | ||||
| 	} | ||||
| 
 | ||||
| 	vh := hashNew() | ||||
| 	for _, val := range labelValues { | ||||
| 		vh = hashAdd(vh, val) | ||||
|  | @ -193,8 +186,3 @@ func (d *Desc) String() string { | |||
| 		d.variableLabels, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func checkLabelName(l string) bool { | ||||
| 	return model.LabelName(l).IsValid() && | ||||
| 		!strings.HasPrefix(l, reservedLabelPrefix) | ||||
| } | ||||
|  |  | |||
|  | @ -11,10 +11,12 @@ | |||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| // Package prometheus provides metrics primitives to instrument code for
 | ||||
| // monitoring. It also offers a registry for metrics. Sub-packages allow to
 | ||||
| // expose the registered metrics via HTTP (package promhttp) or push them to a
 | ||||
| // Pushgateway (package push).
 | ||||
| // Package prometheus is the core instrumentation package. It provides metrics
 | ||||
| // primitives to instrument code for monitoring. It also offers a registry for
 | ||||
| // metrics. Sub-packages allow to expose the registered metrics via HTTP
 | ||||
| // (package promhttp) or push them to a Pushgateway (package push). There is
 | ||||
| // also a sub-package promauto, which provides metrics constructors with
 | ||||
| // automatic registration.
 | ||||
| //
 | ||||
| // All exported functions and methods are safe to be used concurrently unless
 | ||||
| // specified otherwise.
 | ||||
|  | @ -26,6 +28,7 @@ | |||
| //    package main
 | ||||
| //
 | ||||
| //    import (
 | ||||
| //    	"log"
 | ||||
| //    	"net/http"
 | ||||
| //
 | ||||
| //    	"github.com/prometheus/client_golang/prometheus"
 | ||||
|  | @ -71,7 +74,10 @@ | |||
| // The number of exported identifiers in this package might appear a bit
 | ||||
| // overwhelming. However, in addition to the basic plumbing shown in the example
 | ||||
| // above, you only need to understand the different metric types and their
 | ||||
| // vector versions for basic usage.
 | ||||
| // vector versions for basic usage. Furthermore, if you are not concerned with
 | ||||
| // fine-grained control of when and how to register metrics with the registry,
 | ||||
| // have a look at the promauto package, which will effectively allow you to
 | ||||
| // ignore registration altogether in simple cases.
 | ||||
| //
 | ||||
| // Above, you have already touched the Counter and the Gauge. There are two more
 | ||||
| // advanced metric types: the Summary and Histogram. A more thorough description
 | ||||
|  | @ -144,7 +150,7 @@ | |||
| // registry.
 | ||||
| //
 | ||||
| // So far, everything we did operated on the so-called default registry, as it
 | ||||
| // can be found in the global DefaultRegistry variable. With NewRegistry, you
 | ||||
| // can be found in the global DefaultRegisterer variable. With NewRegistry, you
 | ||||
| // can create a custom registry, or you can even implement the Registerer or
 | ||||
| // Gatherer interfaces yourself. The methods Register and Unregister work in the
 | ||||
| // same way on a custom registry as the global functions Register and Unregister
 | ||||
|  | @ -152,11 +158,11 @@ | |||
| //
 | ||||
| // There are a number of uses for custom registries: You can use registries with
 | ||||
| // special properties, see NewPedanticRegistry. You can avoid global state, as
 | ||||
| // it is imposed by the DefaultRegistry. You can use multiple registries at the
 | ||||
| // same time to expose different metrics in different ways. You can use separate
 | ||||
| // registries for testing purposes.
 | ||||
| // it is imposed by the DefaultRegisterer. You can use multiple registries at
 | ||||
| // the same time to expose different metrics in different ways.  You can use
 | ||||
| // separate registries for testing purposes.
 | ||||
| //
 | ||||
| // Also note that the DefaultRegistry comes registered with a Collector for Go
 | ||||
| // Also note that the DefaultRegisterer comes registered with a Collector for Go
 | ||||
| // runtime metrics (via NewGoCollector) and a Collector for process metrics (via
 | ||||
| // NewProcessCollector). With a custom registry, you are in control and decide
 | ||||
| // yourself about the Collectors to register.
 | ||||
|  |  | |||
|  | @ -13,6 +13,14 @@ | |||
| 
 | ||||
| package prometheus | ||||
| 
 | ||||
| import ( | ||||
| 	"math" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| 
 | ||||
| 	dto "github.com/prometheus/client_model/go" | ||||
| ) | ||||
| 
 | ||||
| // Gauge is a Metric that represents a single numerical value that can
 | ||||
| // arbitrarily go up and down.
 | ||||
| //
 | ||||
|  | @ -48,13 +56,74 @@ type Gauge interface { | |||
| type GaugeOpts Opts | ||||
| 
 | ||||
| // NewGauge creates a new Gauge based on the provided GaugeOpts.
 | ||||
| //
 | ||||
| // The returned implementation is optimized for a fast Set method. If you have a
 | ||||
| // choice for managing the value of a Gauge via Set vs. Inc/Dec/Add/Sub, pick
 | ||||
| // the former. For example, the Inc method of the returned Gauge is slower than
 | ||||
| // the Inc method of a Counter returned by NewCounter. This matches the typical
 | ||||
| // scenarios for Gauges and Counters, where the former tends to be Set-heavy and
 | ||||
| // the latter Inc-heavy.
 | ||||
| func NewGauge(opts GaugeOpts) Gauge { | ||||
| 	return newValue(NewDesc( | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
| 		opts.Help, | ||||
| 		nil, | ||||
| 		opts.ConstLabels, | ||||
| 	), GaugeValue, 0) | ||||
| 	) | ||||
| 	result := &gauge{desc: desc, labelPairs: desc.constLabelPairs} | ||||
| 	result.init(result) // Init self-collection.
 | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| type gauge struct { | ||||
| 	// valBits contains the bits of the represented float64 value. It has
 | ||||
| 	// to go first in the struct to guarantee alignment for atomic
 | ||||
| 	// operations.  http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | ||||
| 	valBits uint64 | ||||
| 
 | ||||
| 	selfCollector | ||||
| 
 | ||||
| 	desc       *Desc | ||||
| 	labelPairs []*dto.LabelPair | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Desc() *Desc { | ||||
| 	return g.desc | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Set(val float64) { | ||||
| 	atomic.StoreUint64(&g.valBits, math.Float64bits(val)) | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) SetToCurrentTime() { | ||||
| 	g.Set(float64(time.Now().UnixNano()) / 1e9) | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Inc() { | ||||
| 	g.Add(1) | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Dec() { | ||||
| 	g.Add(-1) | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Add(val float64) { | ||||
| 	for { | ||||
| 		oldBits := atomic.LoadUint64(&g.valBits) | ||||
| 		newBits := math.Float64bits(math.Float64frombits(oldBits) + val) | ||||
| 		if atomic.CompareAndSwapUint64(&g.valBits, oldBits, newBits) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Sub(val float64) { | ||||
| 	g.Add(val * -1) | ||||
| } | ||||
| 
 | ||||
| func (g *gauge) Write(out *dto.Metric) error { | ||||
| 	val := math.Float64frombits(atomic.LoadUint64(&g.valBits)) | ||||
| 	return populateMetric(GaugeValue, val, g.labelPairs, out) | ||||
| } | ||||
| 
 | ||||
| // GaugeVec is a Collector that bundles a set of Gauges that all share the same
 | ||||
|  | @ -63,12 +132,11 @@ func NewGauge(opts GaugeOpts) Gauge { | |||
| // (e.g. number of operations queued, partitioned by user and operation
 | ||||
| // type). Create instances with NewGaugeVec.
 | ||||
| type GaugeVec struct { | ||||
| 	*MetricVec | ||||
| 	*metricVec | ||||
| } | ||||
| 
 | ||||
| // NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
 | ||||
| // partitioned by the given label names. At least one label name must be
 | ||||
| // provided.
 | ||||
| // partitioned by the given label names.
 | ||||
| func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
|  | @ -77,28 +145,62 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { | |||
| 		opts.ConstLabels, | ||||
| 	) | ||||
| 	return &GaugeVec{ | ||||
| 		MetricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			return newValue(desc, GaugeValue, 0, lvs...) | ||||
| 		metricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			if len(lvs) != len(desc.variableLabels) { | ||||
| 				panic(errInconsistentCardinality) | ||||
| 			} | ||||
| 			result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)} | ||||
| 			result.init(result) // Init self-collection.
 | ||||
| 			return result | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetMetricWithLabelValues replaces the method of the same name in
 | ||||
| // MetricVec. The difference is that this method returns a Gauge and not a
 | ||||
| // Metric so that no type conversion is required.
 | ||||
| func (m *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) | ||||
| // GetMetricWithLabelValues returns the Gauge for the given slice of label
 | ||||
| // values (same order as the VariableLabels in Desc). If that combination of
 | ||||
| // label values is accessed for the first time, a new Gauge is created.
 | ||||
| //
 | ||||
| // It is possible to call this method without using the returned Gauge to only
 | ||||
| // create the new Gauge but leave it at its starting value 0. See also the
 | ||||
| // SummaryVec example.
 | ||||
| //
 | ||||
| // Keeping the Gauge for later use is possible (and should be considered if
 | ||||
| // performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | ||||
| // Delete can be used to delete the Gauge from the GaugeVec. In that case, the
 | ||||
| // Gauge will still exist, but it will not be exported anymore, even if a
 | ||||
| // Gauge with the same label values is created later. See also the CounterVec
 | ||||
| // example.
 | ||||
| //
 | ||||
| // An error is returned if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | ||||
| // an alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { | ||||
| 	metric, err := v.metricVec.getMetricWithLabelValues(lvs...) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Gauge), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // GetMetricWith replaces the method of the same name in MetricVec. The
 | ||||
| // difference is that this method returns a Gauge and not a Metric so that no
 | ||||
| // type conversion is required.
 | ||||
| func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWith(labels) | ||||
| // GetMetricWith returns the Gauge for the given Labels map (the label names
 | ||||
| // must match those of the VariableLabels in Desc). If that label map is
 | ||||
| // accessed for the first time, a new Gauge is created. Implications of
 | ||||
| // creating a Gauge without using it and keeping the Gauge for later use are
 | ||||
| // the same as for GetMetricWithLabelValues.
 | ||||
| //
 | ||||
| // An error is returned if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // This method is used for the same purpose as
 | ||||
| // GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | ||||
| // methods.
 | ||||
| func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { | ||||
| 	metric, err := v.metricVec.getMetricWith(labels) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Gauge), err | ||||
| 	} | ||||
|  | @ -106,18 +208,57 @@ func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { | |||
| } | ||||
| 
 | ||||
| // WithLabelValues works as GetMetricWithLabelValues, but panics where
 | ||||
| // GetMetricWithLabelValues would have returned an error. By not returning an
 | ||||
| // error, WithLabelValues allows shortcuts like
 | ||||
| // GetMetricWithLabelValues would have returned an error. Not returning an
 | ||||
| // error allows shortcuts like
 | ||||
| //     myVec.WithLabelValues("404", "GET").Add(42)
 | ||||
| func (m *GaugeVec) WithLabelValues(lvs ...string) Gauge { | ||||
| 	return m.MetricVec.WithLabelValues(lvs...).(Gauge) | ||||
| func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { | ||||
| 	g, err := v.GetMetricWithLabelValues(lvs...) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return g | ||||
| } | ||||
| 
 | ||||
| // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | ||||
| // returned an error. By not returning an error, With allows shortcuts like
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| func (m *GaugeVec) With(labels Labels) Gauge { | ||||
| 	return m.MetricVec.With(labels).(Gauge) | ||||
| // returned an error. Not returning an error allows shortcuts like
 | ||||
| //     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| func (v *GaugeVec) With(labels Labels) Gauge { | ||||
| 	g, err := v.GetMetricWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return g | ||||
| } | ||||
| 
 | ||||
| // CurryWith returns a vector curried with the provided labels, i.e. the
 | ||||
| // returned vector has those labels pre-set for all labeled operations performed
 | ||||
| // on it. The cardinality of the curried vector is reduced accordingly. The
 | ||||
| // order of the remaining labels stays the same (just with the curried labels
 | ||||
| // taken out of the sequence – which is relevant for the
 | ||||
| // (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | ||||
| // vector, but only with labels not yet used for currying before.
 | ||||
| //
 | ||||
| // The metrics contained in the GaugeVec are shared between the curried and
 | ||||
| // uncurried vectors. They are just accessed differently. Curried and uncurried
 | ||||
| // vectors behave identically in terms of collection. Only one must be
 | ||||
| // registered with a given registry (usually the uncurried version). The Reset
 | ||||
| // method deletes all metrics, even if called on a curried vector.
 | ||||
| func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) { | ||||
| 	vec, err := v.curryWith(labels) | ||||
| 	if vec != nil { | ||||
| 		return &GaugeVec{vec}, err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // MustCurryWith works as CurryWith but panics where CurryWith would have
 | ||||
| // returned an error.
 | ||||
| func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec { | ||||
| 	vec, err := v.CurryWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return vec | ||||
| } | ||||
| 
 | ||||
| // GaugeFunc is a Gauge whose value is determined at collect time by calling a
 | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ type goCollector struct { | |||
| 	goroutinesDesc *Desc | ||||
| 	threadsDesc    *Desc | ||||
| 	gcDesc         *Desc | ||||
| 	goInfoDesc     *Desc | ||||
| 
 | ||||
| 	// metrics to describe and collect
 | ||||
| 	metrics memStatsMetrics | ||||
|  | @ -26,12 +27,16 @@ func NewGoCollector() Collector { | |||
| 			nil, nil), | ||||
| 		threadsDesc: NewDesc( | ||||
| 			"go_threads", | ||||
| 			"Number of OS threads created", | ||||
| 			"Number of OS threads created.", | ||||
| 			nil, nil), | ||||
| 		gcDesc: NewDesc( | ||||
| 			"go_gc_duration_seconds", | ||||
| 			"A summary of the GC invocation durations.", | ||||
| 			nil, nil), | ||||
| 		goInfoDesc: NewDesc( | ||||
| 			"go_info", | ||||
| 			"Information about the Go environment.", | ||||
| 			nil, Labels{"version": runtime.Version()}), | ||||
| 		metrics: memStatsMetrics{ | ||||
| 			{ | ||||
| 				desc: NewDesc( | ||||
|  | @ -239,6 +244,7 @@ func (c *goCollector) Describe(ch chan<- *Desc) { | |||
| 	ch <- c.goroutinesDesc | ||||
| 	ch <- c.threadsDesc | ||||
| 	ch <- c.gcDesc | ||||
| 	ch <- c.goInfoDesc | ||||
| 	for _, i := range c.metrics { | ||||
| 		ch <- i.desc | ||||
| 	} | ||||
|  | @ -261,6 +267,8 @@ func (c *goCollector) Collect(ch chan<- Metric) { | |||
| 	quantiles[0.0] = stats.PauseQuantiles[0].Seconds() | ||||
| 	ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles) | ||||
| 
 | ||||
| 	ch <- MustNewConstMetric(c.goInfoDesc, GaugeValue, 1) | ||||
| 
 | ||||
| 	ms := &runtime.MemStats{} | ||||
| 	runtime.ReadMemStats(ms) | ||||
| 	for _, i := range c.metrics { | ||||
|  |  | |||
|  | @ -126,23 +126,16 @@ type HistogramOpts struct { | |||
| 	// string.
 | ||||
| 	Help string | ||||
| 
 | ||||
| 	// ConstLabels are used to attach fixed labels to this
 | ||||
| 	// Histogram. Histograms with the same fully-qualified name must have the
 | ||||
| 	// same label names in their ConstLabels.
 | ||||
| 	// ConstLabels are used to attach fixed labels to this metric. Metrics
 | ||||
| 	// with the same fully-qualified name must have the same label names in
 | ||||
| 	// their ConstLabels.
 | ||||
| 	//
 | ||||
| 	// Note that in most cases, labels have a value that varies during the
 | ||||
| 	// lifetime of a process. Those labels are usually managed with a
 | ||||
| 	// HistogramVec. ConstLabels serve only special purposes. One is for the
 | ||||
| 	// special case where the value of a label does not change during the
 | ||||
| 	// lifetime of a process, e.g. if the revision of the running binary is
 | ||||
| 	// put into a label. Another, more advanced purpose is if more than one
 | ||||
| 	// Collector needs to collect Histograms with the same fully-qualified
 | ||||
| 	// name. In that case, those Summaries must differ in the values of
 | ||||
| 	// their ConstLabels. See the Collector examples.
 | ||||
| 	//
 | ||||
| 	// If the value of a label never changes (not even between binaries),
 | ||||
| 	// that label most likely should not be a label at all (but part of the
 | ||||
| 	// metric name).
 | ||||
| 	// ConstLabels are only used rarely. In particular, do not use them to
 | ||||
| 	// attach the same labels to all your metrics. Those use cases are
 | ||||
| 	// better covered by target labels set by the scraping Prometheus
 | ||||
| 	// server, or by one specific metric (e.g. a build_info or a
 | ||||
| 	// machine_role metric). See also
 | ||||
| 	// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
 | ||||
| 	ConstLabels Labels | ||||
| 
 | ||||
| 	// Buckets defines the buckets into which observations are counted. Each
 | ||||
|  | @ -287,12 +280,11 @@ func (h *histogram) Write(out *dto.Metric) error { | |||
| // (e.g. HTTP request latencies, partitioned by status code and method). Create
 | ||||
| // instances with NewHistogramVec.
 | ||||
| type HistogramVec struct { | ||||
| 	*MetricVec | ||||
| 	*metricVec | ||||
| } | ||||
| 
 | ||||
| // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
 | ||||
| // partitioned by the given label names. At least one label name must be
 | ||||
| // provided.
 | ||||
| // partitioned by the given label names.
 | ||||
| func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
|  | @ -301,47 +293,116 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec { | |||
| 		opts.ConstLabels, | ||||
| 	) | ||||
| 	return &HistogramVec{ | ||||
| 		MetricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 		metricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			return newHistogram(desc, opts, lvs...) | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetMetricWithLabelValues replaces the method of the same name in
 | ||||
| // MetricVec. The difference is that this method returns a Histogram and not a
 | ||||
| // Metric so that no type conversion is required.
 | ||||
| func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Histogram, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) | ||||
| // GetMetricWithLabelValues returns the Histogram for the given slice of label
 | ||||
| // values (same order as the VariableLabels in Desc). If that combination of
 | ||||
| // label values is accessed for the first time, a new Histogram is created.
 | ||||
| //
 | ||||
| // It is possible to call this method without using the returned Histogram to only
 | ||||
| // create the new Histogram but leave it at its starting value, a Histogram without
 | ||||
| // any observations.
 | ||||
| //
 | ||||
| // Keeping the Histogram for later use is possible (and should be considered if
 | ||||
| // performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | ||||
| // Delete can be used to delete the Histogram from the HistogramVec. In that case, the
 | ||||
| // Histogram will still exist, but it will not be exported anymore, even if a
 | ||||
| // Histogram with the same label values is created later. See also the CounterVec
 | ||||
| // example.
 | ||||
| //
 | ||||
| // An error is returned if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | ||||
| // an alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| // See also the GaugeVec example.
 | ||||
| func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { | ||||
| 	metric, err := v.metricVec.getMetricWithLabelValues(lvs...) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Histogram), err | ||||
| 		return metric.(Observer), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // GetMetricWith replaces the method of the same name in MetricVec. The
 | ||||
| // difference is that this method returns a Histogram and not a Metric so that no
 | ||||
| // type conversion is required.
 | ||||
| func (m *HistogramVec) GetMetricWith(labels Labels) (Histogram, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWith(labels) | ||||
| // GetMetricWith returns the Histogram for the given Labels map (the label names
 | ||||
| // must match those of the VariableLabels in Desc). If that label map is
 | ||||
| // accessed for the first time, a new Histogram is created. Implications of
 | ||||
| // creating a Histogram without using it and keeping the Histogram for later use
 | ||||
| // are the same as for GetMetricWithLabelValues.
 | ||||
| //
 | ||||
| // An error is returned if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // This method is used for the same purpose as
 | ||||
| // GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | ||||
| // methods.
 | ||||
| func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) { | ||||
| 	metric, err := v.metricVec.getMetricWith(labels) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Histogram), err | ||||
| 		return metric.(Observer), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // WithLabelValues works as GetMetricWithLabelValues, but panics where
 | ||||
| // GetMetricWithLabelValues would have returned an error. By not returning an
 | ||||
| // error, WithLabelValues allows shortcuts like
 | ||||
| // GetMetricWithLabelValues would have returned an error. Not returning an
 | ||||
| // error allows shortcuts like
 | ||||
| //     myVec.WithLabelValues("404", "GET").Observe(42.21)
 | ||||
| func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram { | ||||
| 	return m.MetricVec.WithLabelValues(lvs...).(Histogram) | ||||
| func (v *HistogramVec) WithLabelValues(lvs ...string) Observer { | ||||
| 	h, err := v.GetMetricWithLabelValues(lvs...) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | ||||
| // returned an error. By not returning an error, With allows shortcuts like
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
 | ||||
| func (m *HistogramVec) With(labels Labels) Histogram { | ||||
| 	return m.MetricVec.With(labels).(Histogram) | ||||
| // With works as GetMetricWith but panics where GetMetricWithLabels would have
 | ||||
| // returned an error. Not returning an error allows shortcuts like
 | ||||
| //     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
 | ||||
| func (v *HistogramVec) With(labels Labels) Observer { | ||||
| 	h, err := v.GetMetricWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| // CurryWith returns a vector curried with the provided labels, i.e. the
 | ||||
| // returned vector has those labels pre-set for all labeled operations performed
 | ||||
| // on it. The cardinality of the curried vector is reduced accordingly. The
 | ||||
| // order of the remaining labels stays the same (just with the curried labels
 | ||||
| // taken out of the sequence – which is relevant for the
 | ||||
| // (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | ||||
| // vector, but only with labels not yet used for currying before.
 | ||||
| //
 | ||||
| // The metrics contained in the HistogramVec are shared between the curried and
 | ||||
| // uncurried vectors. They are just accessed differently. Curried and uncurried
 | ||||
| // vectors behave identically in terms of collection. Only one must be
 | ||||
| // registered with a given registry (usually the uncurried version). The Reset
 | ||||
| // method deletes all metrics, even if called on a curried vector.
 | ||||
| func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) { | ||||
| 	vec, err := v.curryWith(labels) | ||||
| 	if vec != nil { | ||||
| 		return &HistogramVec{vec}, err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // MustCurryWith works as CurryWith but panics where CurryWith would have
 | ||||
| // returned an error.
 | ||||
| func (v *HistogramVec) MustCurryWith(labels Labels) ObserverVec { | ||||
| 	vec, err := v.CurryWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return vec | ||||
| } | ||||
| 
 | ||||
| type constHistogram struct { | ||||
|  | @ -401,8 +462,8 @@ func NewConstHistogram( | |||
| 	buckets map[float64]uint64, | ||||
| 	labelValues ...string, | ||||
| ) (Metric, error) { | ||||
| 	if len(desc.variableLabels) != len(labelValues) { | ||||
| 		return nil, errInconsistentCardinality | ||||
| 	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &constHistogram{ | ||||
| 		desc:       desc, | ||||
|  |  | |||
|  | @ -61,8 +61,8 @@ func giveBuf(buf *bytes.Buffer) { | |||
| // name).
 | ||||
| //
 | ||||
| // Deprecated: Please note the issues described in the doc comment of
 | ||||
| // InstrumentHandler. You might want to consider using promhttp.Handler instead
 | ||||
| // (which is not instrumented).
 | ||||
| // InstrumentHandler. You might want to consider using
 | ||||
| // promhttp.InstrumentedHandler instead.
 | ||||
| func Handler() http.Handler { | ||||
| 	return InstrumentHandler("prometheus", UninstrumentedHandler()) | ||||
| } | ||||
|  | @ -95,7 +95,7 @@ func UninstrumentedHandler() http.Handler { | |||
| 			closer.Close() | ||||
| 		} | ||||
| 		if lastErr != nil && buf.Len() == 0 { | ||||
| 			http.Error(w, "No metrics encoded, last error:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 			http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		header := w.Header() | ||||
|  | @ -158,7 +158,8 @@ func nowSeries(t ...time.Time) nower { | |||
| // value. http_requests_total is a metric vector partitioned by HTTP method
 | ||||
| // (label name "method") and HTTP status code (label name "code").
 | ||||
| //
 | ||||
| // Deprecated: InstrumentHandler has several issues:
 | ||||
| // Deprecated: InstrumentHandler has several issues. Use the tooling provided in
 | ||||
| // package promhttp instead. The issues are the following:
 | ||||
| //
 | ||||
| // - It uses Summaries rather than Histograms. Summaries are not useful if
 | ||||
| // aggregation across multiple instances is required.
 | ||||
|  | @ -174,10 +175,6 @@ func nowSeries(t ...time.Time) nower { | |||
| //
 | ||||
| // - It has additional issues with HTTP/2, cf.
 | ||||
| // https://github.com/prometheus/client_golang/issues/272.
 | ||||
| //
 | ||||
| // Upcoming versions of this package will provide ways of instrumenting HTTP
 | ||||
| // handlers that are more flexible and have fewer issues. Please prefer direct
 | ||||
| // instrumentation in the meantime.
 | ||||
| func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc { | ||||
| 	return InstrumentHandlerFunc(handlerName, handler.ServeHTTP) | ||||
| } | ||||
|  | @ -187,7 +184,7 @@ func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFun | |||
| // issues).
 | ||||
| //
 | ||||
| // Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
 | ||||
| // InstrumentHandler is.
 | ||||
| // InstrumentHandler is. Use the tooling provided in package promhttp instead.
 | ||||
| func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { | ||||
| 	return InstrumentHandlerFuncWithOpts( | ||||
| 		SummaryOpts{ | ||||
|  | @ -226,7 +223,7 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri | |||
| // SummaryOpts.
 | ||||
| //
 | ||||
| // Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
 | ||||
| // InstrumentHandler is.
 | ||||
| // InstrumentHandler is. Use the tooling provided in package promhttp instead.
 | ||||
| func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc { | ||||
| 	return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP) | ||||
| } | ||||
|  | @ -237,7 +234,7 @@ func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.Hand | |||
| // SummaryOpts are used.
 | ||||
| //
 | ||||
| // Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
 | ||||
| // as InstrumentHandler is.
 | ||||
| // as InstrumentHandler is. Use the tooling provided in package promhttp instead.
 | ||||
| func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { | ||||
| 	reqCnt := NewCounterVec( | ||||
| 		CounterOpts{ | ||||
|  |  | |||
|  | @ -0,0 +1,57 @@ | |||
| package prometheus | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
| 
 | ||||
| // Labels represents a collection of label name -> value mappings. This type is
 | ||||
| // commonly used with the With(Labels) and GetMetricWith(Labels) methods of
 | ||||
| // metric vector Collectors, e.g.:
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| //
 | ||||
| // The other use-case is the specification of constant label pairs in Opts or to
 | ||||
| // create a Desc.
 | ||||
| type Labels map[string]string | ||||
| 
 | ||||
| // reservedLabelPrefix is a prefix which is not legal in user-supplied
 | ||||
| // label names.
 | ||||
| const reservedLabelPrefix = "__" | ||||
| 
 | ||||
| var errInconsistentCardinality = errors.New("inconsistent label cardinality") | ||||
| 
 | ||||
| func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error { | ||||
| 	if len(labels) != expectedNumberOfValues { | ||||
| 		return errInconsistentCardinality | ||||
| 	} | ||||
| 
 | ||||
| 	for name, val := range labels { | ||||
| 		if !utf8.ValidString(val) { | ||||
| 			return fmt.Errorf("label %s: value %q is not valid UTF-8", name, val) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func validateLabelValues(vals []string, expectedNumberOfValues int) error { | ||||
| 	if len(vals) != expectedNumberOfValues { | ||||
| 		return errInconsistentCardinality | ||||
| 	} | ||||
| 
 | ||||
| 	for _, val := range vals { | ||||
| 		if !utf8.ValidString(val) { | ||||
| 			return fmt.Errorf("label value %q is not valid UTF-8", val) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func checkLabelName(l string) bool { | ||||
| 	return model.LabelName(l).IsValid() && !strings.HasPrefix(l, reservedLabelPrefix) | ||||
| } | ||||
|  | @ -79,20 +79,12 @@ type Opts struct { | |||
| 	// with the same fully-qualified name must have the same label names in
 | ||||
| 	// their ConstLabels.
 | ||||
| 	//
 | ||||
| 	// Note that in most cases, labels have a value that varies during the
 | ||||
| 	// lifetime of a process. Those labels are usually managed with a metric
 | ||||
| 	// vector collector (like CounterVec, GaugeVec, UntypedVec). ConstLabels
 | ||||
| 	// serve only special purposes. One is for the special case where the
 | ||||
| 	// value of a label does not change during the lifetime of a process,
 | ||||
| 	// e.g. if the revision of the running binary is put into a
 | ||||
| 	// label. Another, more advanced purpose is if more than one Collector
 | ||||
| 	// needs to collect Metrics with the same fully-qualified name. In that
 | ||||
| 	// case, those Metrics must differ in the values of their
 | ||||
| 	// ConstLabels. See the Collector examples.
 | ||||
| 	//
 | ||||
| 	// If the value of a label never changes (not even between binaries),
 | ||||
| 	// that label most likely should not be a label at all (but part of the
 | ||||
| 	// metric name).
 | ||||
| 	// ConstLabels are only used rarely. In particular, do not use them to
 | ||||
| 	// attach the same labels to all your metrics. Those use cases are
 | ||||
| 	// better covered by target labels set by the scraping Prometheus
 | ||||
| 	// server, or by one specific metric (e.g. a build_info or a
 | ||||
| 	// machine_role metric). See also
 | ||||
| 	// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
 | ||||
| 	ConstLabels Labels | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,52 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| package prometheus | ||||
| 
 | ||||
| // Observer is the interface that wraps the Observe method, which is used by
 | ||||
| // Histogram and Summary to add observations.
 | ||||
| type Observer interface { | ||||
| 	Observe(float64) | ||||
| } | ||||
| 
 | ||||
| // The ObserverFunc type is an adapter to allow the use of ordinary
 | ||||
| // functions as Observers. If f is a function with the appropriate
 | ||||
| // signature, ObserverFunc(f) is an Observer that calls f.
 | ||||
| //
 | ||||
| // This adapter is usually used in connection with the Timer type, and there are
 | ||||
| // two general use cases:
 | ||||
| //
 | ||||
| // The most common one is to use a Gauge as the Observer for a Timer.
 | ||||
| // See the "Gauge" Timer example.
 | ||||
| //
 | ||||
| // The more advanced use case is to create a function that dynamically decides
 | ||||
| // which Observer to use for observing the duration. See the "Complex" Timer
 | ||||
| // example.
 | ||||
| type ObserverFunc func(float64) | ||||
| 
 | ||||
| // Observe calls f(value). It implements Observer.
 | ||||
| func (f ObserverFunc) Observe(value float64) { | ||||
| 	f(value) | ||||
| } | ||||
| 
 | ||||
| // ObserverVec is an interface implemented by `HistogramVec` and `SummaryVec`.
 | ||||
| type ObserverVec interface { | ||||
| 	GetMetricWith(Labels) (Observer, error) | ||||
| 	GetMetricWithLabelValues(lvs ...string) (Observer, error) | ||||
| 	With(Labels) Observer | ||||
| 	WithLabelValues(...string) Observer | ||||
| 	CurryWith(Labels) (ObserverVec, error) | ||||
| 	MustCurryWith(Labels) ObserverVec | ||||
| 
 | ||||
| 	Collector | ||||
| } | ||||
|  | @ -26,8 +26,11 @@ type processCollector struct { | |||
| } | ||||
| 
 | ||||
| // NewProcessCollector returns a collector which exports the current state of
 | ||||
| // process metrics including cpu, memory and file descriptor usage as well as
 | ||||
| // the process start time for the given process id under the given namespace.
 | ||||
| // process metrics including CPU, memory and file descriptor usage as well as
 | ||||
| // the process start time for the given process ID under the given namespace.
 | ||||
| //
 | ||||
| // Currently, the collector depends on a Linux-style proc filesystem and
 | ||||
| // therefore only exports metrics for Linux.
 | ||||
| func NewProcessCollector(pid int, namespace string) Collector { | ||||
| 	return NewProcessCollectorPIDFn( | ||||
| 		func() (int, error) { return pid, nil }, | ||||
|  | @ -35,11 +38,8 @@ func NewProcessCollector(pid int, namespace string) Collector { | |||
| 	) | ||||
| } | ||||
| 
 | ||||
| // NewProcessCollectorPIDFn returns a collector which exports the current state
 | ||||
| // of process metrics including cpu, memory and file descriptor usage as well
 | ||||
| // as the process start time under the given namespace. The given pidFn is
 | ||||
| // called on each collect and is used to determine the process to export
 | ||||
| // metrics for.
 | ||||
| // NewProcessCollectorPIDFn works like NewProcessCollector but the process ID is
 | ||||
| // determined on each collect anew by calling the given pidFn function.
 | ||||
| func NewProcessCollectorPIDFn( | ||||
| 	pidFn func() (int, error), | ||||
| 	namespace string, | ||||
|  |  | |||
							
								
								
									
										199
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										199
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,199 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	closeNotifier = 1 << iota | ||||
| 	flusher | ||||
| 	hijacker | ||||
| 	readerFrom | ||||
| 	pusher | ||||
| ) | ||||
| 
 | ||||
| type delegator interface { | ||||
| 	http.ResponseWriter | ||||
| 
 | ||||
| 	Status() int | ||||
| 	Written() int64 | ||||
| } | ||||
| 
 | ||||
| type responseWriterDelegator struct { | ||||
| 	http.ResponseWriter | ||||
| 
 | ||||
| 	handler, method    string | ||||
| 	status             int | ||||
| 	written            int64 | ||||
| 	wroteHeader        bool | ||||
| 	observeWriteHeader func(int) | ||||
| } | ||||
| 
 | ||||
| func (r *responseWriterDelegator) Status() int { | ||||
| 	return r.status | ||||
| } | ||||
| 
 | ||||
| func (r *responseWriterDelegator) Written() int64 { | ||||
| 	return r.written | ||||
| } | ||||
| 
 | ||||
| func (r *responseWriterDelegator) WriteHeader(code int) { | ||||
| 	r.status = code | ||||
| 	r.wroteHeader = true | ||||
| 	r.ResponseWriter.WriteHeader(code) | ||||
| 	if r.observeWriteHeader != nil { | ||||
| 		r.observeWriteHeader(code) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (r *responseWriterDelegator) Write(b []byte) (int, error) { | ||||
| 	if !r.wroteHeader { | ||||
| 		r.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| 	n, err := r.ResponseWriter.Write(b) | ||||
| 	r.written += int64(n) | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| type closeNotifierDelegator struct{ *responseWriterDelegator } | ||||
| type flusherDelegator struct{ *responseWriterDelegator } | ||||
| type hijackerDelegator struct{ *responseWriterDelegator } | ||||
| type readerFromDelegator struct{ *responseWriterDelegator } | ||||
| 
 | ||||
| func (d *closeNotifierDelegator) CloseNotify() <-chan bool { | ||||
| 	return d.ResponseWriter.(http.CloseNotifier).CloseNotify() | ||||
| } | ||||
| func (d *flusherDelegator) Flush() { | ||||
| 	d.ResponseWriter.(http.Flusher).Flush() | ||||
| } | ||||
| func (d *hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { | ||||
| 	return d.ResponseWriter.(http.Hijacker).Hijack() | ||||
| } | ||||
| func (d *readerFromDelegator) ReadFrom(re io.Reader) (int64, error) { | ||||
| 	if !d.wroteHeader { | ||||
| 		d.WriteHeader(http.StatusOK) | ||||
| 	} | ||||
| 	n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re) | ||||
| 	d.written += n | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32) | ||||
| 
 | ||||
| func init() { | ||||
| 	// TODO(beorn7): Code generation would help here.
 | ||||
| 	pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0
 | ||||
| 		return d | ||||
| 	} | ||||
| 	pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1
 | ||||
| 		return &closeNotifierDelegator{d} | ||||
| 	} | ||||
| 	pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2
 | ||||
| 		return &flusherDelegator{d} | ||||
| 	} | ||||
| 	pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4
 | ||||
| 		return &hijackerDelegator{d} | ||||
| 	} | ||||
| 	pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Hijacker | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &hijackerDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 		}{d, &hijackerDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8
 | ||||
| 		return readerFromDelegator{d} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &readerFromDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.Flusher | ||||
| 		}{d, &readerFromDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &readerFromDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 		}{d, &readerFromDelegator{d}, &hijackerDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 		}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										181
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										181
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,181 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| // +build go1.8
 | ||||
| 
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| type pusherDelegator struct{ *responseWriterDelegator } | ||||
| 
 | ||||
| func (d *pusherDelegator) Push(target string, opts *http.PushOptions) error { | ||||
| 	return d.ResponseWriter.(http.Pusher).Push(target, opts) | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16
 | ||||
| 		return &pusherDelegator{d} | ||||
| 	} | ||||
| 	pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.Flusher | ||||
| 		}{d, &pusherDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.Hijacker | ||||
| 		}{d, &pusherDelegator{d}, &hijackerDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.Hijacker | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 		}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.Flusher | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}} | ||||
| 	} | ||||
| 	pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31
 | ||||
| 		return struct { | ||||
| 			*responseWriterDelegator | ||||
| 			http.Pusher | ||||
| 			io.ReaderFrom | ||||
| 			http.Hijacker | ||||
| 			http.Flusher | ||||
| 			http.CloseNotifier | ||||
| 		}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { | ||||
| 	d := &responseWriterDelegator{ | ||||
| 		ResponseWriter:     w, | ||||
| 		observeWriteHeader: observeWriteHeaderFunc, | ||||
| 	} | ||||
| 
 | ||||
| 	id := 0 | ||||
| 	if _, ok := w.(http.CloseNotifier); ok { | ||||
| 		id += closeNotifier | ||||
| 	} | ||||
| 	if _, ok := w.(http.Flusher); ok { | ||||
| 		id += flusher | ||||
| 	} | ||||
| 	if _, ok := w.(http.Hijacker); ok { | ||||
| 		id += hijacker | ||||
| 	} | ||||
| 	if _, ok := w.(io.ReaderFrom); ok { | ||||
| 		id += readerFrom | ||||
| 	} | ||||
| 	if _, ok := w.(http.Pusher); ok { | ||||
| 		id += pusher | ||||
| 	} | ||||
| 
 | ||||
| 	return pickDelegator[id](d) | ||||
| } | ||||
							
								
								
									
										44
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										44
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,44 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| // +build !go1.8
 | ||||
| 
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { | ||||
| 	d := &responseWriterDelegator{ | ||||
| 		ResponseWriter:     w, | ||||
| 		observeWriteHeader: observeWriteHeaderFunc, | ||||
| 	} | ||||
| 
 | ||||
| 	id := 0 | ||||
| 	if _, ok := w.(http.CloseNotifier); ok { | ||||
| 		id += closeNotifier | ||||
| 	} | ||||
| 	if _, ok := w.(http.Flusher); ok { | ||||
| 		id += flusher | ||||
| 	} | ||||
| 	if _, ok := w.(http.Hijacker); ok { | ||||
| 		id += hijacker | ||||
| 	} | ||||
| 	if _, ok := w.(io.ReaderFrom); ok { | ||||
| 		id += readerFrom | ||||
| 	} | ||||
| 
 | ||||
| 	return pickDelegator[id](d) | ||||
| } | ||||
							
								
								
									
										311
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										311
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,311 @@ | |||
| // Copyright 2016 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 promhttp provides tooling around HTTP servers and clients.
 | ||||
| //
 | ||||
| // First, the package allows the creation of http.Handler instances to expose
 | ||||
| // Prometheus metrics via HTTP. promhttp.Handler acts on the
 | ||||
| // prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a
 | ||||
| // custom registry or anything that implements the Gatherer interface. It also
 | ||||
| // allows the creation of handlers that act differently on errors or allow to
 | ||||
| // log errors.
 | ||||
| //
 | ||||
| // Second, the package provides tooling to instrument instances of http.Handler
 | ||||
| // via middleware. Middleware wrappers follow the naming scheme
 | ||||
| // InstrumentHandlerX, where X describes the intended use of the middleware.
 | ||||
| // See each function's doc comment for specific details.
 | ||||
| //
 | ||||
| // Finally, the package allows for an http.RoundTripper to be instrumented via
 | ||||
| // middleware. Middleware wrappers follow the naming scheme
 | ||||
| // InstrumentRoundTripperX, where X describes the intended use of the
 | ||||
| // middleware. See each function's doc comment for specific details.
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"compress/gzip" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/prometheus/common/expfmt" | ||||
| 
 | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	contentTypeHeader     = "Content-Type" | ||||
| 	contentLengthHeader   = "Content-Length" | ||||
| 	contentEncodingHeader = "Content-Encoding" | ||||
| 	acceptEncodingHeader  = "Accept-Encoding" | ||||
| ) | ||||
| 
 | ||||
| var bufPool sync.Pool | ||||
| 
 | ||||
| func getBuf() *bytes.Buffer { | ||||
| 	buf := bufPool.Get() | ||||
| 	if buf == nil { | ||||
| 		return &bytes.Buffer{} | ||||
| 	} | ||||
| 	return buf.(*bytes.Buffer) | ||||
| } | ||||
| 
 | ||||
| func giveBuf(buf *bytes.Buffer) { | ||||
| 	buf.Reset() | ||||
| 	bufPool.Put(buf) | ||||
| } | ||||
| 
 | ||||
| // Handler returns an http.Handler for the prometheus.DefaultGatherer, using
 | ||||
| // default HandlerOpts, i.e. it reports the first error as an HTTP error, it has
 | ||||
| // no error logging, and it applies compression if requested by the client.
 | ||||
| //
 | ||||
| // The returned http.Handler is already instrumented using the
 | ||||
| // InstrumentMetricHandler function and the prometheus.DefaultRegisterer. If you
 | ||||
| // create multiple http.Handlers by separate calls of the Handler function, the
 | ||||
| // metrics used for instrumentation will be shared between them, providing
 | ||||
| // global scrape counts.
 | ||||
| //
 | ||||
| // This function is meant to cover the bulk of basic use cases. If you are doing
 | ||||
| // anything that requires more customization (including using a non-default
 | ||||
| // Gatherer, different instrumentation, and non-default HandlerOpts), use the
 | ||||
| // HandlerFor function. See there for details.
 | ||||
| func Handler() http.Handler { | ||||
| 	return InstrumentMetricHandler( | ||||
| 		prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| // HandlerFor returns an uninstrumented http.Handler for the provided
 | ||||
| // Gatherer. The behavior of the Handler is defined by the provided
 | ||||
| // HandlerOpts. Thus, HandlerFor is useful to create http.Handlers for custom
 | ||||
| // Gatherers, with non-default HandlerOpts, and/or with custom (or no)
 | ||||
| // instrumentation. Use the InstrumentMetricHandler function to apply the same
 | ||||
| // kind of instrumentation as it is used by the Handler function.
 | ||||
| func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { | ||||
| 	var inFlightSem chan struct{} | ||||
| 	if opts.MaxRequestsInFlight > 0 { | ||||
| 		inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight) | ||||
| 	} | ||||
| 
 | ||||
| 	h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||
| 		if inFlightSem != nil { | ||||
| 			select { | ||||
| 			case inFlightSem <- struct{}{}: // All good, carry on.
 | ||||
| 				defer func() { <-inFlightSem }() | ||||
| 			default: | ||||
| 				http.Error(w, fmt.Sprintf( | ||||
| 					"Limit of concurrent requests reached (%d), try again later.", opts.MaxRequestsInFlight, | ||||
| 				), http.StatusServiceUnavailable) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		mfs, err := reg.Gather() | ||||
| 		if err != nil { | ||||
| 			if opts.ErrorLog != nil { | ||||
| 				opts.ErrorLog.Println("error gathering metrics:", err) | ||||
| 			} | ||||
| 			switch opts.ErrorHandling { | ||||
| 			case PanicOnError: | ||||
| 				panic(err) | ||||
| 			case ContinueOnError: | ||||
| 				if len(mfs) == 0 { | ||||
| 					http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 					return | ||||
| 				} | ||||
| 			case HTTPErrorOnError: | ||||
| 				http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		contentType := expfmt.Negotiate(req.Header) | ||||
| 		buf := getBuf() | ||||
| 		defer giveBuf(buf) | ||||
| 		writer, encoding := decorateWriter(req, buf, opts.DisableCompression) | ||||
| 		enc := expfmt.NewEncoder(writer, contentType) | ||||
| 		var lastErr error | ||||
| 		for _, mf := range mfs { | ||||
| 			if err := enc.Encode(mf); err != nil { | ||||
| 				lastErr = err | ||||
| 				if opts.ErrorLog != nil { | ||||
| 					opts.ErrorLog.Println("error encoding metric family:", err) | ||||
| 				} | ||||
| 				switch opts.ErrorHandling { | ||||
| 				case PanicOnError: | ||||
| 					panic(err) | ||||
| 				case ContinueOnError: | ||||
| 					// Handled later.
 | ||||
| 				case HTTPErrorOnError: | ||||
| 					http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if closer, ok := writer.(io.Closer); ok { | ||||
| 			closer.Close() | ||||
| 		} | ||||
| 		if lastErr != nil && buf.Len() == 0 { | ||||
| 			http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError) | ||||
| 			return | ||||
| 		} | ||||
| 		header := w.Header() | ||||
| 		header.Set(contentTypeHeader, string(contentType)) | ||||
| 		header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) | ||||
| 		if encoding != "" { | ||||
| 			header.Set(contentEncodingHeader, encoding) | ||||
| 		} | ||||
| 		if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil { | ||||
| 			opts.ErrorLog.Println("error while sending encoded metrics:", err) | ||||
| 		} | ||||
| 		// TODO(beorn7): Consider streaming serving of metrics.
 | ||||
| 	}) | ||||
| 
 | ||||
| 	if opts.Timeout <= 0 { | ||||
| 		return h | ||||
| 	} | ||||
| 	return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf( | ||||
| 		"Exceeded configured timeout of %v.\n", | ||||
| 		opts.Timeout, | ||||
| 	)) | ||||
| } | ||||
| 
 | ||||
| // InstrumentMetricHandler is usually used with an http.Handler returned by the
 | ||||
| // HandlerFor function. It instruments the provided http.Handler with two
 | ||||
| // metrics: A counter vector "promhttp_metric_handler_requests_total" to count
 | ||||
| // scrapes partitioned by HTTP status code, and a gauge
 | ||||
| // "promhttp_metric_handler_requests_in_flight" to track the number of
 | ||||
| // simultaneous scrapes. This function idempotently registers collectors for
 | ||||
| // both metrics with the provided Registerer. It panics if the registration
 | ||||
| // fails. The provided metrics are useful to see how many scrapes hit the
 | ||||
| // monitored target (which could be from different Prometheus servers or other
 | ||||
| // scrapers), and how often they overlap (which would result in more than one
 | ||||
| // scrape in flight at the same time). Note that the scrapes-in-flight gauge
 | ||||
| // will contain the scrape by which it is exposed, while the scrape counter will
 | ||||
| // only get incremented after the scrape is complete (as only then the status
 | ||||
| // code is known). For tracking scrape durations, use the
 | ||||
| // "scrape_duration_seconds" gauge created by the Prometheus server upon each
 | ||||
| // scrape.
 | ||||
| func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) http.Handler { | ||||
| 	cnt := prometheus.NewCounterVec( | ||||
| 		prometheus.CounterOpts{ | ||||
| 			Name: "promhttp_metric_handler_requests_total", | ||||
| 			Help: "Total number of scrapes by HTTP status code.", | ||||
| 		}, | ||||
| 		[]string{"code"}, | ||||
| 	) | ||||
| 	// Initialize the most likely HTTP status codes.
 | ||||
| 	cnt.WithLabelValues("200") | ||||
| 	cnt.WithLabelValues("500") | ||||
| 	cnt.WithLabelValues("503") | ||||
| 	if err := reg.Register(cnt); err != nil { | ||||
| 		if are, ok := err.(prometheus.AlreadyRegisteredError); ok { | ||||
| 			cnt = are.ExistingCollector.(*prometheus.CounterVec) | ||||
| 		} else { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	gge := prometheus.NewGauge(prometheus.GaugeOpts{ | ||||
| 		Name: "promhttp_metric_handler_requests_in_flight", | ||||
| 		Help: "Current number of scrapes being served.", | ||||
| 	}) | ||||
| 	if err := reg.Register(gge); err != nil { | ||||
| 		if are, ok := err.(prometheus.AlreadyRegisteredError); ok { | ||||
| 			gge = are.ExistingCollector.(prometheus.Gauge) | ||||
| 		} else { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler)) | ||||
| } | ||||
| 
 | ||||
| // HandlerErrorHandling defines how a Handler serving metrics will handle
 | ||||
| // errors.
 | ||||
| type HandlerErrorHandling int | ||||
| 
 | ||||
| // These constants cause handlers serving metrics to behave as described if
 | ||||
| // errors are encountered.
 | ||||
| const ( | ||||
| 	// Serve an HTTP status code 500 upon the first error
 | ||||
| 	// encountered. Report the error message in the body.
 | ||||
| 	HTTPErrorOnError HandlerErrorHandling = iota | ||||
| 	// Ignore errors and try to serve as many metrics as possible.  However,
 | ||||
| 	// if no metrics can be served, serve an HTTP status code 500 and the
 | ||||
| 	// last error message in the body. Only use this in deliberate "best
 | ||||
| 	// effort" metrics collection scenarios. It is recommended to at least
 | ||||
| 	// log errors (by providing an ErrorLog in HandlerOpts) to not mask
 | ||||
| 	// errors completely.
 | ||||
| 	ContinueOnError | ||||
| 	// Panic upon the first error encountered (useful for "crash only" apps).
 | ||||
| 	PanicOnError | ||||
| ) | ||||
| 
 | ||||
| // Logger is the minimal interface HandlerOpts needs for logging. Note that
 | ||||
| // log.Logger from the standard library implements this interface, and it is
 | ||||
| // easy to implement by custom loggers, if they don't do so already anyway.
 | ||||
| type Logger interface { | ||||
| 	Println(v ...interface{}) | ||||
| } | ||||
| 
 | ||||
| // HandlerOpts specifies options how to serve metrics via an http.Handler. The
 | ||||
| // zero value of HandlerOpts is a reasonable default.
 | ||||
| type HandlerOpts struct { | ||||
| 	// ErrorLog specifies an optional logger for errors collecting and
 | ||||
| 	// serving metrics. If nil, errors are not logged at all.
 | ||||
| 	ErrorLog Logger | ||||
| 	// ErrorHandling defines how errors are handled. Note that errors are
 | ||||
| 	// logged regardless of the configured ErrorHandling provided ErrorLog
 | ||||
| 	// is not nil.
 | ||||
| 	ErrorHandling HandlerErrorHandling | ||||
| 	// If DisableCompression is true, the handler will never compress the
 | ||||
| 	// response, even if requested by the client.
 | ||||
| 	DisableCompression bool | ||||
| 	// The number of concurrent HTTP requests is limited to
 | ||||
| 	// MaxRequestsInFlight. Additional requests are responded to with 503
 | ||||
| 	// Service Unavailable and a suitable message in the body. If
 | ||||
| 	// MaxRequestsInFlight is 0 or negative, no limit is applied.
 | ||||
| 	MaxRequestsInFlight int | ||||
| 	// If handling a request takes longer than Timeout, it is responded to
 | ||||
| 	// with 503 ServiceUnavailable and a suitable Message. No timeout is
 | ||||
| 	// applied if Timeout is 0 or negative. Note that with the current
 | ||||
| 	// implementation, reaching the timeout simply ends the HTTP requests as
 | ||||
| 	// described above (and even that only if sending of the body hasn't
 | ||||
| 	// started yet), while the bulk work of gathering all the metrics keeps
 | ||||
| 	// running in the background (with the eventual result to be thrown
 | ||||
| 	// away). Until the implementation is improved, it is recommended to
 | ||||
| 	// implement a separate timeout in potentially slow Collectors.
 | ||||
| 	Timeout time.Duration | ||||
| } | ||||
| 
 | ||||
| // decorateWriter wraps a writer to handle gzip compression if requested.  It
 | ||||
| // returns the decorated writer and the appropriate "Content-Encoding" header
 | ||||
| // (which is empty if no compression is enabled).
 | ||||
| func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) { | ||||
| 	if compressionDisabled { | ||||
| 		return writer, "" | ||||
| 	} | ||||
| 	header := request.Header.Get(acceptEncodingHeader) | ||||
| 	parts := strings.Split(header, ",") | ||||
| 	for _, part := range parts { | ||||
| 		part := strings.TrimSpace(part) | ||||
| 		if part == "gzip" || strings.HasPrefix(part, "gzip;") { | ||||
| 			return gzip.NewWriter(writer), "gzip" | ||||
| 		} | ||||
| 	} | ||||
| 	return writer, "" | ||||
| } | ||||
							
								
								
									
										97
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										97
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,97 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| ) | ||||
| 
 | ||||
| // The RoundTripperFunc type is an adapter to allow the use of ordinary
 | ||||
| // functions as RoundTrippers. If f is a function with the appropriate
 | ||||
| // signature, RountTripperFunc(f) is a RoundTripper that calls f.
 | ||||
| type RoundTripperFunc func(req *http.Request) (*http.Response, error) | ||||
| 
 | ||||
| // RoundTrip implements the RoundTripper interface.
 | ||||
| func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { | ||||
| 	return rt(r) | ||||
| } | ||||
| 
 | ||||
| // InstrumentRoundTripperInFlight is a middleware that wraps the provided
 | ||||
| // http.RoundTripper. It sets the provided prometheus.Gauge to the number of
 | ||||
| // requests currently handled by the wrapped http.RoundTripper.
 | ||||
| //
 | ||||
| // See the example for ExampleInstrumentRoundTripperDuration for example usage.
 | ||||
| func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc { | ||||
| 	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { | ||||
| 		gauge.Inc() | ||||
| 		defer gauge.Dec() | ||||
| 		return next.RoundTrip(r) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentRoundTripperCounter is a middleware that wraps the provided
 | ||||
| // http.RoundTripper to observe the request result with the provided CounterVec.
 | ||||
| // The CounterVec must have zero, one, or two non-const non-curried labels. For
 | ||||
| // those, the only allowed label names are "code" and "method". The function
 | ||||
| // panics otherwise. Partitioning of the CounterVec happens by HTTP status code
 | ||||
| // and/or HTTP method if the respective instance label names are present in the
 | ||||
| // CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
 | ||||
| //
 | ||||
| // If the wrapped RoundTripper panics or returns a non-nil error, the Counter
 | ||||
| // is not incremented.
 | ||||
| //
 | ||||
| // See the example for ExampleInstrumentRoundTripperDuration for example usage.
 | ||||
| func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc { | ||||
| 	code, method := checkLabels(counter) | ||||
| 
 | ||||
| 	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { | ||||
| 		resp, err := next.RoundTrip(r) | ||||
| 		if err == nil { | ||||
| 			counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc() | ||||
| 		} | ||||
| 		return resp, err | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentRoundTripperDuration is a middleware that wraps the provided
 | ||||
| // http.RoundTripper to observe the request duration with the provided
 | ||||
| // ObserverVec.  The ObserverVec must have zero, one, or two non-const
 | ||||
| // non-curried labels. For those, the only allowed label names are "code" and
 | ||||
| // "method". The function panics otherwise. The Observe method of the Observer
 | ||||
| // in the ObserverVec is called with the request duration in
 | ||||
| // seconds. Partitioning happens by HTTP status code and/or HTTP method if the
 | ||||
| // respective instance label names are present in the ObserverVec. For
 | ||||
| // unpartitioned observations, use an ObserverVec with zero labels. Note that
 | ||||
| // partitioning of Histograms is expensive and should be used judiciously.
 | ||||
| //
 | ||||
| // If the wrapped RoundTripper panics or returns a non-nil error, no values are
 | ||||
| // reported.
 | ||||
| //
 | ||||
| // Note that this method is only guaranteed to never observe negative durations
 | ||||
| // if used with Go1.9+.
 | ||||
| func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc { | ||||
| 	code, method := checkLabels(obs) | ||||
| 
 | ||||
| 	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { | ||||
| 		start := time.Now() | ||||
| 		resp, err := next.RoundTrip(r) | ||||
| 		if err == nil { | ||||
| 			obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds()) | ||||
| 		} | ||||
| 		return resp, err | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										144
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										144
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,144 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| // +build go1.8
 | ||||
| 
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"crypto/tls" | ||||
| 	"net/http" | ||||
| 	"net/http/httptrace" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // InstrumentTrace is used to offer flexibility in instrumenting the available
 | ||||
| // httptrace.ClientTrace hook functions. Each function is passed a float64
 | ||||
| // representing the time in seconds since the start of the http request. A user
 | ||||
| // may choose to use separately buckets Histograms, or implement custom
 | ||||
| // instance labels on a per function basis.
 | ||||
| type InstrumentTrace struct { | ||||
| 	GotConn              func(float64) | ||||
| 	PutIdleConn          func(float64) | ||||
| 	GotFirstResponseByte func(float64) | ||||
| 	Got100Continue       func(float64) | ||||
| 	DNSStart             func(float64) | ||||
| 	DNSDone              func(float64) | ||||
| 	ConnectStart         func(float64) | ||||
| 	ConnectDone          func(float64) | ||||
| 	TLSHandshakeStart    func(float64) | ||||
| 	TLSHandshakeDone     func(float64) | ||||
| 	WroteHeaders         func(float64) | ||||
| 	Wait100Continue      func(float64) | ||||
| 	WroteRequest         func(float64) | ||||
| } | ||||
| 
 | ||||
| // InstrumentRoundTripperTrace is a middleware that wraps the provided
 | ||||
| // RoundTripper and reports times to hook functions provided in the
 | ||||
| // InstrumentTrace struct. Hook functions that are not present in the provided
 | ||||
| // InstrumentTrace struct are ignored. Times reported to the hook functions are
 | ||||
| // time since the start of the request. Only with Go1.9+, those times are
 | ||||
| // guaranteed to never be negative. (Earlier Go versions are not using a
 | ||||
| // monotonic clock.) Note that partitioning of Histograms is expensive and
 | ||||
| // should be used judiciously.
 | ||||
| //
 | ||||
| // For hook functions that receive an error as an argument, no observations are
 | ||||
| // made in the event of a non-nil error value.
 | ||||
| //
 | ||||
| // See the example for ExampleInstrumentRoundTripperDuration for example usage.
 | ||||
| func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc { | ||||
| 	return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { | ||||
| 		start := time.Now() | ||||
| 
 | ||||
| 		trace := &httptrace.ClientTrace{ | ||||
| 			GotConn: func(_ httptrace.GotConnInfo) { | ||||
| 				if it.GotConn != nil { | ||||
| 					it.GotConn(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			PutIdleConn: func(err error) { | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 				if it.PutIdleConn != nil { | ||||
| 					it.PutIdleConn(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			DNSStart: func(_ httptrace.DNSStartInfo) { | ||||
| 				if it.DNSStart != nil { | ||||
| 					it.DNSStart(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			DNSDone: func(_ httptrace.DNSDoneInfo) { | ||||
| 				if it.DNSStart != nil { | ||||
| 					it.DNSStart(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			ConnectStart: func(_, _ string) { | ||||
| 				if it.ConnectStart != nil { | ||||
| 					it.ConnectStart(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			ConnectDone: func(_, _ string, err error) { | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 				if it.ConnectDone != nil { | ||||
| 					it.ConnectDone(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			GotFirstResponseByte: func() { | ||||
| 				if it.GotFirstResponseByte != nil { | ||||
| 					it.GotFirstResponseByte(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			Got100Continue: func() { | ||||
| 				if it.Got100Continue != nil { | ||||
| 					it.Got100Continue(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			TLSHandshakeStart: func() { | ||||
| 				if it.TLSHandshakeStart != nil { | ||||
| 					it.TLSHandshakeStart(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			TLSHandshakeDone: func(_ tls.ConnectionState, err error) { | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 				if it.TLSHandshakeDone != nil { | ||||
| 					it.TLSHandshakeDone(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			WroteHeaders: func() { | ||||
| 				if it.WroteHeaders != nil { | ||||
| 					it.WroteHeaders(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			Wait100Continue: func() { | ||||
| 				if it.Wait100Continue != nil { | ||||
| 					it.Wait100Continue(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 			WroteRequest: func(_ httptrace.WroteRequestInfo) { | ||||
| 				if it.WroteRequest != nil { | ||||
| 					it.WroteRequest(time.Since(start).Seconds()) | ||||
| 				} | ||||
| 			}, | ||||
| 		} | ||||
| 		r = r.WithContext(httptrace.WithClientTrace(context.Background(), trace)) | ||||
| 
 | ||||
| 		return next.RoundTrip(r) | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										447
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										447
									
								
								vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							|  | @ -0,0 +1,447 @@ | |||
| // Copyright 2017 The Prometheus Authors
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| // http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| package promhttp | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	dto "github.com/prometheus/client_model/go" | ||||
| 
 | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| ) | ||||
| 
 | ||||
| // magicString is used for the hacky label test in checkLabels. Remove once fixed.
 | ||||
| const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" | ||||
| 
 | ||||
| // InstrumentHandlerInFlight is a middleware that wraps the provided
 | ||||
| // http.Handler. It sets the provided prometheus.Gauge to the number of
 | ||||
| // requests currently handled by the wrapped http.Handler.
 | ||||
| //
 | ||||
| // See the example for InstrumentHandlerDuration for example usage.
 | ||||
| func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		g.Inc() | ||||
| 		defer g.Dec() | ||||
| 		next.ServeHTTP(w, r) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentHandlerDuration is a middleware that wraps the provided
 | ||||
| // http.Handler to observe the request duration with the provided ObserverVec.
 | ||||
| // The ObserverVec must have zero, one, or two non-const non-curried labels. For
 | ||||
| // those, the only allowed label names are "code" and "method". The function
 | ||||
| // panics otherwise. The Observe method of the Observer in the ObserverVec is
 | ||||
| // called with the request duration in seconds. Partitioning happens by HTTP
 | ||||
| // status code and/or HTTP method if the respective instance label names are
 | ||||
| // present in the ObserverVec. For unpartitioned observations, use an
 | ||||
| // ObserverVec with zero labels. Note that partitioning of Histograms is
 | ||||
| // expensive and should be used judiciously.
 | ||||
| //
 | ||||
| // If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | ||||
| //
 | ||||
| // If the wrapped Handler panics, no values are reported.
 | ||||
| //
 | ||||
| // Note that this method is only guaranteed to never observe negative durations
 | ||||
| // if used with Go1.9+.
 | ||||
| func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { | ||||
| 	code, method := checkLabels(obs) | ||||
| 
 | ||||
| 	if code { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			now := time.Now() | ||||
| 			d := newDelegator(w, nil) | ||||
| 			next.ServeHTTP(d, r) | ||||
| 
 | ||||
| 			obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds()) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		now := time.Now() | ||||
| 		next.ServeHTTP(w, r) | ||||
| 		obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds()) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentHandlerCounter is a middleware that wraps the provided http.Handler
 | ||||
| // to observe the request result with the provided CounterVec.  The CounterVec
 | ||||
| // must have zero, one, or two non-const non-curried labels. For those, the only
 | ||||
| // allowed label names are "code" and "method". The function panics
 | ||||
| // otherwise. Partitioning of the CounterVec happens by HTTP status code and/or
 | ||||
| // HTTP method if the respective instance label names are present in the
 | ||||
| // CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
 | ||||
| //
 | ||||
| // If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | ||||
| //
 | ||||
| // If the wrapped Handler panics, the Counter is not incremented.
 | ||||
| //
 | ||||
| // See the example for InstrumentHandlerDuration for example usage.
 | ||||
| func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc { | ||||
| 	code, method := checkLabels(counter) | ||||
| 
 | ||||
| 	if code { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			d := newDelegator(w, nil) | ||||
| 			next.ServeHTTP(d, r) | ||||
| 			counter.With(labels(code, method, r.Method, d.Status())).Inc() | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		next.ServeHTTP(w, r) | ||||
| 		counter.With(labels(code, method, r.Method, 0)).Inc() | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
 | ||||
| // http.Handler to observe with the provided ObserverVec the request duration
 | ||||
| // until the response headers are written. The ObserverVec must have zero, one,
 | ||||
| // or two non-const non-curried labels. For those, the only allowed label names
 | ||||
| // are "code" and "method". The function panics otherwise. The Observe method of
 | ||||
| // the Observer in the ObserverVec is called with the request duration in
 | ||||
| // seconds. Partitioning happens by HTTP status code and/or HTTP method if the
 | ||||
| // respective instance label names are present in the ObserverVec. For
 | ||||
| // unpartitioned observations, use an ObserverVec with zero labels. Note that
 | ||||
| // partitioning of Histograms is expensive and should be used judiciously.
 | ||||
| //
 | ||||
| // If the wrapped Handler panics before calling WriteHeader, no value is
 | ||||
| // reported.
 | ||||
| //
 | ||||
| // Note that this method is only guaranteed to never observe negative durations
 | ||||
| // if used with Go1.9+.
 | ||||
| //
 | ||||
| // See the example for InstrumentHandlerDuration for example usage.
 | ||||
| func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { | ||||
| 	code, method := checkLabels(obs) | ||||
| 
 | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		now := time.Now() | ||||
| 		d := newDelegator(w, func(status int) { | ||||
| 			obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds()) | ||||
| 		}) | ||||
| 		next.ServeHTTP(d, r) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentHandlerRequestSize is a middleware that wraps the provided
 | ||||
| // http.Handler to observe the request size with the provided ObserverVec.  The
 | ||||
| // ObserverVec must have zero, one, or two non-const non-curried labels. For
 | ||||
| // those, the only allowed label names are "code" and "method". The function
 | ||||
| // panics otherwise. The Observe method of the Observer in the ObserverVec is
 | ||||
| // called with the request size in bytes. Partitioning happens by HTTP status
 | ||||
| // code and/or HTTP method if the respective instance label names are present in
 | ||||
| // the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
 | ||||
| // labels. Note that partitioning of Histograms is expensive and should be used
 | ||||
| // judiciously.
 | ||||
| //
 | ||||
| // If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | ||||
| //
 | ||||
| // If the wrapped Handler panics, no values are reported.
 | ||||
| //
 | ||||
| // See the example for InstrumentHandlerDuration for example usage.
 | ||||
| func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { | ||||
| 	code, method := checkLabels(obs) | ||||
| 
 | ||||
| 	if code { | ||||
| 		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			d := newDelegator(w, nil) | ||||
| 			next.ServeHTTP(d, r) | ||||
| 			size := computeApproximateRequestSize(r) | ||||
| 			obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size)) | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		next.ServeHTTP(w, r) | ||||
| 		size := computeApproximateRequestSize(r) | ||||
| 		obs.With(labels(code, method, r.Method, 0)).Observe(float64(size)) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // InstrumentHandlerResponseSize is a middleware that wraps the provided
 | ||||
| // http.Handler to observe the response size with the provided ObserverVec.  The
 | ||||
| // ObserverVec must have zero, one, or two non-const non-curried labels. For
 | ||||
| // those, the only allowed label names are "code" and "method". The function
 | ||||
| // panics otherwise. The Observe method of the Observer in the ObserverVec is
 | ||||
| // called with the response size in bytes. Partitioning happens by HTTP status
 | ||||
| // code and/or HTTP method if the respective instance label names are present in
 | ||||
| // the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
 | ||||
| // labels. Note that partitioning of Histograms is expensive and should be used
 | ||||
| // judiciously.
 | ||||
| //
 | ||||
| // If the wrapped Handler does not set a status code, a status code of 200 is assumed.
 | ||||
| //
 | ||||
| // If the wrapped Handler panics, no values are reported.
 | ||||
| //
 | ||||
| // See the example for InstrumentHandlerDuration for example usage.
 | ||||
| func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler { | ||||
| 	code, method := checkLabels(obs) | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		d := newDelegator(w, nil) | ||||
| 		next.ServeHTTP(d, r) | ||||
| 		obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written())) | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func checkLabels(c prometheus.Collector) (code bool, method bool) { | ||||
| 	// TODO(beorn7): Remove this hacky way to check for instance labels
 | ||||
| 	// once Descriptors can have their dimensionality queried.
 | ||||
| 	var ( | ||||
| 		desc *prometheus.Desc | ||||
| 		m    prometheus.Metric | ||||
| 		pm   dto.Metric | ||||
| 		lvs  []string | ||||
| 	) | ||||
| 
 | ||||
| 	// Get the Desc from the Collector.
 | ||||
| 	descc := make(chan *prometheus.Desc, 1) | ||||
| 	c.Describe(descc) | ||||
| 
 | ||||
| 	select { | ||||
| 	case desc = <-descc: | ||||
| 	default: | ||||
| 		panic("no description provided by collector") | ||||
| 	} | ||||
| 	select { | ||||
| 	case <-descc: | ||||
| 		panic("more than one description provided by collector") | ||||
| 	default: | ||||
| 	} | ||||
| 
 | ||||
| 	close(descc) | ||||
| 
 | ||||
| 	// Create a ConstMetric with the Desc. Since we don't know how many
 | ||||
| 	// variable labels there are, try for as long as it needs.
 | ||||
| 	for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) { | ||||
| 		m, err = prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, lvs...) | ||||
| 	} | ||||
| 
 | ||||
| 	// Write out the metric into a proto message and look at the labels.
 | ||||
| 	// If the value is not the magicString, it is a constLabel, which doesn't interest us.
 | ||||
| 	// If the label is curried, it doesn't interest us.
 | ||||
| 	// In all other cases, only "code" or "method" is allowed.
 | ||||
| 	if err := m.Write(&pm); err != nil { | ||||
| 		panic("error checking metric for labels") | ||||
| 	} | ||||
| 	for _, label := range pm.Label { | ||||
| 		name, value := label.GetName(), label.GetValue() | ||||
| 		if value != magicString || isLabelCurried(c, name) { | ||||
| 			continue | ||||
| 		} | ||||
| 		switch name { | ||||
| 		case "code": | ||||
| 			code = true | ||||
| 		case "method": | ||||
| 			method = true | ||||
| 		default: | ||||
| 			panic("metric partitioned with non-supported labels") | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func isLabelCurried(c prometheus.Collector, label string) bool { | ||||
| 	// This is even hackier than the label test above.
 | ||||
| 	// We essentially try to curry again and see if it works.
 | ||||
| 	// But for that, we need to type-convert to the two
 | ||||
| 	// types we use here, ObserverVec or *CounterVec.
 | ||||
| 	switch v := c.(type) { | ||||
| 	case *prometheus.CounterVec: | ||||
| 		if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 	case prometheus.ObserverVec: | ||||
| 		if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 	default: | ||||
| 		panic("unsupported metric vec type") | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // emptyLabels is a one-time allocation for non-partitioned metrics to avoid
 | ||||
| // unnecessary allocations on each request.
 | ||||
| var emptyLabels = prometheus.Labels{} | ||||
| 
 | ||||
| func labels(code, method bool, reqMethod string, status int) prometheus.Labels { | ||||
| 	if !(code || method) { | ||||
| 		return emptyLabels | ||||
| 	} | ||||
| 	labels := prometheus.Labels{} | ||||
| 
 | ||||
| 	if code { | ||||
| 		labels["code"] = sanitizeCode(status) | ||||
| 	} | ||||
| 	if method { | ||||
| 		labels["method"] = sanitizeMethod(reqMethod) | ||||
| 	} | ||||
| 
 | ||||
| 	return labels | ||||
| } | ||||
| 
 | ||||
| func computeApproximateRequestSize(r *http.Request) int { | ||||
| 	s := 0 | ||||
| 	if r.URL != nil { | ||||
| 		s += len(r.URL.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	s += len(r.Method) | ||||
| 	s += len(r.Proto) | ||||
| 	for name, values := range r.Header { | ||||
| 		s += len(name) | ||||
| 		for _, value := range values { | ||||
| 			s += len(value) | ||||
| 		} | ||||
| 	} | ||||
| 	s += len(r.Host) | ||||
| 
 | ||||
| 	// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
 | ||||
| 
 | ||||
| 	if r.ContentLength != -1 { | ||||
| 		s += int(r.ContentLength) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| func sanitizeMethod(m string) string { | ||||
| 	switch m { | ||||
| 	case "GET", "get": | ||||
| 		return "get" | ||||
| 	case "PUT", "put": | ||||
| 		return "put" | ||||
| 	case "HEAD", "head": | ||||
| 		return "head" | ||||
| 	case "POST", "post": | ||||
| 		return "post" | ||||
| 	case "DELETE", "delete": | ||||
| 		return "delete" | ||||
| 	case "CONNECT", "connect": | ||||
| 		return "connect" | ||||
| 	case "OPTIONS", "options": | ||||
| 		return "options" | ||||
| 	case "NOTIFY", "notify": | ||||
| 		return "notify" | ||||
| 	default: | ||||
| 		return strings.ToLower(m) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // If the wrapped http.Handler has not set a status code, i.e. the value is
 | ||||
| // currently 0, santizeCode will return 200, for consistency with behavior in
 | ||||
| // the stdlib.
 | ||||
| func sanitizeCode(s int) string { | ||||
| 	switch s { | ||||
| 	case 100: | ||||
| 		return "100" | ||||
| 	case 101: | ||||
| 		return "101" | ||||
| 
 | ||||
| 	case 200, 0: | ||||
| 		return "200" | ||||
| 	case 201: | ||||
| 		return "201" | ||||
| 	case 202: | ||||
| 		return "202" | ||||
| 	case 203: | ||||
| 		return "203" | ||||
| 	case 204: | ||||
| 		return "204" | ||||
| 	case 205: | ||||
| 		return "205" | ||||
| 	case 206: | ||||
| 		return "206" | ||||
| 
 | ||||
| 	case 300: | ||||
| 		return "300" | ||||
| 	case 301: | ||||
| 		return "301" | ||||
| 	case 302: | ||||
| 		return "302" | ||||
| 	case 304: | ||||
| 		return "304" | ||||
| 	case 305: | ||||
| 		return "305" | ||||
| 	case 307: | ||||
| 		return "307" | ||||
| 
 | ||||
| 	case 400: | ||||
| 		return "400" | ||||
| 	case 401: | ||||
| 		return "401" | ||||
| 	case 402: | ||||
| 		return "402" | ||||
| 	case 403: | ||||
| 		return "403" | ||||
| 	case 404: | ||||
| 		return "404" | ||||
| 	case 405: | ||||
| 		return "405" | ||||
| 	case 406: | ||||
| 		return "406" | ||||
| 	case 407: | ||||
| 		return "407" | ||||
| 	case 408: | ||||
| 		return "408" | ||||
| 	case 409: | ||||
| 		return "409" | ||||
| 	case 410: | ||||
| 		return "410" | ||||
| 	case 411: | ||||
| 		return "411" | ||||
| 	case 412: | ||||
| 		return "412" | ||||
| 	case 413: | ||||
| 		return "413" | ||||
| 	case 414: | ||||
| 		return "414" | ||||
| 	case 415: | ||||
| 		return "415" | ||||
| 	case 416: | ||||
| 		return "416" | ||||
| 	case 417: | ||||
| 		return "417" | ||||
| 	case 418: | ||||
| 		return "418" | ||||
| 
 | ||||
| 	case 500: | ||||
| 		return "500" | ||||
| 	case 501: | ||||
| 		return "501" | ||||
| 	case 502: | ||||
| 		return "502" | ||||
| 	case 503: | ||||
| 		return "503" | ||||
| 	case 504: | ||||
| 		return "504" | ||||
| 	case 505: | ||||
| 		return "505" | ||||
| 
 | ||||
| 	case 428: | ||||
| 		return "428" | ||||
| 	case 429: | ||||
| 		return "429" | ||||
| 	case 431: | ||||
| 		return "431" | ||||
| 	case 511: | ||||
| 		return "511" | ||||
| 
 | ||||
| 	default: | ||||
| 		return strconv.Itoa(s) | ||||
| 	} | ||||
| } | ||||
|  | @ -18,8 +18,10 @@ import ( | |||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"sort" | ||||
| 	"sync" | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"github.com/golang/protobuf/proto" | ||||
| 
 | ||||
|  | @ -35,13 +37,13 @@ const ( | |||
| // DefaultRegisterer and DefaultGatherer are the implementations of the
 | ||||
| // Registerer and Gatherer interface a number of convenience functions in this
 | ||||
| // package act on. Initially, both variables point to the same Registry, which
 | ||||
| // has a process collector (see NewProcessCollector) and a Go collector (see
 | ||||
| // NewGoCollector) already registered. This approach to keep default instances
 | ||||
| // as global state mirrors the approach of other packages in the Go standard
 | ||||
| // library. Note that there are caveats. Change the variables with caution and
 | ||||
| // only if you understand the consequences. Users who want to avoid global state
 | ||||
| // altogether should not use the convenience function and act on custom
 | ||||
| // instances instead.
 | ||||
| // has a process collector (currently on Linux only, see NewProcessCollector)
 | ||||
| // and a Go collector (see NewGoCollector) already registered. This approach to
 | ||||
| // keep default instances as global state mirrors the approach of other packages
 | ||||
| // in the Go standard library. Note that there are caveats. Change the variables
 | ||||
| // with caution and only if you understand the consequences. Users who want to
 | ||||
| // avoid global state altogether should not use the convenience functions and
 | ||||
| // act on custom instances instead.
 | ||||
| var ( | ||||
| 	defaultRegistry              = NewRegistry() | ||||
| 	DefaultRegisterer Registerer = defaultRegistry | ||||
|  | @ -80,7 +82,7 @@ func NewPedanticRegistry() *Registry { | |||
| 
 | ||||
| // Registerer is the interface for the part of a registry in charge of
 | ||||
| // registering and unregistering. Users of custom registries should use
 | ||||
| // Registerer as type for registration purposes (rather then the Registry type
 | ||||
| // Registerer as type for registration purposes (rather than the Registry type
 | ||||
| // directly). In that way, they are free to use custom Registerer implementation
 | ||||
| // (e.g. for testing purposes).
 | ||||
| type Registerer interface { | ||||
|  | @ -201,6 +203,13 @@ func (errs MultiError) Error() string { | |||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // Append appends the provided error if it is not nil.
 | ||||
| func (errs *MultiError) Append(err error) { | ||||
| 	if err != nil { | ||||
| 		*errs = append(*errs, err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
 | ||||
| // contained error as error if len(errs is 1). In all other cases, it returns
 | ||||
| // the MultiError directly. This is helpful for returning a MultiError in a way
 | ||||
|  | @ -367,22 +376,12 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { | |||
| 	) | ||||
| 
 | ||||
| 	r.mtx.RLock() | ||||
| 	goroutineBudget := len(r.collectorsByID) | ||||
| 	metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName)) | ||||
| 
 | ||||
| 	// Scatter.
 | ||||
| 	// (Collectors could be complex and slow, so we call them all at once.)
 | ||||
| 	wg.Add(len(r.collectorsByID)) | ||||
| 	go func() { | ||||
| 		wg.Wait() | ||||
| 		close(metricChan) | ||||
| 	}() | ||||
| 	collectors := make(chan Collector, len(r.collectorsByID)) | ||||
| 	for _, collector := range r.collectorsByID { | ||||
| 		go func(collector Collector) { | ||||
| 			defer wg.Done() | ||||
| 			collector.Collect(metricChan) | ||||
| 		}(collector) | ||||
| 		collectors <- collector | ||||
| 	} | ||||
| 
 | ||||
| 	// In case pedantic checks are enabled, we have to copy the map before
 | ||||
| 	// giving up the RLock.
 | ||||
| 	if r.pedanticChecksEnabled { | ||||
|  | @ -391,78 +390,132 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { | |||
| 			registeredDescIDs[id] = struct{}{} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	r.mtx.RUnlock() | ||||
| 
 | ||||
| 	wg.Add(goroutineBudget) | ||||
| 
 | ||||
| 	collectWorker := func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case collector := <-collectors: | ||||
| 				collector.Collect(metricChan) | ||||
| 				wg.Done() | ||||
| 			default: | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Start the first worker now to make sure at least one is running.
 | ||||
| 	go collectWorker() | ||||
| 	goroutineBudget-- | ||||
| 
 | ||||
| 	// Close the metricChan once all collectors are collected.
 | ||||
| 	go func() { | ||||
| 		wg.Wait() | ||||
| 		close(metricChan) | ||||
| 	}() | ||||
| 
 | ||||
| 	// Drain metricChan in case of premature return.
 | ||||
| 	defer func() { | ||||
| 		for range metricChan { | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	// Gather.
 | ||||
| collectLoop: | ||||
| 	for { | ||||
| 		select { | ||||
| 		case metric, ok := <-metricChan: | ||||
| 			if !ok { | ||||
| 				// metricChan is closed, we are done.
 | ||||
| 				break collectLoop | ||||
| 			} | ||||
| 			errs.Append(processMetric( | ||||
| 				metric, metricFamiliesByName, | ||||
| 				metricHashes, dimHashes, | ||||
| 				registeredDescIDs, | ||||
| 			)) | ||||
| 		default: | ||||
| 			if goroutineBudget <= 0 || len(collectors) == 0 { | ||||
| 				// All collectors are aleady being worked on or
 | ||||
| 				// we have already as many goroutines started as
 | ||||
| 				// there are collectors. Just process metrics
 | ||||
| 				// from now on.
 | ||||
| 				for metric := range metricChan { | ||||
| 		// This could be done concurrently, too, but it required locking
 | ||||
| 		// of metricFamiliesByName (and of metricHashes if checks are
 | ||||
| 		// enabled). Most likely not worth it.
 | ||||
| 					errs.Append(processMetric( | ||||
| 						metric, metricFamiliesByName, | ||||
| 						metricHashes, dimHashes, | ||||
| 						registeredDescIDs, | ||||
| 					)) | ||||
| 				} | ||||
| 				break collectLoop | ||||
| 			} | ||||
| 			// Start more workers.
 | ||||
| 			go collectWorker() | ||||
| 			goroutineBudget-- | ||||
| 			runtime.Gosched() | ||||
| 		} | ||||
| 	} | ||||
| 	return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() | ||||
| } | ||||
| 
 | ||||
| // processMetric is an internal helper method only used by the Gather method.
 | ||||
| func processMetric( | ||||
| 	metric Metric, | ||||
| 	metricFamiliesByName map[string]*dto.MetricFamily, | ||||
| 	metricHashes map[uint64]struct{}, | ||||
| 	dimHashes map[string]uint64, | ||||
| 	registeredDescIDs map[uint64]struct{}, | ||||
| ) error { | ||||
| 	desc := metric.Desc() | ||||
| 	dtoMetric := &dto.Metric{} | ||||
| 	if err := metric.Write(dtoMetric); err != nil { | ||||
| 			errs = append(errs, fmt.Errorf( | ||||
| 				"error collecting metric %v: %s", desc, err, | ||||
| 			)) | ||||
| 			continue | ||||
| 		return fmt.Errorf("error collecting metric %v: %s", desc, err) | ||||
| 	} | ||||
| 	metricFamily, ok := metricFamiliesByName[desc.fqName] | ||||
| 	if ok { | ||||
| 		if metricFamily.GetHelp() != desc.help { | ||||
| 				errs = append(errs, fmt.Errorf( | ||||
| 			return fmt.Errorf( | ||||
| 				"collected metric %s %s has help %q but should have %q", | ||||
| 				desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(), | ||||
| 				)) | ||||
| 				continue | ||||
| 			) | ||||
| 		} | ||||
| 		// TODO(beorn7): Simplify switch once Desc has type.
 | ||||
| 		switch metricFamily.GetType() { | ||||
| 		case dto.MetricType_COUNTER: | ||||
| 			if dtoMetric.Counter == nil { | ||||
| 					errs = append(errs, fmt.Errorf( | ||||
| 				return fmt.Errorf( | ||||
| 					"collected metric %s %s should be a Counter", | ||||
| 					desc.fqName, dtoMetric, | ||||
| 					)) | ||||
| 					continue | ||||
| 				) | ||||
| 			} | ||||
| 		case dto.MetricType_GAUGE: | ||||
| 			if dtoMetric.Gauge == nil { | ||||
| 					errs = append(errs, fmt.Errorf( | ||||
| 				return fmt.Errorf( | ||||
| 					"collected metric %s %s should be a Gauge", | ||||
| 					desc.fqName, dtoMetric, | ||||
| 					)) | ||||
| 					continue | ||||
| 				) | ||||
| 			} | ||||
| 		case dto.MetricType_SUMMARY: | ||||
| 			if dtoMetric.Summary == nil { | ||||
| 					errs = append(errs, fmt.Errorf( | ||||
| 				return fmt.Errorf( | ||||
| 					"collected metric %s %s should be a Summary", | ||||
| 					desc.fqName, dtoMetric, | ||||
| 					)) | ||||
| 					continue | ||||
| 				) | ||||
| 			} | ||||
| 		case dto.MetricType_UNTYPED: | ||||
| 			if dtoMetric.Untyped == nil { | ||||
| 					errs = append(errs, fmt.Errorf( | ||||
| 				return fmt.Errorf( | ||||
| 					"collected metric %s %s should be Untyped", | ||||
| 					desc.fqName, dtoMetric, | ||||
| 					)) | ||||
| 					continue | ||||
| 				) | ||||
| 			} | ||||
| 		case dto.MetricType_HISTOGRAM: | ||||
| 			if dtoMetric.Histogram == nil { | ||||
| 					errs = append(errs, fmt.Errorf( | ||||
| 				return fmt.Errorf( | ||||
| 					"collected metric %s %s should be a Histogram", | ||||
| 					desc.fqName, dtoMetric, | ||||
| 					)) | ||||
| 					continue | ||||
| 				) | ||||
| 			} | ||||
| 		default: | ||||
| 			panic("encountered MetricFamily with invalid type") | ||||
|  | @ -484,34 +537,27 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { | |||
| 		case dtoMetric.Histogram != nil: | ||||
| 			metricFamily.Type = dto.MetricType_HISTOGRAM.Enum() | ||||
| 		default: | ||||
| 				errs = append(errs, fmt.Errorf( | ||||
| 					"empty metric collected: %s", dtoMetric, | ||||
| 				)) | ||||
| 				continue | ||||
| 			return fmt.Errorf("empty metric collected: %s", dtoMetric) | ||||
| 		} | ||||
| 		metricFamiliesByName[desc.fqName] = metricFamily | ||||
| 	} | ||||
| 	if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil { | ||||
| 			errs = append(errs, err) | ||||
| 			continue | ||||
| 		return err | ||||
| 	} | ||||
| 		if r.pedanticChecksEnabled { | ||||
| 	if registeredDescIDs != nil { | ||||
| 		// Is the desc registered at all?
 | ||||
| 		if _, exist := registeredDescIDs[desc.id]; !exist { | ||||
| 				errs = append(errs, fmt.Errorf( | ||||
| 			return fmt.Errorf( | ||||
| 				"collected metric %s %s with unregistered descriptor %s", | ||||
| 				metricFamily.GetName(), dtoMetric, desc, | ||||
| 				)) | ||||
| 				continue | ||||
| 			) | ||||
| 		} | ||||
| 		if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil { | ||||
| 				errs = append(errs, err) | ||||
| 				continue | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	metricFamily.Metric = append(metricFamily.Metric, dtoMetric) | ||||
| 	} | ||||
| 	return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Gatherers is a slice of Gatherer instances that implements the Gatherer
 | ||||
|  | @ -655,7 +701,7 @@ func normalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) | |||
| 
 | ||||
| // checkMetricConsistency checks if the provided Metric is consistent with the
 | ||||
| // provided MetricFamily. It also hashed the Metric labels and the MetricFamily
 | ||||
| // name. If the resulting hash is alread in the provided metricHashes, an error
 | ||||
| // name. If the resulting hash is already in the provided metricHashes, an error
 | ||||
| // is returned. If not, it is added to metricHashes. The provided dimHashes maps
 | ||||
| // MetricFamily names to their dimHash (hashed sorted label names). If dimHashes
 | ||||
| // doesn't yet contain a hash for the provided MetricFamily, it is
 | ||||
|  | @ -679,6 +725,12 @@ func checkMetricConsistency( | |||
| 		) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, labelPair := range dtoMetric.GetLabel() { | ||||
| 		if !utf8.ValidString(*labelPair.Value) { | ||||
| 			return fmt.Errorf("collected metric's label %s is not utf8: %#v", *labelPair.Name, *labelPair.Value) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Is the metric unique (i.e. no other metric with the same name and the same label values)?
 | ||||
| 	h := hashNew() | ||||
| 	h = hashAdd(h, metricFamily.GetName()) | ||||
|  |  | |||
|  | @ -36,7 +36,10 @@ const quantileLabel = "quantile" | |||
| //
 | ||||
| // A typical use-case is the observation of request latencies. By default, a
 | ||||
| // Summary provides the median, the 90th and the 99th percentile of the latency
 | ||||
| // as rank estimations.
 | ||||
| // as rank estimations. However, the default behavior will change in the
 | ||||
| // upcoming v0.10 of the library. There will be no rank estiamtions at all by
 | ||||
| // default. For a sane transition, it is recommended to set the desired rank
 | ||||
| // estimations explicitly.
 | ||||
| //
 | ||||
| // Note that the rank estimations cannot be aggregated in a meaningful way with
 | ||||
| // the Prometheus query language (i.e. you cannot average or add them). If you
 | ||||
|  | @ -78,8 +81,10 @@ const ( | |||
| ) | ||||
| 
 | ||||
| // SummaryOpts bundles the options for creating a Summary metric. It is
 | ||||
| // mandatory to set Name and Help to a non-empty string. All other fields are
 | ||||
| // optional and can safely be left at their zero value.
 | ||||
| // mandatory to set Name and Help to a non-empty string. While all other fields
 | ||||
| // are optional and can safely be left at their zero value, it is recommended to
 | ||||
| // explicitly set the Objectives field to the desired value as the default value
 | ||||
| // will change in the upcoming v0.10 of the library.
 | ||||
| type SummaryOpts struct { | ||||
| 	// Namespace, Subsystem, and Name are components of the fully-qualified
 | ||||
| 	// name of the Summary (created by joining these components with
 | ||||
|  | @ -96,23 +101,16 @@ type SummaryOpts struct { | |||
| 	// string.
 | ||||
| 	Help string | ||||
| 
 | ||||
| 	// ConstLabels are used to attach fixed labels to this
 | ||||
| 	// Summary. Summaries with the same fully-qualified name must have the
 | ||||
| 	// same label names in their ConstLabels.
 | ||||
| 	// ConstLabels are used to attach fixed labels to this metric. Metrics
 | ||||
| 	// with the same fully-qualified name must have the same label names in
 | ||||
| 	// their ConstLabels.
 | ||||
| 	//
 | ||||
| 	// Note that in most cases, labels have a value that varies during the
 | ||||
| 	// lifetime of a process. Those labels are usually managed with a
 | ||||
| 	// SummaryVec. ConstLabels serve only special purposes. One is for the
 | ||||
| 	// special case where the value of a label does not change during the
 | ||||
| 	// lifetime of a process, e.g. if the revision of the running binary is
 | ||||
| 	// put into a label. Another, more advanced purpose is if more than one
 | ||||
| 	// Collector needs to collect Summaries with the same fully-qualified
 | ||||
| 	// name. In that case, those Summaries must differ in the values of
 | ||||
| 	// their ConstLabels. See the Collector examples.
 | ||||
| 	//
 | ||||
| 	// If the value of a label never changes (not even between binaries),
 | ||||
| 	// that label most likely should not be a label at all (but part of the
 | ||||
| 	// metric name).
 | ||||
| 	// ConstLabels are only used rarely. In particular, do not use them to
 | ||||
| 	// attach the same labels to all your metrics. Those use cases are
 | ||||
| 	// better covered by target labels set by the scraping Prometheus
 | ||||
| 	// server, or by one specific metric (e.g. a build_info or a
 | ||||
| 	// machine_role metric). See also
 | ||||
| 	// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
 | ||||
| 	ConstLabels Labels | ||||
| 
 | ||||
| 	// Objectives defines the quantile rank estimates with their respective
 | ||||
|  | @ -399,12 +397,11 @@ func (s quantSort) Less(i, j int) bool { | |||
| // (e.g. HTTP request latencies, partitioned by status code and method). Create
 | ||||
| // instances with NewSummaryVec.
 | ||||
| type SummaryVec struct { | ||||
| 	*MetricVec | ||||
| 	*metricVec | ||||
| } | ||||
| 
 | ||||
| // NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
 | ||||
| // partitioned by the given label names. At least one label name must be
 | ||||
| // provided.
 | ||||
| // partitioned by the given label names.
 | ||||
| func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
|  | @ -413,47 +410,116 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec { | |||
| 		opts.ConstLabels, | ||||
| 	) | ||||
| 	return &SummaryVec{ | ||||
| 		MetricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 		metricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			return newSummary(desc, opts, lvs...) | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetMetricWithLabelValues replaces the method of the same name in
 | ||||
| // MetricVec. The difference is that this method returns a Summary and not a
 | ||||
| // Metric so that no type conversion is required.
 | ||||
| func (m *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Summary, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) | ||||
| // GetMetricWithLabelValues returns the Summary for the given slice of label
 | ||||
| // values (same order as the VariableLabels in Desc). If that combination of
 | ||||
| // label values is accessed for the first time, a new Summary is created.
 | ||||
| //
 | ||||
| // It is possible to call this method without using the returned Summary to only
 | ||||
| // create the new Summary but leave it at its starting value, a Summary without
 | ||||
| // any observations.
 | ||||
| //
 | ||||
| // Keeping the Summary for later use is possible (and should be considered if
 | ||||
| // performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | ||||
| // Delete can be used to delete the Summary from the SummaryVec. In that case,
 | ||||
| // the Summary will still exist, but it will not be exported anymore, even if a
 | ||||
| // Summary with the same label values is created later. See also the CounterVec
 | ||||
| // example.
 | ||||
| //
 | ||||
| // An error is returned if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | ||||
| // an alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| // See also the GaugeVec example.
 | ||||
| func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { | ||||
| 	metric, err := v.metricVec.getMetricWithLabelValues(lvs...) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Summary), err | ||||
| 		return metric.(Observer), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // GetMetricWith replaces the method of the same name in MetricVec. The
 | ||||
| // difference is that this method returns a Summary and not a Metric so that no
 | ||||
| // type conversion is required.
 | ||||
| func (m *SummaryVec) GetMetricWith(labels Labels) (Summary, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWith(labels) | ||||
| // GetMetricWith returns the Summary for the given Labels map (the label names
 | ||||
| // must match those of the VariableLabels in Desc). If that label map is
 | ||||
| // accessed for the first time, a new Summary is created. Implications of
 | ||||
| // creating a Summary without using it and keeping the Summary for later use are
 | ||||
| // the same as for GetMetricWithLabelValues.
 | ||||
| //
 | ||||
| // An error is returned if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in Desc (minus any curried labels).
 | ||||
| //
 | ||||
| // This method is used for the same purpose as
 | ||||
| // GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | ||||
| // methods.
 | ||||
| func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) { | ||||
| 	metric, err := v.metricVec.getMetricWith(labels) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Summary), err | ||||
| 		return metric.(Observer), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // WithLabelValues works as GetMetricWithLabelValues, but panics where
 | ||||
| // GetMetricWithLabelValues would have returned an error. By not returning an
 | ||||
| // error, WithLabelValues allows shortcuts like
 | ||||
| // GetMetricWithLabelValues would have returned an error. Not returning an
 | ||||
| // error allows shortcuts like
 | ||||
| //     myVec.WithLabelValues("404", "GET").Observe(42.21)
 | ||||
| func (m *SummaryVec) WithLabelValues(lvs ...string) Summary { | ||||
| 	return m.MetricVec.WithLabelValues(lvs...).(Summary) | ||||
| func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { | ||||
| 	s, err := v.GetMetricWithLabelValues(lvs...) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | ||||
| // returned an error. By not returning an error, With allows shortcuts like
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
 | ||||
| func (m *SummaryVec) With(labels Labels) Summary { | ||||
| 	return m.MetricVec.With(labels).(Summary) | ||||
| // returned an error. Not returning an error allows shortcuts like
 | ||||
| //     myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21)
 | ||||
| func (v *SummaryVec) With(labels Labels) Observer { | ||||
| 	s, err := v.GetMetricWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // CurryWith returns a vector curried with the provided labels, i.e. the
 | ||||
| // returned vector has those labels pre-set for all labeled operations performed
 | ||||
| // on it. The cardinality of the curried vector is reduced accordingly. The
 | ||||
| // order of the remaining labels stays the same (just with the curried labels
 | ||||
| // taken out of the sequence – which is relevant for the
 | ||||
| // (GetMetric)WithLabelValues methods). It is possible to curry a curried
 | ||||
| // vector, but only with labels not yet used for currying before.
 | ||||
| //
 | ||||
| // The metrics contained in the SummaryVec are shared between the curried and
 | ||||
| // uncurried vectors. They are just accessed differently. Curried and uncurried
 | ||||
| // vectors behave identically in terms of collection. Only one must be
 | ||||
| // registered with a given registry (usually the uncurried version). The Reset
 | ||||
| // method deletes all metrics, even if called on a curried vector.
 | ||||
| func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) { | ||||
| 	vec, err := v.curryWith(labels) | ||||
| 	if vec != nil { | ||||
| 		return &SummaryVec{vec}, err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // MustCurryWith works as CurryWith but panics where CurryWith would have
 | ||||
| // returned an error.
 | ||||
| func (v *SummaryVec) MustCurryWith(labels Labels) ObserverVec { | ||||
| 	vec, err := v.CurryWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return vec | ||||
| } | ||||
| 
 | ||||
| type constSummary struct { | ||||
|  | @ -514,8 +580,8 @@ func NewConstSummary( | |||
| 	quantiles map[float64]float64, | ||||
| 	labelValues ...string, | ||||
| ) (Metric, error) { | ||||
| 	if len(desc.variableLabels) != len(labelValues) { | ||||
| 		return nil, errInconsistentCardinality | ||||
| 	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &constSummary{ | ||||
| 		desc:       desc, | ||||
|  |  | |||
|  | @ -15,32 +15,6 @@ package prometheus | |||
| 
 | ||||
| import "time" | ||||
| 
 | ||||
| // Observer is the interface that wraps the Observe method, which is used by
 | ||||
| // Histogram and Summary to add observations.
 | ||||
| type Observer interface { | ||||
| 	Observe(float64) | ||||
| } | ||||
| 
 | ||||
| // The ObserverFunc type is an adapter to allow the use of ordinary
 | ||||
| // functions as Observers. If f is a function with the appropriate
 | ||||
| // signature, ObserverFunc(f) is an Observer that calls f.
 | ||||
| //
 | ||||
| // This adapter is usually used in connection with the Timer type, and there are
 | ||||
| // two general use cases:
 | ||||
| //
 | ||||
| // The most common one is to use a Gauge as the Observer for a Timer.
 | ||||
| // See the "Gauge" Timer example.
 | ||||
| //
 | ||||
| // The more advanced use case is to create a function that dynamically decides
 | ||||
| // which Observer to use for observing the duration. See the "Complex" Timer
 | ||||
| // example.
 | ||||
| type ObserverFunc func(float64) | ||||
| 
 | ||||
| // Observe calls f(value). It implements Observer.
 | ||||
| func (f ObserverFunc) Observe(value float64) { | ||||
| 	f(value) | ||||
| } | ||||
| 
 | ||||
| // Timer is a helper type to time functions. Use NewTimer to create new
 | ||||
| // instances.
 | ||||
| type Timer struct { | ||||
|  | @ -67,6 +41,9 @@ func NewTimer(o Observer) *Timer { | |||
| // NewTimer. It calls the Observe method of the Observer provided during
 | ||||
| // construction with the duration in seconds as an argument. ObserveDuration is
 | ||||
| // usually called with a defer statement.
 | ||||
| //
 | ||||
| // Note that this method is only guaranteed to never observe negative durations
 | ||||
| // if used with Go1.9+.
 | ||||
| func (t *Timer) ObserveDuration() { | ||||
| 	if t.observer != nil { | ||||
| 		t.observer.Observe(time.Since(t.begin).Seconds()) | ||||
|  |  | |||
|  | @ -13,113 +13,12 @@ | |||
| 
 | ||||
| package prometheus | ||||
| 
 | ||||
| // Untyped is a Metric that represents a single numerical value that can
 | ||||
| // arbitrarily go up and down.
 | ||||
| //
 | ||||
| // An Untyped metric works the same as a Gauge. The only difference is that to
 | ||||
| // no type information is implied.
 | ||||
| //
 | ||||
| // To create Untyped instances, use NewUntyped.
 | ||||
| //
 | ||||
| // Deprecated: The Untyped type is deprecated because it doesn't make sense in
 | ||||
| // direct instrumentation. If you need to mirror an external metric of unknown
 | ||||
| // type (usually while writing exporters), Use MustNewConstMetric to create an
 | ||||
| // untyped metric instance on the fly.
 | ||||
| type Untyped interface { | ||||
| 	Metric | ||||
| 	Collector | ||||
| 
 | ||||
| 	// Set sets the Untyped metric to an arbitrary value.
 | ||||
| 	Set(float64) | ||||
| 	// Inc increments the Untyped metric by 1.
 | ||||
| 	Inc() | ||||
| 	// Dec decrements the Untyped metric by 1.
 | ||||
| 	Dec() | ||||
| 	// Add adds the given value to the Untyped metric. (The value can be
 | ||||
| 	// negative, resulting in a decrease.)
 | ||||
| 	Add(float64) | ||||
| 	// Sub subtracts the given value from the Untyped metric. (The value can
 | ||||
| 	// be negative, resulting in an increase.)
 | ||||
| 	Sub(float64) | ||||
| } | ||||
| 
 | ||||
| // UntypedOpts is an alias for Opts. See there for doc comments.
 | ||||
| type UntypedOpts Opts | ||||
| 
 | ||||
| // NewUntyped creates a new Untyped metric from the provided UntypedOpts.
 | ||||
| func NewUntyped(opts UntypedOpts) Untyped { | ||||
| 	return newValue(NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
| 		opts.Help, | ||||
| 		nil, | ||||
| 		opts.ConstLabels, | ||||
| 	), UntypedValue, 0) | ||||
| } | ||||
| 
 | ||||
| // UntypedVec is a Collector that bundles a set of Untyped metrics that all
 | ||||
| // share the same Desc, but have different values for their variable
 | ||||
| // labels. This is used if you want to count the same thing partitioned by
 | ||||
| // various dimensions. Create instances with NewUntypedVec.
 | ||||
| type UntypedVec struct { | ||||
| 	*MetricVec | ||||
| } | ||||
| 
 | ||||
| // NewUntypedVec creates a new UntypedVec based on the provided UntypedOpts and
 | ||||
| // partitioned by the given label names. At least one label name must be
 | ||||
| // provided.
 | ||||
| func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec { | ||||
| 	desc := NewDesc( | ||||
| 		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||||
| 		opts.Help, | ||||
| 		labelNames, | ||||
| 		opts.ConstLabels, | ||||
| 	) | ||||
| 	return &UntypedVec{ | ||||
| 		MetricVec: newMetricVec(desc, func(lvs ...string) Metric { | ||||
| 			return newValue(desc, UntypedValue, 0, lvs...) | ||||
| 		}), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetMetricWithLabelValues replaces the method of the same name in
 | ||||
| // MetricVec. The difference is that this method returns an Untyped and not a
 | ||||
| // Metric so that no type conversion is required.
 | ||||
| func (m *UntypedVec) GetMetricWithLabelValues(lvs ...string) (Untyped, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Untyped), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // GetMetricWith replaces the method of the same name in MetricVec. The
 | ||||
| // difference is that this method returns an Untyped and not a Metric so that no
 | ||||
| // type conversion is required.
 | ||||
| func (m *UntypedVec) GetMetricWith(labels Labels) (Untyped, error) { | ||||
| 	metric, err := m.MetricVec.GetMetricWith(labels) | ||||
| 	if metric != nil { | ||||
| 		return metric.(Untyped), err | ||||
| 	} | ||||
| 	return nil, err | ||||
| } | ||||
| 
 | ||||
| // WithLabelValues works as GetMetricWithLabelValues, but panics where
 | ||||
| // GetMetricWithLabelValues would have returned an error. By not returning an
 | ||||
| // error, WithLabelValues allows shortcuts like
 | ||||
| //     myVec.WithLabelValues("404", "GET").Add(42)
 | ||||
| func (m *UntypedVec) WithLabelValues(lvs ...string) Untyped { | ||||
| 	return m.MetricVec.WithLabelValues(lvs...).(Untyped) | ||||
| } | ||||
| 
 | ||||
| // With works as GetMetricWith, but panics where GetMetricWithLabels would have
 | ||||
| // returned an error. By not returning an error, With allows shortcuts like
 | ||||
| //     myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
 | ||||
| func (m *UntypedVec) With(labels Labels) Untyped { | ||||
| 	return m.MetricVec.With(labels).(Untyped) | ||||
| } | ||||
| 
 | ||||
| // UntypedFunc is an Untyped whose value is determined at collect time by
 | ||||
| // calling a provided function.
 | ||||
| // UntypedFunc works like GaugeFunc but the collected metric is of type
 | ||||
| // "Untyped". UntypedFunc is useful to mirror an external metric of unknown
 | ||||
| // type.
 | ||||
| //
 | ||||
| // To create UntypedFunc instances, use NewUntypedFunc.
 | ||||
| type UntypedFunc interface { | ||||
|  |  | |||
|  | @ -14,12 +14,8 @@ | |||
| package prometheus | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"sort" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| 
 | ||||
| 	dto "github.com/prometheus/client_model/go" | ||||
| 
 | ||||
|  | @ -37,81 +33,6 @@ const ( | |||
| 	UntypedValue | ||||
| ) | ||||
| 
 | ||||
| var errInconsistentCardinality = errors.New("inconsistent label cardinality") | ||||
| 
 | ||||
| // value is a generic metric for simple values. It implements Metric, Collector,
 | ||||
| // Counter, Gauge, and Untyped. Its effective type is determined by
 | ||||
| // ValueType. This is a low-level building block used by the library to back the
 | ||||
| // implementations of Counter, Gauge, and Untyped.
 | ||||
| type value struct { | ||||
| 	// valBits contains the bits of the represented float64 value. It has
 | ||||
| 	// to go first in the struct to guarantee alignment for atomic
 | ||||
| 	// operations.  http://golang.org/pkg/sync/atomic/#pkg-note-BUG
 | ||||
| 	valBits uint64 | ||||
| 
 | ||||
| 	selfCollector | ||||
| 
 | ||||
| 	desc       *Desc | ||||
| 	valType    ValueType | ||||
| 	labelPairs []*dto.LabelPair | ||||
| } | ||||
| 
 | ||||
| // newValue returns a newly allocated value with the given Desc, ValueType,
 | ||||
| // sample value and label values. It panics if the number of label
 | ||||
| // values is different from the number of variable labels in Desc.
 | ||||
| func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value { | ||||
| 	if len(labelValues) != len(desc.variableLabels) { | ||||
| 		panic(errInconsistentCardinality) | ||||
| 	} | ||||
| 	result := &value{ | ||||
| 		desc:       desc, | ||||
| 		valType:    valueType, | ||||
| 		valBits:    math.Float64bits(val), | ||||
| 		labelPairs: makeLabelPairs(desc, labelValues), | ||||
| 	} | ||||
| 	result.init(result) | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| func (v *value) Desc() *Desc { | ||||
| 	return v.desc | ||||
| } | ||||
| 
 | ||||
| func (v *value) Set(val float64) { | ||||
| 	atomic.StoreUint64(&v.valBits, math.Float64bits(val)) | ||||
| } | ||||
| 
 | ||||
| func (v *value) SetToCurrentTime() { | ||||
| 	v.Set(float64(time.Now().UnixNano()) / 1e9) | ||||
| } | ||||
| 
 | ||||
| func (v *value) Inc() { | ||||
| 	v.Add(1) | ||||
| } | ||||
| 
 | ||||
| func (v *value) Dec() { | ||||
| 	v.Add(-1) | ||||
| } | ||||
| 
 | ||||
| func (v *value) Add(val float64) { | ||||
| 	for { | ||||
| 		oldBits := atomic.LoadUint64(&v.valBits) | ||||
| 		newBits := math.Float64bits(math.Float64frombits(oldBits) + val) | ||||
| 		if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (v *value) Sub(val float64) { | ||||
| 	v.Add(val * -1) | ||||
| } | ||||
| 
 | ||||
| func (v *value) Write(out *dto.Metric) error { | ||||
| 	val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) | ||||
| 	return populateMetric(v.valType, val, v.labelPairs, out) | ||||
| } | ||||
| 
 | ||||
| // valueFunc is a generic metric for simple values retrieved on collect time
 | ||||
| // from a function. It implements Metric and Collector. Its effective type is
 | ||||
| // determined by ValueType. This is a low-level building block used by the
 | ||||
|  | @ -158,8 +79,8 @@ func (v *valueFunc) Write(out *dto.Metric) error { | |||
| // the Collect method. NewConstMetric returns an error if the length of
 | ||||
| // labelValues is not consistent with the variable labels in Desc.
 | ||||
| func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) { | ||||
| 	if len(desc.variableLabels) != len(labelValues) { | ||||
| 		return nil, errInconsistentCardinality | ||||
| 	if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &constMetric{ | ||||
| 		desc:       desc, | ||||
|  |  | |||
|  | @ -20,33 +20,180 @@ import ( | |||
| 	"github.com/prometheus/common/model" | ||||
| ) | ||||
| 
 | ||||
| // MetricVec is a Collector to bundle metrics of the same name that
 | ||||
| // differ in their label values. MetricVec is usually not used directly but as a
 | ||||
| // building block for implementations of vectors of a given metric
 | ||||
| // type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
 | ||||
| // provided in this package.
 | ||||
| type MetricVec struct { | ||||
| 	mtx      sync.RWMutex // Protects the children.
 | ||||
| 	children map[uint64][]metricWithLabelValues | ||||
| 	desc     *Desc | ||||
| // metricVec is a Collector to bundle metrics of the same name that differ in
 | ||||
| // their label values. metricVec is not used directly (and therefore
 | ||||
| // unexported). It is used as a building block for implementations of vectors of
 | ||||
| // a given metric type, like GaugeVec, CounterVec, SummaryVec, and HistogramVec.
 | ||||
| // It also handles label currying. It uses basicMetricVec internally.
 | ||||
| type metricVec struct { | ||||
| 	*metricMap | ||||
| 
 | ||||
| 	newMetric   func(labelValues ...string) Metric | ||||
| 	hashAdd     func(h uint64, s string) uint64 // replace hash function for testing collision handling
 | ||||
| 	curry []curriedLabelValue | ||||
| 
 | ||||
| 	// hashAdd and hashAddByte can be replaced for testing collision handling.
 | ||||
| 	hashAdd     func(h uint64, s string) uint64 | ||||
| 	hashAddByte func(h uint64, b byte) uint64 | ||||
| } | ||||
| 
 | ||||
| // newMetricVec returns an initialized MetricVec. The concrete value is
 | ||||
| // returned for embedding into another struct.
 | ||||
| func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { | ||||
| 	return &MetricVec{ | ||||
| 		children:    map[uint64][]metricWithLabelValues{}, | ||||
| // newMetricVec returns an initialized metricVec.
 | ||||
| func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec { | ||||
| 	return &metricVec{ | ||||
| 		metricMap: &metricMap{ | ||||
| 			metrics:   map[uint64][]metricWithLabelValues{}, | ||||
| 			desc:      desc, | ||||
| 			newMetric: newMetric, | ||||
| 		}, | ||||
| 		hashAdd:     hashAdd, | ||||
| 		hashAddByte: hashAddByte, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // DeleteLabelValues removes the metric where the variable labels are the same
 | ||||
| // as those passed in as labels (same order as the VariableLabels in Desc). It
 | ||||
| // returns true if a metric was deleted.
 | ||||
| //
 | ||||
| // It is not an error if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc. However, such inconsistent label count can
 | ||||
| // never match an actual metric, so the method will always return false in that
 | ||||
| // case.
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider Delete(Labels) as an
 | ||||
| // alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| // See also the CounterVec example.
 | ||||
| func (m *metricVec) DeleteLabelValues(lvs ...string) bool { | ||||
| 	h, err := m.hashLabelValues(lvs) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return m.metricMap.deleteByHashWithLabelValues(h, lvs, m.curry) | ||||
| } | ||||
| 
 | ||||
| // Delete deletes the metric where the variable labels are the same as those
 | ||||
| // passed in as labels. It returns true if a metric was deleted.
 | ||||
| //
 | ||||
| // It is not an error if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in Desc. However, such inconsistent Labels
 | ||||
| // can never match an actual metric, so the method will always return false in
 | ||||
| // that case.
 | ||||
| //
 | ||||
| // This method is used for the same purpose as DeleteLabelValues(...string). See
 | ||||
| // there for pros and cons of the two methods.
 | ||||
| func (m *metricVec) Delete(labels Labels) bool { | ||||
| 	h, err := m.hashLabels(labels) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return m.metricMap.deleteByHashWithLabels(h, labels, m.curry) | ||||
| } | ||||
| 
 | ||||
| func (m *metricVec) curryWith(labels Labels) (*metricVec, error) { | ||||
| 	var ( | ||||
| 		newCurry []curriedLabelValue | ||||
| 		oldCurry = m.curry | ||||
| 		iCurry   int | ||||
| 	) | ||||
| 	for i, label := range m.desc.variableLabels { | ||||
| 		val, ok := labels[label] | ||||
| 		if iCurry < len(oldCurry) && oldCurry[iCurry].index == i { | ||||
| 			if ok { | ||||
| 				return nil, fmt.Errorf("label name %q is already curried", label) | ||||
| 			} | ||||
| 			newCurry = append(newCurry, oldCurry[iCurry]) | ||||
| 			iCurry++ | ||||
| 		} else { | ||||
| 			if !ok { | ||||
| 				continue // Label stays uncurried.
 | ||||
| 			} | ||||
| 			newCurry = append(newCurry, curriedLabelValue{i, val}) | ||||
| 		} | ||||
| 	} | ||||
| 	if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 { | ||||
| 		return nil, fmt.Errorf("%d unknown label(s) found during currying", l) | ||||
| 	} | ||||
| 
 | ||||
| 	return &metricVec{ | ||||
| 		metricMap:   m.metricMap, | ||||
| 		curry:       newCurry, | ||||
| 		hashAdd:     m.hashAdd, | ||||
| 		hashAddByte: m.hashAddByte, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) { | ||||
| 	h, err := m.hashLabelValues(lvs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), nil | ||||
| } | ||||
| 
 | ||||
| func (m *metricVec) getMetricWith(labels Labels) (Metric, error) { | ||||
| 	h, err := m.hashLabels(labels) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil | ||||
| } | ||||
| 
 | ||||
| func (m *metricVec) hashLabelValues(vals []string) (uint64, error) { | ||||
| 	if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 
 | ||||
| 	var ( | ||||
| 		h             = hashNew() | ||||
| 		curry         = m.curry | ||||
| 		iVals, iCurry int | ||||
| 	) | ||||
| 	for i := 0; i < len(m.desc.variableLabels); i++ { | ||||
| 		if iCurry < len(curry) && curry[iCurry].index == i { | ||||
| 			h = m.hashAdd(h, curry[iCurry].value) | ||||
| 			iCurry++ | ||||
| 		} else { | ||||
| 			h = m.hashAdd(h, vals[iVals]) | ||||
| 			iVals++ | ||||
| 		} | ||||
| 		h = m.hashAddByte(h, model.SeparatorByte) | ||||
| 	} | ||||
| 	return h, nil | ||||
| } | ||||
| 
 | ||||
| func (m *metricVec) hashLabels(labels Labels) (uint64, error) { | ||||
| 	if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 
 | ||||
| 	var ( | ||||
| 		h      = hashNew() | ||||
| 		curry  = m.curry | ||||
| 		iCurry int | ||||
| 	) | ||||
| 	for i, label := range m.desc.variableLabels { | ||||
| 		val, ok := labels[label] | ||||
| 		if iCurry < len(curry) && curry[iCurry].index == i { | ||||
| 			if ok { | ||||
| 				return 0, fmt.Errorf("label name %q is already curried", label) | ||||
| 			} | ||||
| 			h = m.hashAdd(h, curry[iCurry].value) | ||||
| 			iCurry++ | ||||
| 		} else { | ||||
| 			if !ok { | ||||
| 				return 0, fmt.Errorf("label name %q missing in label map", label) | ||||
| 			} | ||||
| 			h = m.hashAdd(h, val) | ||||
| 		} | ||||
| 		h = m.hashAddByte(h, model.SeparatorByte) | ||||
| 	} | ||||
| 	return h, nil | ||||
| } | ||||
| 
 | ||||
| // metricWithLabelValues provides the metric and its label values for
 | ||||
| // disambiguation on hash collision.
 | ||||
| type metricWithLabelValues struct { | ||||
|  | @ -54,166 +201,72 @@ type metricWithLabelValues struct { | |||
| 	metric Metric | ||||
| } | ||||
| 
 | ||||
| // Describe implements Collector. The length of the returned slice
 | ||||
| // is always one.
 | ||||
| func (m *MetricVec) Describe(ch chan<- *Desc) { | ||||
| // curriedLabelValue sets the curried value for a label at the given index.
 | ||||
| type curriedLabelValue struct { | ||||
| 	index int | ||||
| 	value string | ||||
| } | ||||
| 
 | ||||
| // metricMap is a helper for metricVec and shared between differently curried
 | ||||
| // metricVecs.
 | ||||
| type metricMap struct { | ||||
| 	mtx       sync.RWMutex // Protects metrics.
 | ||||
| 	metrics   map[uint64][]metricWithLabelValues | ||||
| 	desc      *Desc | ||||
| 	newMetric func(labelValues ...string) Metric | ||||
| } | ||||
| 
 | ||||
| // Describe implements Collector. It will send exactly one Desc to the provided
 | ||||
| // channel.
 | ||||
| func (m *metricMap) Describe(ch chan<- *Desc) { | ||||
| 	ch <- m.desc | ||||
| } | ||||
| 
 | ||||
| // Collect implements Collector.
 | ||||
| func (m *MetricVec) Collect(ch chan<- Metric) { | ||||
| func (m *metricMap) Collect(ch chan<- Metric) { | ||||
| 	m.mtx.RLock() | ||||
| 	defer m.mtx.RUnlock() | ||||
| 
 | ||||
| 	for _, metrics := range m.children { | ||||
| 	for _, metrics := range m.metrics { | ||||
| 		for _, metric := range metrics { | ||||
| 			ch <- metric.metric | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetMetricWithLabelValues returns the Metric for the given slice of label
 | ||||
| // values (same order as the VariableLabels in Desc). If that combination of
 | ||||
| // label values is accessed for the first time, a new Metric is created.
 | ||||
| //
 | ||||
| // It is possible to call this method without using the returned Metric to only
 | ||||
| // create the new Metric but leave it at its start value (e.g. a Summary or
 | ||||
| // Histogram without any observations). See also the SummaryVec example.
 | ||||
| //
 | ||||
| // Keeping the Metric for later use is possible (and should be considered if
 | ||||
| // performance is critical), but keep in mind that Reset, DeleteLabelValues and
 | ||||
| // Delete can be used to delete the Metric from the MetricVec. In that case, the
 | ||||
| // Metric will still exist, but it will not be exported anymore, even if a
 | ||||
| // Metric with the same label values is created later. See also the CounterVec
 | ||||
| // example.
 | ||||
| //
 | ||||
| // An error is returned if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc.
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
 | ||||
| // an alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| // See also the GaugeVec example.
 | ||||
| func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { | ||||
| 	h, err := m.hashLabelValues(lvs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return m.getOrCreateMetricWithLabelValues(h, lvs), nil | ||||
| } | ||||
| 
 | ||||
| // GetMetricWith returns the Metric for the given Labels map (the label names
 | ||||
| // must match those of the VariableLabels in Desc). If that label map is
 | ||||
| // accessed for the first time, a new Metric is created. Implications of
 | ||||
| // creating a Metric without using it and keeping the Metric for later use are
 | ||||
| // the same as for GetMetricWithLabelValues.
 | ||||
| //
 | ||||
| // An error is returned if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in Desc.
 | ||||
| //
 | ||||
| // This method is used for the same purpose as
 | ||||
| // GetMetricWithLabelValues(...string). See there for pros and cons of the two
 | ||||
| // methods.
 | ||||
| func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { | ||||
| 	h, err := m.hashLabels(labels) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return m.getOrCreateMetricWithLabels(h, labels), nil | ||||
| } | ||||
| 
 | ||||
| // WithLabelValues works as GetMetricWithLabelValues, but panics if an error
 | ||||
| // occurs. The method allows neat syntax like:
 | ||||
| //     httpReqs.WithLabelValues("404", "POST").Inc()
 | ||||
| func (m *MetricVec) WithLabelValues(lvs ...string) Metric { | ||||
| 	metric, err := m.GetMetricWithLabelValues(lvs...) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return metric | ||||
| } | ||||
| 
 | ||||
| // With works as GetMetricWith, but panics if an error occurs. The method allows
 | ||||
| // neat syntax like:
 | ||||
| //     httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
 | ||||
| func (m *MetricVec) With(labels Labels) Metric { | ||||
| 	metric, err := m.GetMetricWith(labels) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return metric | ||||
| } | ||||
| 
 | ||||
| // DeleteLabelValues removes the metric where the variable labels are the same
 | ||||
| // as those passed in as labels (same order as the VariableLabels in Desc). It
 | ||||
| // returns true if a metric was deleted.
 | ||||
| //
 | ||||
| // It is not an error if the number of label values is not the same as the
 | ||||
| // number of VariableLabels in Desc.  However, such inconsistent label count can
 | ||||
| // never match an actual Metric, so the method will always return false in that
 | ||||
| // case.
 | ||||
| //
 | ||||
| // Note that for more than one label value, this method is prone to mistakes
 | ||||
| // caused by an incorrect order of arguments. Consider Delete(Labels) as an
 | ||||
| // alternative to avoid that type of mistake. For higher label numbers, the
 | ||||
| // latter has a much more readable (albeit more verbose) syntax, but it comes
 | ||||
| // with a performance overhead (for creating and processing the Labels map).
 | ||||
| // See also the CounterVec example.
 | ||||
| func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { | ||||
| // Reset deletes all metrics in this vector.
 | ||||
| func (m *metricMap) Reset() { | ||||
| 	m.mtx.Lock() | ||||
| 	defer m.mtx.Unlock() | ||||
| 
 | ||||
| 	h, err := m.hashLabelValues(lvs) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	for h := range m.metrics { | ||||
| 		delete(m.metrics, h) | ||||
| 	} | ||||
| 	return m.deleteByHashWithLabelValues(h, lvs) | ||||
| } | ||||
| 
 | ||||
| // Delete deletes the metric where the variable labels are the same as those
 | ||||
| // passed in as labels. It returns true if a metric was deleted.
 | ||||
| //
 | ||||
| // It is not an error if the number and names of the Labels are inconsistent
 | ||||
| // with those of the VariableLabels in the Desc of the MetricVec. However, such
 | ||||
| // inconsistent Labels can never match an actual Metric, so the method will
 | ||||
| // always return false in that case.
 | ||||
| //
 | ||||
| // This method is used for the same purpose as DeleteLabelValues(...string). See
 | ||||
| // there for pros and cons of the two methods.
 | ||||
| func (m *MetricVec) Delete(labels Labels) bool { | ||||
| 	m.mtx.Lock() | ||||
| 	defer m.mtx.Unlock() | ||||
| 
 | ||||
| 	h, err := m.hashLabels(labels) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return m.deleteByHashWithLabels(h, labels) | ||||
| } | ||||
| 
 | ||||
| // deleteByHashWithLabelValues removes the metric from the hash bucket h. If
 | ||||
| // there are multiple matches in the bucket, use lvs to select a metric and
 | ||||
| // remove only that metric.
 | ||||
| func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool { | ||||
| 	metrics, ok := m.children[h] | ||||
| func (m *metricMap) deleteByHashWithLabelValues( | ||||
| 	h uint64, lvs []string, curry []curriedLabelValue, | ||||
| ) bool { | ||||
| 	m.mtx.Lock() | ||||
| 	defer m.mtx.Unlock() | ||||
| 
 | ||||
| 	metrics, ok := m.metrics[h] | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	i := m.findMetricWithLabelValues(metrics, lvs) | ||||
| 	i := findMetricWithLabelValues(metrics, lvs, curry) | ||||
| 	if i >= len(metrics) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if len(metrics) > 1 { | ||||
| 		m.children[h] = append(metrics[:i], metrics[i+1:]...) | ||||
| 		m.metrics[h] = append(metrics[:i], metrics[i+1:]...) | ||||
| 	} else { | ||||
| 		delete(m.children, h) | ||||
| 		delete(m.metrics, h) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | @ -221,69 +274,35 @@ func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool { | |||
| // deleteByHashWithLabels removes the metric from the hash bucket h. If there
 | ||||
| // are multiple matches in the bucket, use lvs to select a metric and remove
 | ||||
| // only that metric.
 | ||||
| func (m *MetricVec) deleteByHashWithLabels(h uint64, labels Labels) bool { | ||||
| 	metrics, ok := m.children[h] | ||||
| func (m *metricMap) deleteByHashWithLabels( | ||||
| 	h uint64, labels Labels, curry []curriedLabelValue, | ||||
| ) bool { | ||||
| 	metrics, ok := m.metrics[h] | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
| 	i := m.findMetricWithLabels(metrics, labels) | ||||
| 	i := findMetricWithLabels(m.desc, metrics, labels, curry) | ||||
| 	if i >= len(metrics) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if len(metrics) > 1 { | ||||
| 		m.children[h] = append(metrics[:i], metrics[i+1:]...) | ||||
| 		m.metrics[h] = append(metrics[:i], metrics[i+1:]...) | ||||
| 	} else { | ||||
| 		delete(m.children, h) | ||||
| 		delete(m.metrics, h) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Reset deletes all metrics in this vector.
 | ||||
| func (m *MetricVec) Reset() { | ||||
| 	m.mtx.Lock() | ||||
| 	defer m.mtx.Unlock() | ||||
| 
 | ||||
| 	for h := range m.children { | ||||
| 		delete(m.children, h) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { | ||||
| 	if len(vals) != len(m.desc.variableLabels) { | ||||
| 		return 0, errInconsistentCardinality | ||||
| 	} | ||||
| 	h := hashNew() | ||||
| 	for _, val := range vals { | ||||
| 		h = m.hashAdd(h, val) | ||||
| 		h = m.hashAddByte(h, model.SeparatorByte) | ||||
| 	} | ||||
| 	return h, nil | ||||
| } | ||||
| 
 | ||||
| func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { | ||||
| 	if len(labels) != len(m.desc.variableLabels) { | ||||
| 		return 0, errInconsistentCardinality | ||||
| 	} | ||||
| 	h := hashNew() | ||||
| 	for _, label := range m.desc.variableLabels { | ||||
| 		val, ok := labels[label] | ||||
| 		if !ok { | ||||
| 			return 0, fmt.Errorf("label name %q missing in label map", label) | ||||
| 		} | ||||
| 		h = m.hashAdd(h, val) | ||||
| 		h = m.hashAddByte(h, model.SeparatorByte) | ||||
| 	} | ||||
| 	return h, nil | ||||
| } | ||||
| 
 | ||||
| // getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
 | ||||
| // or creates it and returns the new one.
 | ||||
| //
 | ||||
| // This function holds the mutex.
 | ||||
| func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric { | ||||
| func (m *metricMap) getOrCreateMetricWithLabelValues( | ||||
| 	hash uint64, lvs []string, curry []curriedLabelValue, | ||||
| ) Metric { | ||||
| 	m.mtx.RLock() | ||||
| 	metric, ok := m.getMetricWithLabelValues(hash, lvs) | ||||
| 	metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs, curry) | ||||
| 	m.mtx.RUnlock() | ||||
| 	if ok { | ||||
| 		return metric | ||||
|  | @ -291,13 +310,11 @@ func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) | |||
| 
 | ||||
| 	m.mtx.Lock() | ||||
| 	defer m.mtx.Unlock() | ||||
| 	metric, ok = m.getMetricWithLabelValues(hash, lvs) | ||||
| 	metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs, curry) | ||||
| 	if !ok { | ||||
| 		// Copy to avoid allocation in case wo don't go down this code path.
 | ||||
| 		copiedLVs := make([]string, len(lvs)) | ||||
| 		copy(copiedLVs, lvs) | ||||
| 		metric = m.newMetric(copiedLVs...) | ||||
| 		m.children[hash] = append(m.children[hash], metricWithLabelValues{values: copiedLVs, metric: metric}) | ||||
| 		inlinedLVs := inlineLabelValues(lvs, curry) | ||||
| 		metric = m.newMetric(inlinedLVs...) | ||||
| 		m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: inlinedLVs, metric: metric}) | ||||
| 	} | ||||
| 	return metric | ||||
| } | ||||
|  | @ -306,9 +323,11 @@ func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) | |||
| // or creates it and returns the new one.
 | ||||
| //
 | ||||
| // This function holds the mutex.
 | ||||
| func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric { | ||||
| func (m *metricMap) getOrCreateMetricWithLabels( | ||||
| 	hash uint64, labels Labels, curry []curriedLabelValue, | ||||
| ) Metric { | ||||
| 	m.mtx.RLock() | ||||
| 	metric, ok := m.getMetricWithLabels(hash, labels) | ||||
| 	metric, ok := m.getMetricWithHashAndLabels(hash, labels, curry) | ||||
| 	m.mtx.RUnlock() | ||||
| 	if ok { | ||||
| 		return metric | ||||
|  | @ -316,33 +335,37 @@ func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metr | |||
| 
 | ||||
| 	m.mtx.Lock() | ||||
| 	defer m.mtx.Unlock() | ||||
| 	metric, ok = m.getMetricWithLabels(hash, labels) | ||||
| 	metric, ok = m.getMetricWithHashAndLabels(hash, labels, curry) | ||||
| 	if !ok { | ||||
| 		lvs := m.extractLabelValues(labels) | ||||
| 		lvs := extractLabelValues(m.desc, labels, curry) | ||||
| 		metric = m.newMetric(lvs...) | ||||
| 		m.children[hash] = append(m.children[hash], metricWithLabelValues{values: lvs, metric: metric}) | ||||
| 		m.metrics[hash] = append(m.metrics[hash], metricWithLabelValues{values: lvs, metric: metric}) | ||||
| 	} | ||||
| 	return metric | ||||
| } | ||||
| 
 | ||||
| // getMetricWithLabelValues gets a metric while handling possible collisions in
 | ||||
| // the hash space. Must be called while holding read mutex.
 | ||||
| func (m *MetricVec) getMetricWithLabelValues(h uint64, lvs []string) (Metric, bool) { | ||||
| 	metrics, ok := m.children[h] | ||||
| // getMetricWithHashAndLabelValues gets a metric while handling possible
 | ||||
| // collisions in the hash space. Must be called while holding the read mutex.
 | ||||
| func (m *metricMap) getMetricWithHashAndLabelValues( | ||||
| 	h uint64, lvs []string, curry []curriedLabelValue, | ||||
| ) (Metric, bool) { | ||||
| 	metrics, ok := m.metrics[h] | ||||
| 	if ok { | ||||
| 		if i := m.findMetricWithLabelValues(metrics, lvs); i < len(metrics) { | ||||
| 		if i := findMetricWithLabelValues(metrics, lvs, curry); i < len(metrics) { | ||||
| 			return metrics[i].metric, true | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, false | ||||
| } | ||||
| 
 | ||||
| // getMetricWithLabels gets a metric while handling possible collisions in
 | ||||
| // getMetricWithHashAndLabels gets a metric while handling possible collisions in
 | ||||
| // the hash space. Must be called while holding read mutex.
 | ||||
| func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) { | ||||
| 	metrics, ok := m.children[h] | ||||
| func (m *metricMap) getMetricWithHashAndLabels( | ||||
| 	h uint64, labels Labels, curry []curriedLabelValue, | ||||
| ) (Metric, bool) { | ||||
| 	metrics, ok := m.metrics[h] | ||||
| 	if ok { | ||||
| 		if i := m.findMetricWithLabels(metrics, labels); i < len(metrics) { | ||||
| 		if i := findMetricWithLabels(m.desc, metrics, labels, curry); i < len(metrics) { | ||||
| 			return metrics[i].metric, true | ||||
| 		} | ||||
| 	} | ||||
|  | @ -351,9 +374,11 @@ func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) | |||
| 
 | ||||
| // findMetricWithLabelValues returns the index of the matching metric or
 | ||||
| // len(metrics) if not found.
 | ||||
| func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int { | ||||
| func findMetricWithLabelValues( | ||||
| 	metrics []metricWithLabelValues, lvs []string, curry []curriedLabelValue, | ||||
| ) int { | ||||
| 	for i, metric := range metrics { | ||||
| 		if m.matchLabelValues(metric.values, lvs) { | ||||
| 		if matchLabelValues(metric.values, lvs, curry) { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
|  | @ -362,32 +387,51 @@ func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, l | |||
| 
 | ||||
| // findMetricWithLabels returns the index of the matching metric or len(metrics)
 | ||||
| // if not found.
 | ||||
| func (m *MetricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int { | ||||
| func findMetricWithLabels( | ||||
| 	desc *Desc, metrics []metricWithLabelValues, labels Labels, curry []curriedLabelValue, | ||||
| ) int { | ||||
| 	for i, metric := range metrics { | ||||
| 		if m.matchLabels(metric.values, labels) { | ||||
| 		if matchLabels(desc, metric.values, labels, curry) { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	return len(metrics) | ||||
| } | ||||
| 
 | ||||
| func (m *MetricVec) matchLabelValues(values []string, lvs []string) bool { | ||||
| 	if len(values) != len(lvs) { | ||||
| func matchLabelValues(values []string, lvs []string, curry []curriedLabelValue) bool { | ||||
| 	if len(values) != len(lvs)+len(curry) { | ||||
| 		return false | ||||
| 	} | ||||
| 	var iLVs, iCurry int | ||||
| 	for i, v := range values { | ||||
| 		if v != lvs[i] { | ||||
| 		if iCurry < len(curry) && curry[iCurry].index == i { | ||||
| 			if v != curry[iCurry].value { | ||||
| 				return false | ||||
| 			} | ||||
| 			iCurry++ | ||||
| 			continue | ||||
| 		} | ||||
| 		if v != lvs[iLVs] { | ||||
| 			return false | ||||
| 		} | ||||
| 		iLVs++ | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (m *MetricVec) matchLabels(values []string, labels Labels) bool { | ||||
| 	if len(labels) != len(values) { | ||||
| func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool { | ||||
| 	if len(values) != len(labels)+len(curry) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i, k := range m.desc.variableLabels { | ||||
| 	iCurry := 0 | ||||
| 	for i, k := range desc.variableLabels { | ||||
| 		if iCurry < len(curry) && curry[iCurry].index == i { | ||||
| 			if values[i] != curry[iCurry].value { | ||||
| 				return false | ||||
| 			} | ||||
| 			iCurry++ | ||||
| 			continue | ||||
| 		} | ||||
| 		if values[i] != labels[k] { | ||||
| 			return false | ||||
| 		} | ||||
|  | @ -395,10 +439,31 @@ func (m *MetricVec) matchLabels(values []string, labels Labels) bool { | |||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (m *MetricVec) extractLabelValues(labels Labels) []string { | ||||
| 	labelValues := make([]string, len(labels)) | ||||
| 	for i, k := range m.desc.variableLabels { | ||||
| func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string { | ||||
| 	labelValues := make([]string, len(labels)+len(curry)) | ||||
| 	iCurry := 0 | ||||
| 	for i, k := range desc.variableLabels { | ||||
| 		if iCurry < len(curry) && curry[iCurry].index == i { | ||||
| 			labelValues[i] = curry[iCurry].value | ||||
| 			iCurry++ | ||||
| 			continue | ||||
| 		} | ||||
| 		labelValues[i] = labels[k] | ||||
| 	} | ||||
| 	return labelValues | ||||
| } | ||||
| 
 | ||||
| func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { | ||||
| 	labelValues := make([]string, len(lvs)+len(curry)) | ||||
| 	var iCurry, iLVs int | ||||
| 	for i := range labelValues { | ||||
| 		if iCurry < len(curry) && curry[iCurry].index == i { | ||||
| 			labelValues[i] = curry[iCurry].value | ||||
| 			iCurry++ | ||||
| 			continue | ||||
| 		} | ||||
| 		labelValues[i] = lvs[iLVs] | ||||
| 		iLVs++ | ||||
| 	} | ||||
| 	return labelValues | ||||
| } | ||||
|  |  | |||
|  | @ -19,11 +19,12 @@ func WithParam(ctx context.Context, p, v string) context.Context { | |||
| 	return context.WithValue(ctx, param(p), v) | ||||
| } | ||||
| 
 | ||||
| // Router wraps httprouter.Router and adds support for prefixed sub-routers
 | ||||
| // and per-request context injections.
 | ||||
| // Router wraps httprouter.Router and adds support for prefixed sub-routers,
 | ||||
| // per-request context injections and instrumentation.
 | ||||
| type Router struct { | ||||
| 	rtr    *httprouter.Router | ||||
| 	prefix string | ||||
| 	instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc | ||||
| } | ||||
| 
 | ||||
| // New returns a new Router.
 | ||||
|  | @ -33,13 +34,18 @@ func New() *Router { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // WithInstrumentation returns a router with instrumentation support.
 | ||||
| func (r *Router) WithInstrumentation(instrh func(handlerName string, handler http.HandlerFunc) http.HandlerFunc) *Router { | ||||
| 	return &Router{rtr: r.rtr, prefix: r.prefix, instrh: instrh} | ||||
| } | ||||
| 
 | ||||
| // WithPrefix returns a router that prefixes all registered routes with prefix.
 | ||||
| func (r *Router) WithPrefix(prefix string) *Router { | ||||
| 	return &Router{rtr: r.rtr, prefix: r.prefix + prefix} | ||||
| 	return &Router{rtr: r.rtr, prefix: r.prefix + prefix, instrh: r.instrh} | ||||
| } | ||||
| 
 | ||||
| // handle turns a HandlerFunc into an httprouter.Handle.
 | ||||
| func (r *Router) handle(h http.HandlerFunc) httprouter.Handle { | ||||
| func (r *Router) handle(handlerName string, h http.HandlerFunc) httprouter.Handle { | ||||
| 	return func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { | ||||
| 		ctx, cancel := context.WithCancel(req.Context()) | ||||
| 		defer cancel() | ||||
|  | @ -47,33 +53,36 @@ func (r *Router) handle(h http.HandlerFunc) httprouter.Handle { | |||
| 		for _, p := range params { | ||||
| 			ctx = context.WithValue(ctx, param(p.Key), p.Value) | ||||
| 		} | ||||
| 		if r.instrh != nil { | ||||
| 			h = r.instrh(handlerName, h) | ||||
| 		} | ||||
| 		h(w, req.WithContext(ctx)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Get registers a new GET route.
 | ||||
| func (r *Router) Get(path string, h http.HandlerFunc) { | ||||
| 	r.rtr.GET(r.prefix+path, r.handle(h)) | ||||
| 	r.rtr.GET(r.prefix+path, r.handle(path, h)) | ||||
| } | ||||
| 
 | ||||
| // Options registers a new OPTIONS route.
 | ||||
| func (r *Router) Options(path string, h http.HandlerFunc) { | ||||
| 	r.rtr.OPTIONS(r.prefix+path, r.handle(h)) | ||||
| 	r.rtr.OPTIONS(r.prefix+path, r.handle(path, h)) | ||||
| } | ||||
| 
 | ||||
| // Del registers a new DELETE route.
 | ||||
| func (r *Router) Del(path string, h http.HandlerFunc) { | ||||
| 	r.rtr.DELETE(r.prefix+path, r.handle(h)) | ||||
| 	r.rtr.DELETE(r.prefix+path, r.handle(path, h)) | ||||
| } | ||||
| 
 | ||||
| // Put registers a new PUT route.
 | ||||
| func (r *Router) Put(path string, h http.HandlerFunc) { | ||||
| 	r.rtr.PUT(r.prefix+path, r.handle(h)) | ||||
| 	r.rtr.PUT(r.prefix+path, r.handle(path, h)) | ||||
| } | ||||
| 
 | ||||
| // Post registers a new POST route.
 | ||||
| func (r *Router) Post(path string, h http.HandlerFunc) { | ||||
| 	r.rtr.POST(r.prefix+path, r.handle(h)) | ||||
| 	r.rtr.POST(r.prefix+path, r.handle(path, h)) | ||||
| } | ||||
| 
 | ||||
| // Redirect takes an absolute path and sends an internal HTTP redirect for it,
 | ||||
|  |  | |||
|  | @ -734,10 +734,16 @@ | |||
| 			"revisionTime": "2016-06-15T09:26:46Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "d2irkxoHgazkTuLIvJGiYwagl8o=", | ||||
| 			"checksumSHA1": "I87tkF1e/hrl4d/XIKFfkPRq1ww=", | ||||
| 			"path": "github.com/prometheus/client_golang/prometheus", | ||||
| 			"revision": "08fd2e12372a66e68e30523c7642e0cbc3e4fbde", | ||||
| 			"revisionTime": "2017-04-01T10:34:46Z" | ||||
| 			"revision": "e69720d204a4aa3b0c65dc91208645ba0a52b9cd", | ||||
| 			"revisionTime": "2018-02-16T13:12:53Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "mIWVz1E1QJ6yZnf7ELNwLboyK4w=", | ||||
| 			"path": "github.com/prometheus/client_golang/prometheus/promhttp", | ||||
| 			"revision": "e69720d204a4aa3b0c65dc91208645ba0a52b9cd", | ||||
| 			"revisionTime": "2018-02-16T13:12:53Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "DvwvOlPNAgRntBzt3b3OSRMS2N4=", | ||||
|  | @ -782,10 +788,10 @@ | |||
| 			"revisionTime": "2017-11-04T09:59:07Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "9aDxDuzZt1l7FQJ9qpn2kPcF7NU=", | ||||
| 			"checksumSHA1": "h6H+CjfOU6FfHV8cLWHN1VhAqp8=", | ||||
| 			"path": "github.com/prometheus/common/route", | ||||
| 			"revision": "e3fb1a1acd7605367a2b378bc2e2f893c05174b7", | ||||
| 			"revisionTime": "2017-11-04T09:59:07Z" | ||||
| 			"revision": "e4aa40a9169a88835b849a6efb71e05dc04b88f0", | ||||
| 			"revisionTime": "2018-03-12T11:28:59Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "91KYK0SpvkaMJJA2+BcxbVnyRO0=", | ||||
|  |  | |||
|  | @ -28,7 +28,6 @@ import ( | |||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| 	"github.com/prometheus/common/model" | ||||
| 	"github.com/prometheus/common/route" | ||||
| 	"github.com/prometheus/tsdb" | ||||
|  | @ -153,7 +152,7 @@ func NewAPI( | |||
| 
 | ||||
| // Register the API's endpoints in the given router.
 | ||||
| func (api *API) Register(r *route.Router) { | ||||
| 	instr := func(name string, f apiFunc) http.HandlerFunc { | ||||
| 	wrap := func(f apiFunc) http.HandlerFunc { | ||||
| 		hf := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 			setCORS(w) | ||||
| 			if data, err := f(r); err != nil { | ||||
|  | @ -164,34 +163,34 @@ func (api *API) Register(r *route.Router) { | |||
| 				w.WriteHeader(http.StatusNoContent) | ||||
| 			} | ||||
| 		}) | ||||
| 		return api.ready(prometheus.InstrumentHandler(name, httputil.CompressionHandler{ | ||||
| 		return api.ready(httputil.CompressionHandler{ | ||||
| 			Handler: hf, | ||||
| 		})) | ||||
| 		}.ServeHTTP) | ||||
| 	} | ||||
| 
 | ||||
| 	r.Options("/*path", instr("options", api.options)) | ||||
| 	r.Options("/*path", wrap(api.options)) | ||||
| 
 | ||||
| 	r.Get("/query", instr("query", api.query)) | ||||
| 	r.Post("/query", instr("query", api.query)) | ||||
| 	r.Get("/query_range", instr("query_range", api.queryRange)) | ||||
| 	r.Post("/query_range", instr("query_range", api.queryRange)) | ||||
| 	r.Get("/query", wrap(api.query)) | ||||
| 	r.Post("/query", wrap(api.query)) | ||||
| 	r.Get("/query_range", wrap(api.queryRange)) | ||||
| 	r.Post("/query_range", wrap(api.queryRange)) | ||||
| 
 | ||||
| 	r.Get("/label/:name/values", instr("label_values", api.labelValues)) | ||||
| 	r.Get("/label/:name/values", wrap(api.labelValues)) | ||||
| 
 | ||||
| 	r.Get("/series", instr("series", api.series)) | ||||
| 	r.Del("/series", instr("drop_series", api.dropSeries)) | ||||
| 	r.Get("/series", wrap(api.series)) | ||||
| 	r.Del("/series", wrap(api.dropSeries)) | ||||
| 
 | ||||
| 	r.Get("/targets", instr("targets", api.targets)) | ||||
| 	r.Get("/alertmanagers", instr("alertmanagers", api.alertmanagers)) | ||||
| 	r.Get("/targets", wrap(api.targets)) | ||||
| 	r.Get("/alertmanagers", wrap(api.alertmanagers)) | ||||
| 
 | ||||
| 	r.Get("/status/config", instr("config", api.serveConfig)) | ||||
| 	r.Get("/status/flags", instr("flags", api.serveFlags)) | ||||
| 	r.Post("/read", api.ready(prometheus.InstrumentHandler("read", http.HandlerFunc(api.remoteRead)))) | ||||
| 	r.Get("/status/config", wrap(api.serveConfig)) | ||||
| 	r.Get("/status/flags", wrap(api.serveFlags)) | ||||
| 	r.Post("/read", api.ready(http.HandlerFunc(api.remoteRead))) | ||||
| 
 | ||||
| 	// Admin APIs
 | ||||
| 	r.Post("/admin/tsdb/delete_series", instr("delete_series", api.deleteSeries)) | ||||
| 	r.Post("/admin/tsdb/clean_tombstones", instr("clean_tombstones", api.cleanTombstones)) | ||||
| 	r.Post("/admin/tsdb/snapshot", instr("snapshot", api.snapshot)) | ||||
| 	r.Post("/admin/tsdb/delete_series", wrap(api.deleteSeries)) | ||||
| 	r.Post("/admin/tsdb/clean_tombstones", wrap(api.cleanTombstones)) | ||||
| 	r.Post("/admin/tsdb/snapshot", wrap(api.snapshot)) | ||||
| } | ||||
| 
 | ||||
| type queryData struct { | ||||
|  |  | |||
							
								
								
									
										72
									
								
								web/web.go
								
								
								
								
							
							
						
						
									
										72
									
								
								web/web.go
								
								
								
								
							|  | @ -47,6 +47,7 @@ import ( | |||
| 	"github.com/opentracing-contrib/go-stdlib/nethttp" | ||||
| 	"github.com/opentracing/opentracing-go" | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| 	"github.com/prometheus/client_golang/prometheus/promhttp" | ||||
| 	"github.com/prometheus/common/model" | ||||
| 	"github.com/prometheus/common/route" | ||||
| 	"github.com/prometheus/tsdb" | ||||
|  | @ -68,6 +69,29 @@ import ( | |||
| 
 | ||||
| var localhostRepresentations = []string{"127.0.0.1", "localhost"} | ||||
| 
 | ||||
| var ( | ||||
| 	requestDuration = prometheus.NewHistogramVec( | ||||
| 		prometheus.HistogramOpts{ | ||||
| 			Name:    "prometheus_http_request_duration_seconds", | ||||
| 			Help:    "Histogram of latencies for HTTP requests.", | ||||
| 			Buckets: []float64{.1, .2, .4, 1, 3, 8, 20, 60, 120}, | ||||
| 		}, | ||||
| 		[]string{"handler"}, | ||||
| 	) | ||||
| 	responseSize = prometheus.NewHistogramVec( | ||||
| 		prometheus.HistogramOpts{ | ||||
| 			Name:    "prometheus_http_response_size_bytes", | ||||
| 			Help:    "Histogram of response size for HTTP requests.", | ||||
| 			Buckets: prometheus.ExponentialBuckets(100, 10, 8), | ||||
| 		}, | ||||
| 		[]string{"handler"}, | ||||
| 	) | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	prometheus.MustRegister(requestDuration, responseSize) | ||||
| } | ||||
| 
 | ||||
| // Handler serves various HTTP endpoints of the Prometheus server
 | ||||
| type Handler struct { | ||||
| 	logger log.Logger | ||||
|  | @ -146,9 +170,19 @@ type Options struct { | |||
| 	EnableAdminAPI       bool | ||||
| } | ||||
| 
 | ||||
| func instrumentHandler(handlerName string, handler http.HandlerFunc) http.HandlerFunc { | ||||
| 	return promhttp.InstrumentHandlerDuration( | ||||
| 		requestDuration.MustCurryWith(prometheus.Labels{"handler": handlerName}), | ||||
| 		promhttp.InstrumentHandlerResponseSize( | ||||
| 			responseSize.MustCurryWith(prometheus.Labels{"handler": handlerName}), | ||||
| 			handler, | ||||
| 		), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| // New initializes a new web Handler.
 | ||||
| func New(logger log.Logger, o *Options) *Handler { | ||||
| 	router := route.New() | ||||
| 	router := route.New().WithInstrumentation(instrumentHandler) | ||||
| 	cwd, err := os.Getwd() | ||||
| 
 | ||||
| 	if err != nil { | ||||
|  | @ -202,38 +236,36 @@ func New(logger log.Logger, o *Options) *Handler { | |||
| 		router = router.WithPrefix(o.RoutePrefix) | ||||
| 	} | ||||
| 
 | ||||
| 	instrh := prometheus.InstrumentHandler | ||||
| 	instrf := prometheus.InstrumentHandlerFunc | ||||
| 	readyf := h.testReady | ||||
| 
 | ||||
| 	router.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		http.Redirect(w, r, path.Join(o.ExternalURL.Path, "/graph"), http.StatusFound) | ||||
| 	}) | ||||
| 
 | ||||
| 	router.Get("/alerts", readyf(instrf("alerts", h.alerts))) | ||||
| 	router.Get("/graph", readyf(instrf("graph", h.graph))) | ||||
| 	router.Get("/status", readyf(instrf("status", h.status))) | ||||
| 	router.Get("/flags", readyf(instrf("flags", h.flags))) | ||||
| 	router.Get("/config", readyf(instrf("config", h.serveConfig))) | ||||
| 	router.Get("/rules", readyf(instrf("rules", h.rules))) | ||||
| 	router.Get("/targets", readyf(instrf("targets", h.targets))) | ||||
| 	router.Get("/version", readyf(instrf("version", h.version))) | ||||
| 	router.Get("/service-discovery", readyf(instrf("servicediscovery", h.serviceDiscovery))) | ||||
| 	router.Get("/alerts", readyf(h.alerts)) | ||||
| 	router.Get("/graph", readyf(h.graph)) | ||||
| 	router.Get("/status", readyf(h.status)) | ||||
| 	router.Get("/flags", readyf(h.flags)) | ||||
| 	router.Get("/config", readyf(h.serveConfig)) | ||||
| 	router.Get("/rules", readyf(h.rules)) | ||||
| 	router.Get("/targets", readyf(h.targets)) | ||||
| 	router.Get("/version", readyf(h.version)) | ||||
| 	router.Get("/service-discovery", readyf(h.serviceDiscovery)) | ||||
| 
 | ||||
| 	router.Get("/heap", instrf("heap", h.dumpHeap)) | ||||
| 	router.Get("/heap", h.dumpHeap) | ||||
| 
 | ||||
| 	router.Get("/metrics", prometheus.Handler().ServeHTTP) | ||||
| 	router.Get("/metrics", promhttp.Handler().ServeHTTP) | ||||
| 
 | ||||
| 	router.Get("/federate", readyf(instrh("federate", httputil.CompressionHandler{ | ||||
| 	router.Get("/federate", readyf(httputil.CompressionHandler{ | ||||
| 		Handler: http.HandlerFunc(h.federation), | ||||
| 	}))) | ||||
| 	}.ServeHTTP)) | ||||
| 
 | ||||
| 	router.Get("/consoles/*filepath", readyf(instrf("consoles", h.consoles))) | ||||
| 	router.Get("/consoles/*filepath", readyf(h.consoles)) | ||||
| 
 | ||||
| 	router.Get("/static/*filepath", instrf("static", h.serveStaticAsset)) | ||||
| 	router.Get("/static/*filepath", h.serveStaticAsset) | ||||
| 
 | ||||
| 	if o.UserAssetsPath != "" { | ||||
| 		router.Get("/user/*filepath", instrf("user", route.FileServe(o.UserAssetsPath))) | ||||
| 		router.Get("/user/*filepath", route.FileServe(o.UserAssetsPath)) | ||||
| 	} | ||||
| 
 | ||||
| 	if o.EnableLifecycle { | ||||
|  | @ -426,7 +458,7 @@ func (h *Handler) Run(ctx context.Context) error { | |||
| 	mux := http.NewServeMux() | ||||
| 	mux.Handle("/", h.router) | ||||
| 
 | ||||
| 	av1 := route.New() | ||||
| 	av1 := route.New().WithInstrumentation(instrumentHandler) | ||||
| 	h.apiV1.Register(av1) | ||||
| 	apiPath := "/api" | ||||
| 	if h.options.RoutePrefix != "/" { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue