Initial refactoring. WIP.
diff --git a/prometheus/collector.go b/prometheus/collector.go
index c046880..1905e15 100644
--- a/prometheus/collector.go
+++ b/prometheus/collector.go
@@ -24,6 +24,9 @@
// 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 GaugeVec or SummaryVec, and the ExpvarCollector.
+//
+// (Two Collectors are considered equal if their
+// Describe method yields the same set of descriptors.)
type Collector interface {
// Describe sends the super-set of all possible descriptors of metrics
// collected by this Collector to the provided channel and returns once
@@ -46,7 +49,9 @@
// 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.
+ // 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)
}
diff --git a/prometheus/collectors/doc.go b/prometheus/collectors/doc.go
new file mode 100644
index 0000000..032fabe
--- /dev/null
+++ b/prometheus/collectors/doc.go
@@ -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
diff --git a/prometheus/go_collector.go b/prometheus/collectors/go.go
similarity index 100%
rename from prometheus/go_collector.go
rename to prometheus/collectors/go.go
diff --git a/prometheus/go_collector_test.go b/prometheus/collectors/go_test.go
similarity index 100%
rename from prometheus/go_collector_test.go
rename to prometheus/collectors/go_test.go
diff --git a/prometheus/process_collector.go b/prometheus/collectors/process.go
similarity index 95%
rename from prometheus/process_collector.go
rename to prometheus/collectors/process.go
index d8cf0ed..473e52e 100644
--- a/prometheus/process_collector.go
+++ b/prometheus/collectors/process.go
@@ -11,13 +11,17 @@
// See the License for the specific language governing permissions and
// 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 {
pid int
- collectFn func(chan<- Metric)
+ collectFn func(chan<- metric.Metric)
pidFn func() (int, error)
cpuTotal Counter
openFDs, maxFDs Gauge
diff --git a/prometheus/process_collector_test.go b/prometheus/collectors/process_test.go
similarity index 98%
rename from prometheus/process_collector_test.go
rename to prometheus/collectors/process_test.go
index 829715a..6169842 100644
--- a/prometheus/process_collector_test.go
+++ b/prometheus/collectors/process_test.go
@@ -1,4 +1,4 @@
-package prometheus
+package collectors
import (
"io/ioutil"
diff --git a/prometheus/desc.go b/prometheus/desc.go
index ee02d9b..5a214e3 100644
--- a/prometheus/desc.go
+++ b/prometheus/desc.go
@@ -6,6 +6,7 @@
"regexp"
"sort"
"strings"
+ "unicode/utf8"
"github.com/golang/protobuf/proto"
@@ -30,6 +31,197 @@
// create a Desc.
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
// 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
@@ -102,7 +294,7 @@
labelNameSet := map[string]struct{}{}
// First add only the const label names and sort them...
for labelName := range constLabels {
- if !checkLabelName(labelName) {
+ if !validLabelName(labelName) {
d.err = fmt.Errorf("%q is not a valid label name", labelName)
return d
}
@@ -118,7 +310,7 @@
// cannot be in a regular label name. That prevents matching the label
// dimension with a different mix between preset and variable labels.
for _, labelName := range variableLabels {
- if !checkLabelName(labelName) {
+ if !validLabelName(labelName) {
d.err = fmt.Errorf("%q is not a valid label name", labelName)
return d
}
@@ -159,16 +351,6 @@
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 {
lpStrings := make([]string, 0, len(d.constLabelPairs))
for _, lp := range d.constLabelPairs {
@@ -185,8 +367,3 @@
d.variableLabels,
)
}
-
-func checkLabelName(l string) bool {
- return labelNameRE.MatchString(l) &&
- !strings.HasPrefix(l, reservedLabelPrefix)
-}
diff --git a/prometheus/metric.go b/prometheus/metric.go
index 86fd81c..6c31d9a 100644
--- a/prometheus/metric.go
+++ b/prometheus/metric.go
@@ -13,11 +13,7 @@
package prometheus
-import (
- "strings"
-
- dto "github.com/prometheus/client_model/go"
-)
+import dto "github.com/prometheus/client_model/go"
const separatorByte byte = 255
@@ -53,6 +49,42 @@
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
// 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.)
@@ -96,28 +128,6 @@
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
// dto.LabelPair pointers. This is useful for implementing the Write method of
// custom metrics.
@@ -150,17 +160,18 @@
}
type invalidMetric struct {
- desc *Desc
+ desc Desc
err error
}
// NewInvalidMetric returns a metric whose Write method always returns the
-// provided error. It is useful if a Collector finds itself unable to collect
-// a metric and wishes to report an error to the registry.
-func NewInvalidMetric(desc *Desc, err error) Metric {
- return &invalidMetric{desc, err}
+// provided error, and whose descriptor is invalid, carrying the provided
+// error. It is useful if a Collector finds itself unable to collect a metric
+// and wishes to report an error to the registry.
+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 }
diff --git a/prometheus/metric/collector.go b/prometheus/metric/collector.go
new file mode 100644
index 0000000..763fda5
--- /dev/null
+++ b/prometheus/metric/collector.go
@@ -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)
+}
diff --git a/prometheus/metric/desc.go b/prometheus/metric/desc.go
new file mode 100644
index 0000000..f5ae294
--- /dev/null
+++ b/prometheus/metric/desc.go
@@ -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
+}
+
diff --git a/prometheus/fnv.go b/prometheus/metric/fnv.go
similarity index 96%
rename from prometheus/fnv.go
rename to prometheus/metric/fnv.go
index e3b67df..ef0c224 100644
--- a/prometheus/fnv.go
+++ b/prometheus/metric/fnv.go
@@ -1,4 +1,4 @@
-package prometheus
+package metric
// Inline and byte-free variant of hash/fnv's fnv64a.
diff --git a/prometheus/metric/metric.go b/prometheus/metric/metric.go
new file mode 100644
index 0000000..9a42112
--- /dev/null
+++ b/prometheus/metric/metric.go
@@ -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
+}
diff --git a/prometheus/promhttp/exposition.go b/prometheus/promhttp/exposition.go
new file mode 100644
index 0000000..79b581d
--- /dev/null
+++ b/prometheus/promhttp/exposition.go
@@ -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
+}
diff --git a/prometheus/http.go b/prometheus/promhttp/instrumentation.go
similarity index 97%
rename from prometheus/http.go
rename to prometheus/promhttp/instrumentation.go
index eabe602..bd327bd 100644
--- a/prometheus/http.go
+++ b/prometheus/promhttp/instrumentation.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The Prometheus Authors
+// 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
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package prometheus
+package promhttp
import (
"bufio"
@@ -23,6 +23,8 @@
"time"
)
+// TODO: Rework handler instrumentation (histograms, more configurable...).
+
var instLabels = []string{"method", "code"}
type nower interface {
diff --git a/prometheus/registry.go b/prometheus/registry.go
index 1dc2536..2f7f8eb 100644
--- a/prometheus/registry.go
+++ b/prometheus/registry.go
@@ -27,7 +27,6 @@
"io"
"net/http"
"net/url"
- "os"
"sort"
"strings"
"sync"
@@ -36,56 +35,19 @@
"github.com/prometheus/common/expfmt"
dto "github.com/prometheus/client_model/go"
+
+ "github.com/prometheus/client_golang/prometheus/promhttp"
)
-var (
- defRegistry = newDefaultRegistry()
- errAlreadyReg = errors.New("duplicate metrics collector registration attempted")
-)
-
-// 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.
+// Handler returns an instrumented HTTP handler for the default 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 {
- return InstrumentHandler("prometheus", defRegistry)
+ return promhttp.InstrumentHandler(
+ "prometheus",
+ promhttp.Handler(registry.Default),
+ )
}
// UninstrumentedHandler works in the same way as Handler, but the returned HTTP
@@ -94,30 +56,26 @@
// different handler name (or with a different instrumentation approach
// altogether). See the InstrumentHandler example.
func UninstrumentedHandler() http.Handler {
- return defRegistry
+ return promhttp.Handler(registry.Default)
}
-// 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 Desc
-// documentation.
+// Register registers a new Collector to be included in metrics collection with
+// the default registry. 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 Desc documentation.
//
// 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,
// it is not safe to do so concurrently.)
-func Register(m Collector) error {
- _, err := defRegistry.Register(m)
- return err
+func Register(c Collector) error {
+ return registry.Default.Register(c)
}
// MustRegister works like Register but panics where Register would have
// returned an error.
-func MustRegister(m Collector) {
- err := Register(m)
- if err != nil {
- panic(err)
- }
+func MustRegister(c Collector) {
+ registry.MustRegister(registry.Default, c)
}
// RegisterOrGet works like Register but does not return an error if a Collector
@@ -129,18 +87,14 @@
//
// As for Register, it is still not safe to call RegisterOrGet with the same
// Collector multiple times concurrently.
-func RegisterOrGet(m Collector) (Collector, error) {
- return defRegistry.RegisterOrGet(m)
+func RegisterOrGet(c Collector) (Collector, error) {
+ return registry.RegisterOrGet(registry.Default, c)
}
// MustRegisterOrGet works like Register but panics where RegisterOrGet would
// have returned an error.
-func MustRegisterOrGet(m Collector) Collector {
- existing, err := RegisterOrGet(m)
- if err != nil {
- panic(err)
- }
- return existing
+func MustRegisterOrGet(c Collector) Collector {
+ return registry.MustRegisterOrGet(registry.Default, c)
}
// Unregister unregisters the Collector that equals the Collector passed in as
@@ -148,50 +102,10 @@
// yields the same set of descriptors.) The function returns whether a Collector
// was unregistered.
func Unregister(c Collector) bool {
- return defRegistry.Unregister(c)
+ return registry.Default.Unregister(c)
}
-// SetMetricFamilyInjectionHook sets a function that is called whenever metrics
-// 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
-}
+// TODO: Move out from here.
// 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
@@ -305,7 +219,7 @@
}()
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 {
if _, exists := descIDs[desc.id]; !exists {
collectorID += desc.id
@@ -670,13 +584,6 @@
}
}
-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
// returns the decorated writer and the appropriate "Content-Encoding" header
// (which is empty if no compression is enabled).
diff --git a/prometheus/registry/doc.go b/prometheus/registry/doc.go
new file mode 100644
index 0000000..1aa1022
--- /dev/null
+++ b/prometheus/registry/doc.go
@@ -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
diff --git a/prometheus/registry/error.go b/prometheus/registry/error.go
new file mode 100644
index 0000000..960d3d3
--- /dev/null
+++ b/prometheus/registry/error.go
@@ -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
+}
diff --git a/prometheus/registry/impl.go b/prometheus/registry/impl.go
new file mode 100644
index 0000000..07586b4
--- /dev/null
+++ b/prometheus/registry/impl.go
@@ -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 ®istry{}
+}
+
+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
+}
diff --git a/prometheus/registry/interface.go b/prometheus/registry/interface.go
new file mode 100644
index 0000000..8b180bd
--- /dev/null
+++ b/prometheus/registry/interface.go
@@ -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
+}
diff --git a/prometheus/registry/util.go b/prometheus/registry/util.go
new file mode 100644
index 0000000..85fe34d
--- /dev/null
+++ b/prometheus/registry/util.go
@@ -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
+}
diff --git a/prometheus/vec.go b/prometheus/vec.go
index 68f9461..c7dfc4b 100644
--- a/prometheus/vec.go
+++ b/prometheus/vec.go
@@ -241,6 +241,7 @@
if !ok {
// Copy labelValues. Otherwise, they would be allocated even if we don't go
// down this code path.
+ // TODO: Use copy.
copiedLabelValues := append(make([]string, 0, len(labelValues)), labelValues...)
metric = m.newMetric(copiedLabelValues...)
m.children[hash] = metric