Initial refactoring. WIP.

This commit is contained in:
beorn7 2015-11-27 15:56:42 +01:00
parent 67994f1771
commit 32b10bd039
21 changed files with 745 additions and 178 deletions

View File

@ -24,6 +24,9 @@ package prometheus
// already implemented in this library are the metric vectors (i.e. collection // already implemented in this library are the metric vectors (i.e. collection
// of multiple instances of the same Metric but with different label values) // of multiple instances of the same Metric but with different label values)
// like GaugeVec or SummaryVec, and the ExpvarCollector. // like GaugeVec or SummaryVec, and the ExpvarCollector.
//
// (Two Collectors are considered equal if their
// Describe method yields the same set of descriptors.)
type Collector interface { type Collector interface {
// Describe sends the super-set of all possible descriptors of metrics // Describe sends the super-set of all possible descriptors of metrics
// collected by this Collector to the provided channel and returns once // collected by this Collector to the provided channel and returns once
@ -46,7 +49,9 @@ type Collector interface {
// therefore be implemented in a concurrency safe way. Blocking occurs // therefore be implemented in a concurrency safe way. Blocking occurs
// at the expense of total performance of rendering all registered // at the expense of total performance of rendering all registered
// metrics. Ideally, Collector implementations support concurrent // metrics. Ideally, Collector implementations support concurrent
// readers. // readers. If a Collector finds itself unable to collect a metric, it
// can signal the error to the registry by sending a Metric that will
// return the error when its Write method is called.
Collect(chan<- Metric) Collect(chan<- Metric)
} }

View File

@ -0,0 +1,15 @@
// Copyright 2015 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 collectors provides collector implemntations for various purposes.
package collectors

View File

@ -11,13 +11,17 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package prometheus package collectors
import "github.com/prometheus/procfs" import (
"github.com/prometheus/procfs"
"github.com/prometheus/client_golang/prometheus/metric"
)
type processCollector struct { type processCollector struct {
pid int pid int
collectFn func(chan<- Metric) collectFn func(chan<- metric.Metric)
pidFn func() (int, error) pidFn func() (int, error)
cpuTotal Counter cpuTotal Counter
openFDs, maxFDs Gauge openFDs, maxFDs Gauge

View File

@ -1,4 +1,4 @@
package prometheus package collectors
import ( import (
"io/ioutil" "io/ioutil"

View File

@ -6,6 +6,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"unicode/utf8"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -30,6 +31,197 @@ const reservedLabelPrefix = "__"
// create a Desc. // create a Desc.
type Labels map[string]string type Labels map[string]string
// Desc is used to describe the meta-data of a Metric (via its Desc method) or a
// group of metrics collected by a Collector (via its Describe method). Some of its
// methods are only used internally and are therefore not exported, which also
// prevents users to implement their own descriptors. Descriptor instances must
// be created via suitable NewXXXDesc functions and will in generally only be
// needed in custom collectors.
//
// Desc implementations are immutable by contract.
//
// A Desc that also implements the error interface is called an invalid
// descriptor, which is solely used to communicate an error and must never be
// processed further.
type Desc interface {
// String returns a string representation of the descriptor as usual. It
// is also used as an ID that must be unique among all descriptors
// registered by a Registry.
String() string
dims() string
}
// NewInvalidDesc returns a descriptor that also implements the error
// interface. It is used to communicate an error during a call of Desc (Metric
// method) or Describe (Collector method). Create with NewInvalidDesc.
func NewInvalidDesc(err error) Desc {
return &invalidDesc{err: err}
}
type invalidDesc struct {
err error
}
func (d *invalidDesc) Error() string {
return d.err.Error()
}
func (d *invalidDesc) String() string {
return "[invalid] " + d.err.Error()
}
func (d *invalidDesc) dims() string {
return ""
}
// NewPrefixDesc returns a descriptor that is used by Collectors that want to
// reserve a whole metric name prefix for their own use. An invalid descriptor
// is returned if the prefix is not a valid as the start of a metric
// name. However, an empty prefix is valid and reserves all metric names.
func NewPrefixDesc(prefix string) Desc {
if prefix != "" && !validMetricName(prefix) {
return NewInvalidDesc(fmt.Errorf("%q is not a valid metric name prefix", prefix))
}
return &prefixDesc{pfx: prefix}
}
type prefixDesc struct {
prefix string
}
func (d *prefixDesc) String() string {
return "[prefix] " + d.prefix
}
func (d *prefixDesc) dims() string {
return "" // PrefixDesc is for all dimensions.
}
func (d *prefixDesc) overlapsWith(other Desc) bool {
switch o := other.(type) {
case *invalidDesc:
// Invalid descs never overlap.
return false
case *partialDesc, *fullDesc:
return strings.HasPrefix(o.fqName, d.prefix)
case *prefixDesc:
return strings.HasPrefix(o.prefix, d.prefix) || strings.HasPrefix(d.Prefix, o.prefix)
default:
panic(fmt.Errorf("unexpected type of descriptor %q", o))
}
}
// NewPartialDesc returns a descriptor that is used by Collectors that want to
// reserve a specific metric name and type with specific label dimensions of
// which some (but not all) label values might be set already. An invalid
// descriptor is returned in the following cases: The resulting label name
// (assembled from namespace, subsystem, and name) is invalid, the help string
// is empty, unsetLabels is empty or contains invalid label names,
// setLabels (which might be empty) contains invalid label names or label
// values, metricType is not a valid MetricType.
func NewPartialDesc(
namespace, subsystem, name string,
metricType MetricType,
help string,
setLabels Labels,
unsetLabels []string,
) Desc {
return nil // TODO
}
// NewFullDesc returns a descriptor with fully specified name, type, and
// labels. It can be used by Collectors and must be used by Metrics. An invalid
// descriptor is returned if the resulting label name (assembled from namespace,
// subsystem, and name) is invalid, the help string is empty, metricType has an
// invalid value, or the labels contain invalid label names or values. The labels
// might be empty, though.
func NewFullDesc(
namespace, subsystem, name string,
metricType MetricType,
help string,
labels Labels,
) Desc {
return nil // TODO
}
// FullySpecify returns a fully specified descriptor based on the provided
// partial descriptor by setting all the unset labels to the provided values (in
// the same order as they have been specified in the NewFullDesc or DeSpecify
// call). An invalid desc is returned if the provided desc is not a partial
// descriptor, the cardinality of labelValues does not fit, or labelValues
// contains invalid label values.
func FullySpecify(desc Desc, labelValues ...string) Desc {
d, ok := desc.(*partialDesc)
if !ok {
return NewInvalidDesc(fmt.Errorf("tried to fully specify non-partial descriptor %q", desc))
}
return nil // TODO
}
// DeSpecify creates a partial descriptor based on the provided full descriptor
// by adding un-set labels with the provided label names. An invalid desc is
// returned if the provided desc is not a full descriptor, or labelNames
// contains invalid label names (or no label names at all).
func DeSpecify(desc Desc, labelNames ...string) Desc {
d, ok := desc.(*fullDesc)
if !ok {
return NewInvalidDesc(fmt.Errorf("tried to de-specify non-full descriptor %q", desc))
}
if len(ln) == 0 {
return NewInvalidDesc(fmt.Errorf("no label names provided to de-specify %q", desc))
}
for _, ln := range labelNames {
if !validLabelName(ln) {
return NewInvalidDesc(fmt.Errorf("encountered invalid label name %q while de-specifying %q", ln, desc))
}
}
return &partialDesc{*d, labelNames}
}
type fullDesc struct {
fqName, help string
metricType MetricType
setLabels []*dto.LabelPair // Sorted.
}
type partialDesc struct {
fullDesc
unsetLabels []string // Keep in original order.
}
// buildFQName joins the given three name components by "_". Empty name
// components are ignored. If the name parameter itself is empty, an empty
// string is returned, no matter what.
func buildFQName(namespace, subsystem, name string) string {
if name == "" {
return ""
}
switch {
case namespace != "" && subsystem != "":
return namespace + "_" + subsystem + "_" + name
case namespace != "":
return namespace + "_" + name
case subsystem != "":
return subsystem + "_" + name
}
return name
}
func validMetricName(n string) bool {
return metricNameRE.MatchString(n)
}
func validLabelName(l string) bool {
return labelNameRE.MatchString(l) &&
!strings.HasPrefix(l, reservedLabelPrefix)
}
func validLabelValue(l string) bool {
return utf8.ValidString(l)
}
// OLD CODE below.
// Desc is the descriptor used by every Prometheus Metric. It is essentially // Desc is the descriptor used by every Prometheus Metric. It is essentially
// the immutable meta-data of a Metric. The normal Metric implementations // the immutable meta-data of a Metric. The normal Metric implementations
// included in this package manage their Desc under the hood. Users only have to // included in this package manage their Desc under the hood. Users only have to
@ -102,7 +294,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
labelNameSet := map[string]struct{}{} labelNameSet := map[string]struct{}{}
// First add only the const label names and sort them... // First add only the const label names and sort them...
for labelName := range constLabels { for labelName := range constLabels {
if !checkLabelName(labelName) { if !validLabelName(labelName) {
d.err = fmt.Errorf("%q is not a valid label name", labelName) d.err = fmt.Errorf("%q is not a valid label name", labelName)
return d return d
} }
@ -118,7 +310,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
// cannot be in a regular label name. That prevents matching the label // cannot be in a regular label name. That prevents matching the label
// dimension with a different mix between preset and variable labels. // dimension with a different mix between preset and variable labels.
for _, labelName := range variableLabels { for _, labelName := range variableLabels {
if !checkLabelName(labelName) { if !validLabelName(labelName) {
d.err = fmt.Errorf("%q is not a valid label name", labelName) d.err = fmt.Errorf("%q is not a valid label name", labelName)
return d return d
} }
@ -159,16 +351,6 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
return d return d
} }
// NewInvalidDesc returns an invalid descriptor, i.e. a descriptor with the
// provided error set. If a collector returning such a descriptor is registered,
// registration will fail with the provided error. NewInvalidDesc can be used by
// a Collector to signal inability to describe itself.
func NewInvalidDesc(err error) *Desc {
return &Desc{
err: err,
}
}
func (d *Desc) String() string { func (d *Desc) String() string {
lpStrings := make([]string, 0, len(d.constLabelPairs)) lpStrings := make([]string, 0, len(d.constLabelPairs))
for _, lp := range d.constLabelPairs { for _, lp := range d.constLabelPairs {
@ -185,8 +367,3 @@ func (d *Desc) String() string {
d.variableLabels, d.variableLabels,
) )
} }
func checkLabelName(l string) bool {
return labelNameRE.MatchString(l) &&
!strings.HasPrefix(l, reservedLabelPrefix)
}

View File

@ -13,11 +13,7 @@
package prometheus package prometheus
import ( import dto "github.com/prometheus/client_model/go"
"strings"
dto "github.com/prometheus/client_model/go"
)
const separatorByte byte = 255 const separatorByte byte = 255
@ -53,6 +49,42 @@ type Metric interface {
Write(*dto.Metric) error Write(*dto.Metric) error
} }
// MetricType is an enumeration of metric types. It deliberately mirrors the
// MetricType enum from the protobuf specification to avoid exposing protobuf
// references to the user of this package. (The protobuf parts could be
// internally vendored.)
type MetricType int
// Possible values for the MetricType enum.
const (
CounterMetric MetricType = iota
GaugeMetric
SummaryMetric
UntypedMetric
HistogramMetric
)
func (m MetricType) Valid() bool {
return m >= CounterMetric && m <= HistogramMetric
}
func (m MetricType) String() string {
switch m {
case CounterMetric:
return "COUNTER"
case GaugeMetric:
return "GAUGE"
case SummaryMetric:
return "SUMMARY"
case UntypedMetric:
return "UNTYPED"
case HistogramMetric:
return "HISTOGRAM"
default:
return "INVALID"
}
}
// Opts bundles the options for creating most Metric types. Each metric // Opts bundles the options for creating most Metric types. Each metric
// implementation XXX has its own XXXOpts type, but in most cases, it is just be // implementation XXX has its own XXXOpts type, but in most cases, it is just be
// an alias of this type (which might change when the requirement arises.) // an alias of this type (which might change when the requirement arises.)
@ -96,28 +128,6 @@ type Opts struct {
ConstLabels Labels ConstLabels Labels
} }
// BuildFQName joins the given three name components by "_". Empty name
// components are ignored. If the name parameter itself is empty, an empty
// string is returned, no matter what. Metric implementations included in this
// library use this function internally to generate the fully-qualified metric
// name from the name component in their Opts. Users of the library will only
// need this function if they implement their own Metric or instantiate a Desc
// (with NewDesc) directly.
func BuildFQName(namespace, subsystem, name string) string {
if name == "" {
return ""
}
switch {
case namespace != "" && subsystem != "":
return strings.Join([]string{namespace, subsystem, name}, "_")
case namespace != "":
return strings.Join([]string{namespace, name}, "_")
case subsystem != "":
return strings.Join([]string{subsystem, name}, "_")
}
return name
}
// LabelPairSorter implements sort.Interface. It is used to sort a slice of // LabelPairSorter implements sort.Interface. It is used to sort a slice of
// dto.LabelPair pointers. This is useful for implementing the Write method of // dto.LabelPair pointers. This is useful for implementing the Write method of
// custom metrics. // custom metrics.
@ -150,17 +160,18 @@ func (s hashSorter) Less(i, j int) bool {
} }
type invalidMetric struct { type invalidMetric struct {
desc *Desc desc Desc
err error err error
} }
// NewInvalidMetric returns a metric whose Write method always returns the // NewInvalidMetric returns a metric whose Write method always returns the
// provided error. It is useful if a Collector finds itself unable to collect // provided error, and whose descriptor is invalid, carrying the provided
// a metric and wishes to report an error to the registry. // error. It is useful if a Collector finds itself unable to collect a metric
func NewInvalidMetric(desc *Desc, err error) Metric { // and wishes to report an error to the registry.
return &invalidMetric{desc, err} func NewInvalidMetric(err error) Metric {
return &invalidMetric{NewInvalidDesc(err), err}
} }
func (m *invalidMetric) Desc() *Desc { return m.desc } func (m *invalidMetric) Desc() Desc { return m.desc }
func (m *invalidMetric) Write(*dto.Metric) error { return m.err } func (m *invalidMetric) Write(*dto.Metric) error { return m.err }

View File

@ -0,0 +1,54 @@
// Copyright 2015 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 metric
// Collector is the interface implemented by anything that can be used by
// Prometheus to collect metrics. A Collector has to be registered for
// collection.
//
// The stock metrics provided in their respective packages (like Gauge, Counter,
// Summary, Histogram) are also Collectors (which only ever collect one metric,
// namely itself). An implementer of Collector may, however, collect multiple
// metrics in a coordinated fashion and/or create metrics on the fly. Examples
// for collectors already implemented in this library are the metric vectors
// (i.e. collection of multiple instances of the same Metric but with different
// label values) like gauge.Vec or summary.Vec, and the ExpvarCollector.
//
// Two Collectors are considered equal if their Describe methods yield the same
// set of descriptors.
type Collector interface {
// Describe sends all descriptors required to describe the metrics
// collected by this Collector to the provided channel and returns once
// the last descriptor has been sent. The sent descriptors fulfill the
// consistency and uniqueness requirements described in the Desc
// documentation. This method idempotently sends the same descriptors
// throughout the lifetime of the Collector. If a Collector encounters
// an error while executing this method, it must send an invalid
// descriptor (created with NewInvalidDesc) to signal the error to the
// registry.
Describe(chan<- Desc)
// Collect is called by Prometheus when collecting metrics. The
// implementation sends each collected metric via the provided channel
// and returns once the last metric has been sent. Each sent metric must
// be consistent with one of the descriptors returned by
// Describe. Returned metrics that are described by the same descriptor
// must differ in their variable label values. This method may be called
// concurrently and must therefore be implemented in a concurrency safe
// way. Blocking occurs at the expense of total performance of rendering
// all registered metrics. Ideally, Collector implementations support
// concurrent readers. If a Collector finds itself unable to collect a
// metric, it can signal the error to the registry by sending a Metric
// that will return the error in its Write method..
Collect(chan<- Metric)
}

49
prometheus/metric/desc.go Normal file
View File

@ -0,0 +1,49 @@
import "fmt"
func NewDesc(
namespace, subsystem, name string,
help string,
constLabels Labels,
variableLabels []string,
) Desc {
fqName := buildFQName(namespace, subsystem, name)
if !metricNameRE.MatchString(fqName) {
return NewInvalidDesc(fmt.Errorf("%q is not a valid metric name", fqName))
}
if help == "" {
return NewInvalidDesc(fmt.Errorf("empty help string for metric %q", fqName))
}
return nil // TODO
}
type regularDesc struct {
baseDesc
fqName, help string
constLabelPairs []*dto.LabelPair
variableLabels []string
}
type prefixDesc struct {
baseDesc
prefix string
}
type Set struct {
regular map[string]*regularDesc
// The prefix ones should be tries. But it's unlikely to have many of them.
prefix []*prefixDesc
}
func (s *Set) Add(d Desc) error {
if d.Error() != nil {
return d.Error()
}
return nil
}
func (s *Set) Remove(d Desc) bool {
return false
}

View File

@ -1,4 +1,4 @@
package prometheus package metric
// Inline and byte-free variant of hash/fnv's fnv64a. // Inline and byte-free variant of hash/fnv's fnv64a.

View File

@ -0,0 +1,46 @@
// Copyright 2015 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 metric
import dto "github.com/prometheus/client_model/go"
// A Metric models a single sample value with its meta data being exported to
// Prometheus. Implementers of Metric in this package include Gauge, Counter,
// Untyped, and Summary. Users can implement their own Metric types, but that
// should be rarely needed.
type Metric interface {
// Desc returns the descriptor for the Metric. This method idempotently
// returns the same descriptor throughout the lifetime of the Metric. A
// Metric unable to describe itself must return an invalid descriptor
// (created with NewInvalidDesc).
Desc() Desc
// Write encodes the Metric into a "Metric" Protocol Buffer data
// transmission object.
//
// Implementers of custom Metric types must observe concurrency safety
// as reads of this metric may occur at any time, and any blocking
// occurs at the expense of total performance of rendering all
// registered metrics. Ideally Metric implementations should support
// concurrent readers.
//
// The caller may minimize memory allocations by providing a
// pre-existing reset dto.Metric pointer. The caller may recycle the
// dto.Metric proto message, so Metric implementations should just
// populate the provided dto.Metric and then should not keep any
// reference to it.
//
// While populating dto.Metric, labels must be sorted lexicographically.
// (Implementers may find LabelPairSorter useful for that.)
Write(*dto.Metric) error
}

View File

@ -0,0 +1,53 @@
// Copyright 2015 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"
"github.com/prometheus/client_golang/prometheus/registry"
)
// Constants relevant to the HTTP interface.
const (
// APIVersion is the version of the format of the exported data. This
// will match this library's version, which subscribes to the Semantic
// Versioning scheme.
APIVersion = "0.0.4"
// DelimitedTelemetryContentType is the content type set on telemetry
// data responses in delimited protobuf format.
DelimitedTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`
// TextTelemetryContentType is the content type set on telemetry data
// responses in text format.
TextTelemetryContentType = `text/plain; version=` + APIVersion
// ProtoTextTelemetryContentType is the content type set on telemetry
// data responses in protobuf text format. (Only used for debugging.)
ProtoTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`
// ProtoCompactTextTelemetryContentType is the content type set on
// telemetry data responses in protobuf compact text format. (Only used
// for debugging.)
ProtoCompactTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`
contentTypeHeader = "Content-Type"
contentLengthHeader = "Content-Length"
contentEncodingHeader = "Content-Encoding"
acceptEncodingHeader = "Accept-Encoding"
acceptHeader = "Accept"
)
func Handler(r registry.Registry) http.Handler {
return nil // TODO
}

View File

@ -1,4 +1,4 @@
// Copyright 2014 The Prometheus Authors // Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package prometheus package promhttp
import ( import (
"bufio" "bufio"
@ -23,6 +23,8 @@ import (
"time" "time"
) )
// TODO: Rework handler instrumentation (histograms, more configurable...).
var instLabels = []string{"method", "code"} var instLabels = []string{"method", "code"}
type nower interface { type nower interface {

View File

@ -27,7 +27,6 @@ import (
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"os"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -36,56 +35,19 @@ import (
"github.com/prometheus/common/expfmt" "github.com/prometheus/common/expfmt"
dto "github.com/prometheus/client_model/go" dto "github.com/prometheus/client_model/go"
"github.com/prometheus/client_golang/prometheus/promhttp"
) )
var ( // Handler returns an instrumented HTTP handler for the default Prometheus
defRegistry = newDefaultRegistry() // registry. It is already instrumented with InstrumentHandler (using
errAlreadyReg = errors.New("duplicate metrics collector registration attempted") // "prometheus" as handler name). Usually the handler is used to handle the
) // "/metrics" endpoint.
// Constants relevant to the HTTP interface.
const (
// APIVersion is the version of the format of the exported data. This
// will match this library's version, which subscribes to the Semantic
// Versioning scheme.
APIVersion = "0.0.4"
// DelimitedTelemetryContentType is the content type set on telemetry
// data responses in delimited protobuf format.
DelimitedTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`
// TextTelemetryContentType is the content type set on telemetry data
// responses in text format.
TextTelemetryContentType = `text/plain; version=` + APIVersion
// ProtoTextTelemetryContentType is the content type set on telemetry
// data responses in protobuf text format. (Only used for debugging.)
ProtoTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`
// ProtoCompactTextTelemetryContentType is the content type set on
// telemetry data responses in protobuf compact text format. (Only used
// for debugging.)
ProtoCompactTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`
// Constants for object pools.
numBufs = 4
numMetricFamilies = 1000
numMetrics = 10000
// Capacity for the channel to collect metrics and descriptors.
capMetricChan = 1000
capDescChan = 10
contentTypeHeader = "Content-Type"
contentLengthHeader = "Content-Length"
contentEncodingHeader = "Content-Encoding"
acceptEncodingHeader = "Accept-Encoding"
acceptHeader = "Accept"
)
// Handler returns the HTTP handler for the global Prometheus registry. It is
// already instrumented with InstrumentHandler (using "prometheus" as handler
// name). Usually the handler is used to handle the "/metrics" endpoint.
func Handler() http.Handler { func Handler() http.Handler {
return InstrumentHandler("prometheus", defRegistry) return promhttp.InstrumentHandler(
"prometheus",
promhttp.Handler(registry.Default),
)
} }
// UninstrumentedHandler works in the same way as Handler, but the returned HTTP // UninstrumentedHandler works in the same way as Handler, but the returned HTTP
@ -94,30 +56,26 @@ func Handler() http.Handler {
// different handler name (or with a different instrumentation approach // different handler name (or with a different instrumentation approach
// altogether). See the InstrumentHandler example. // altogether). See the InstrumentHandler example.
func UninstrumentedHandler() http.Handler { func UninstrumentedHandler() http.Handler {
return defRegistry return promhttp.Handler(registry.Default)
} }
// Register registers a new Collector to be included in metrics collection. It // Register registers a new Collector to be included in metrics collection with
// returns an error if the descriptors provided by the Collector are invalid or // the default registry. It returns an error if the descriptors provided by the
// if they - in combination with descriptors of already registered Collectors - // Collector are invalid or if they - in combination with descriptors of already
// do not fulfill the consistency and uniqueness criteria described in the Desc // registered Collectors - do not fulfill the consistency and uniqueness
// documentation. // criteria described in the Desc documentation.
// //
// Do not register the same Collector multiple times concurrently. (Registering // Do not register the same Collector multiple times concurrently. (Registering
// the same Collector twice would result in an error anyway, but on top of that, // the same Collector twice would result in an error anyway, but on top of that,
// it is not safe to do so concurrently.) // it is not safe to do so concurrently.)
func Register(m Collector) error { func Register(c Collector) error {
_, err := defRegistry.Register(m) return registry.Default.Register(c)
return err
} }
// MustRegister works like Register but panics where Register would have // MustRegister works like Register but panics where Register would have
// returned an error. // returned an error.
func MustRegister(m Collector) { func MustRegister(c Collector) {
err := Register(m) registry.MustRegister(registry.Default, c)
if err != nil {
panic(err)
}
} }
// RegisterOrGet works like Register but does not return an error if a Collector // RegisterOrGet works like Register but does not return an error if a Collector
@ -129,18 +87,14 @@ func MustRegister(m Collector) {
// //
// As for Register, it is still not safe to call RegisterOrGet with the same // As for Register, it is still not safe to call RegisterOrGet with the same
// Collector multiple times concurrently. // Collector multiple times concurrently.
func RegisterOrGet(m Collector) (Collector, error) { func RegisterOrGet(c Collector) (Collector, error) {
return defRegistry.RegisterOrGet(m) return registry.RegisterOrGet(registry.Default, c)
} }
// MustRegisterOrGet works like Register but panics where RegisterOrGet would // MustRegisterOrGet works like Register but panics where RegisterOrGet would
// have returned an error. // have returned an error.
func MustRegisterOrGet(m Collector) Collector { func MustRegisterOrGet(c Collector) Collector {
existing, err := RegisterOrGet(m) return registry.MustRegisterOrGet(registry.Default, c)
if err != nil {
panic(err)
}
return existing
} }
// Unregister unregisters the Collector that equals the Collector passed in as // Unregister unregisters the Collector that equals the Collector passed in as
@ -148,50 +102,10 @@ func MustRegisterOrGet(m Collector) Collector {
// yields the same set of descriptors.) The function returns whether a Collector // yields the same set of descriptors.) The function returns whether a Collector
// was unregistered. // was unregistered.
func Unregister(c Collector) bool { func Unregister(c Collector) bool {
return defRegistry.Unregister(c) return registry.Default.Unregister(c)
} }
// SetMetricFamilyInjectionHook sets a function that is called whenever metrics // TODO: Move out from here.
// are collected. The hook function must be set before metrics collection begins
// (i.e. call SetMetricFamilyInjectionHook before setting the HTTP handler.) The
// MetricFamily protobufs returned by the hook function are merged with the
// metrics collected in the usual way.
//
// This is a way to directly inject MetricFamily protobufs managed and owned by
// the caller. The caller has full responsibility. As no registration of the
// injected metrics has happened, there is no descriptor to check against, and
// there are no registration-time checks. If collect-time checks are disabled
// (see function EnableCollectChecks), no sanity checks are performed on the
// returned protobufs at all. If collect-checks are enabled, type and uniqueness
// checks are performed, but no further consistency checks (which would require
// knowledge of a metric descriptor).
//
// Sorting concerns: The caller is responsible for sorting the label pairs in
// each metric. However, the order of metrics will be sorted by the registry as
// it is required anyway after merging with the metric families collected
// conventionally.
//
// The function must be callable at any time and concurrently.
func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) {
defRegistry.metricFamilyInjectionHook = hook
}
// PanicOnCollectError sets the behavior whether a panic is caused upon an error
// while metrics are collected and served to the HTTP endpoint. By default, an
// internal server error (status code 500) is served with an error message.
func PanicOnCollectError(b bool) {
defRegistry.panicOnCollectError = b
}
// EnableCollectChecks enables (or disables) additional consistency checks
// during metrics collection. These additional checks are not enabled by default
// because they inflict a performance penalty and the errors they check for can
// only happen if the used Metric and Collector types have internal programming
// errors. It can be helpful to enable these checks while working with custom
// Collectors or Metrics whose correctness is not well established yet.
func EnableCollectChecks(b bool) {
defRegistry.collectChecksEnabled = b
}
// encoder is a function that writes a dto.MetricFamily to an io.Writer in a // encoder is a function that writes a dto.MetricFamily to an io.Writer in a
// certain encoding. It returns the number of bytes written and any error // certain encoding. It returns the number of bytes written and any error
@ -305,7 +219,7 @@ func (r *registry) Unregister(c Collector) bool {
}() }()
descIDs := map[uint64]struct{}{} descIDs := map[uint64]struct{}{}
var collectorID uint64 // Just a sum of the desc IDs. var collectorID uint64 // Just a sum of the desc IDs. TODO: should be fnv on its own
for desc := range descChan { for desc := range descChan {
if _, exists := descIDs[desc.id]; !exists { if _, exists := descIDs[desc.id]; !exists {
collectorID += desc.id collectorID += desc.id
@ -670,13 +584,6 @@ func newRegistry() *registry {
} }
} }
func newDefaultRegistry() *registry {
r := newRegistry()
r.Register(NewProcessCollector(os.Getpid(), ""))
r.Register(NewGoCollector())
return r
}
// decorateWriter wraps a writer to handle gzip compression if requested. It // decorateWriter wraps a writer to handle gzip compression if requested. It
// returns the decorated writer and the appropriate "Content-Encoding" header // returns the decorated writer and the appropriate "Content-Encoding" header
// (which is empty if no compression is enabled). // (which is empty if no compression is enabled).

View File

@ -0,0 +1,17 @@
// Copyright 2015 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 registry provides the interface of the metrics registry and means to
// create concrete instantiations thereof.
package registry

View File

@ -0,0 +1,24 @@
// Copyright 2015 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 registry
import "github.com/prometheus/client_golang/prometheus/metric"
type AlreadyRegisteredError struct {
ExistingCollector, NewCollector metric.Collector
}
func (err AlreadyRegisteredError) Error() string {
return "" // TODO
}

View File

@ -0,0 +1,66 @@
// Copyright 2015 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 registry
import (
"os"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/client_golang/prometheus"
)
// TODO: These are from the old code. Vet!
const (
// Constants for object pools.
numBufs = 4
numMetricFamilies = 1000
numMetrics = 10000
// Capacity for the channel to collect metrics and descriptors.
capMetricChan = 1000
capDescChan = 10
)
func init() {
MustRegister(Default, collectors.NewProcessCollector(os.Getpid(), ""))
MustRegister(Default, collectors.NewGoCollector())
}
// Default is the default registry implicitly used by the top-level functions in
// the prometheus package. It is using the default value of Opts and has a
// ProcessCollector and a GoCollector pre-registered.
var Default Registry = New(Opts{})
func New(opts Opts) Registry {
return &registry{}
}
type registry struct {
}
func (r *registry) Register(prometheus.Collector) error {
return nil // TODO
}
func (r *registry) Unregister(prometheus.Collector) bool {
return false // TODO
}
func (r *registry) Collect(names map[string]struct{}) <-chan struct {
*dto.MetricFamily
error
} {
return nil // TODO
}

View File

@ -0,0 +1,96 @@
// Copyright 2015 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 registry provides the interface of the metrics registry and means to
// instantiate implementations thereof. It also provides the so-called default
// registry, a pre-configured instantiation of a registry that should be
// sufficient for most use-cases.
package registry
import (
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/client_golang/prometheus/metric"
)
// Registry is the interface for the metrics registry.
type Registry interface {
// Register registers a new Collector to be included in metrics
// collection. It returns an error if the descriptors provided by the
// Collector are invalid or if they - in combination with descriptors of
// already registered Collectors - do not fulfill the consistency and
// uniqueness criteria described in the documentation of metric.Desc.
//
// If the provided Collector is equal to a Collector already registered
// (which includes the case of re-registering the same Collector), the
// returned error is an instance of AlreadyRegisteredError, which
// contains the previously registered Collector.
//
// It is in general not safe to register the same Collector multiple
// times concurrently.
Register(metric.Collector) error
// Unregister unregisters the Collector that equals the Collector passed
// in as an argument. The function returns whether a Collector was
// unregistered.
Unregister(metric.Collector) bool
// Collect returns a channel that yields MetricFamily protobufs
// (collected from registered collectors) together with applicable
// errors. The metric family pointer returned with an error could be nil
// or point to a (presumably incomplete) metric family. Once all
// MetricFamilies have been read, the channel is closed. To not leak
// resources, the channel must always be read until closed, even if one
// ore more errors have been returned. If names is nil or empty, all
// MetricFamilies are returned. Otherwise, only MetricFamilies with a
// name contained in names are returned. Implementations should aim for
// lexicographical sorting of MetricFamilies if the resource cost of
// sorting is not prohibitive.
Collect(names map[string]struct{}) <-chan struct {
*dto.MetricFamily
error
}
}
// Opts are options for the instantiation of new registries. The zero value of
// Opts is a safe default.
type Opts struct {
// If true, metrics are checked for consistency during collection. The
// check has a performance overhead and is not necessary with
// well-behaved collectors. It can be helpful to enable the check while
// working with custom Collectors whose correctness is not well
// established yet or where inconsistent collection might happen by
// design.
CollectCheckEnabled bool
// If true, the channel returned by the Collect method will never yield
// an error (so that no error handling has to be implemented when
// receiving from the channel). Instead, the program will panic. This
// behavior is useful in programs where collect errors cannot (or must
// not) happen.
PanicOnCollectError bool
// The MetricFamilyInjectionHook is a function that is called whenever
// metrics are collected. The MetricFamily protobufs returned by the
// hook function are merged with the metrics collected in the usual way.
//
// This is a way to directly inject MetricFamily protobufs managed and
// owned by the caller. The caller has full responsibility. As no
// registration of the injected metrics has happened, there was no check
// at registration-time. If CollectCheckEnabled is false, only very
// limited sanity checks are performed on the returned protobufs.
//
// Sorting concerns: The caller is responsible for sorting the label
// pairs in each metric. However, the order of metrics will be sorted by
// the registry as it is required anyway after merging with the metric
// families collected conventionally.
//
// The function must be callable at any time and concurrently.
MetricFamilyInjectionHook func() []*dto.MetricFamily
}

View File

@ -0,0 +1,40 @@
// Copyright 2015 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 registry
import "github.com/prometheus/client_golang/prometheus/metric"
func MustRegister(r Registry, c metric.Collector) {
if err := r.Register(c); err != nil {
panic(err)
}
}
func RegisterOrGet(r Registry, c metric.Collector) (metric.Collector, error) {
if err := r.Register(c); err != nil {
if are, ok := err.(AlreadyRegisteredError); ok {
return are.ExistingCollector, nil
}
return nil, err
}
return c, nil
}
func MustRegisterOrGet(r Registry, c metric.Collector) metric.Collector {
existing, err := RegisterOrGet(r, c)
if err != nil {
panic(err)
}
return existing
}

View File

@ -241,6 +241,7 @@ func (m *MetricVec) getOrCreateMetric(hash uint64, labelValues ...string) Metric
if !ok { if !ok {
// Copy labelValues. Otherwise, they would be allocated even if we don't go // Copy labelValues. Otherwise, they would be allocated even if we don't go
// down this code path. // down this code path.
// TODO: Use copy.
copiedLabelValues := append(make([]string, 0, len(labelValues)), labelValues...) copiedLabelValues := append(make([]string, 0, len(labelValues)), labelValues...)
metric = m.newMetric(copiedLabelValues...) metric = m.newMetric(copiedLabelValues...)
m.children[hash] = metric m.children[hash] = metric