diff --git a/NOTICE b/NOTICE index 37e4a7d41..dd878a30e 100644 --- a/NOTICE +++ b/NOTICE @@ -7,11 +7,6 @@ SoundCloud Ltd. (http://soundcloud.com/). The following components are included in this product: -goautoneg -http://bitbucket.org/ww/goautoneg -Copyright 2011, Open Knowledge Foundation Ltd. -See README.txt for license details. - perks - a fork of https://github.com/bmizerany/perks https://github.com/beorn7/perks Copyright 2013-2015 Blake Mizerany, Björn Rabenstein diff --git a/prometheus/collector.go b/prometheus/collector.go index c04688009..adc07b172 100644 --- a/prometheus/collector.go +++ b/prometheus/collector.go @@ -37,16 +37,16 @@ type Collector interface { // 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. The descriptor of - // each sent metric is one of those returned by Describe. Returned - // metrics that share 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. + // Collect is called by the Prometheus registry when collecting + // metrics. The implementation sends each collected metric via the + // provided channel and returns once the last metric has been sent. The + // descriptor of each sent metric is one of those returned by + // Describe. Returned metrics that share 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. Collect(chan<- Metric) } diff --git a/prometheus/doc.go b/prometheus/doc.go index ca56f5ede..b428033c0 100644 --- a/prometheus/doc.go +++ b/prometheus/doc.go @@ -11,18 +11,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package prometheus provides embeddable metric primitives for servers and -// standardized exposition of telemetry through a web services interface. +// Package prometheus provides metrics primitives to instrument code for +// monitoring. It also offers a standardized way to register metrics and then +// expose them via an HTTP endpoint or push them to a Pushgateway. See the +// sub-package registry for advanced metrics registration and the sub-package +// promhttp for customized HTTP exposition and instrumentation. // // All exported functions and methods are safe to be used concurrently unless // specified otherwise. // -// To expose metrics registered with the Prometheus registry, an HTTP server -// needs to know about the Prometheus handler. The usual endpoint is "/metrics". -// -// http.Handle("/metrics", prometheus.Handler()) -// -// As a starting point a very basic usage example: +// As a starting point, a very basic usage example: // // package main // @@ -44,6 +42,7 @@ // ) // // func init() { +// // Metrics have to be registered to be exposed: // prometheus.MustRegister(cpuTemp) // prometheus.MustRegister(hdFailures) // } @@ -52,6 +51,8 @@ // cpuTemp.Set(65.3) // hdFailures.Inc() // +// // The Handler function provides a default handler to expose metrics +// // via an HTTP server. "/metrics" is the usual endpoint for that. // http.Handle("/metrics", prometheus.Handler()) // http.ListenAndServe(":8080", nil) // } @@ -74,8 +75,8 @@ // Those are all the parts needed for basic usage. Detailed documentation and // examples are provided below. // -// Everything else this package offers is essentially for "power users" only. A -// few pointers to "power user features": +// Everything else this package and its sub-packages offer is essentially for +// "power users" only. A few pointers to "power user features": // // All the various ...Opts structs have a ConstLabels field for labels that // never change their value (which is only useful under special circumstances, @@ -84,9 +85,6 @@ // The Untyped metric behaves like a Gauge, but signals the Prometheus server // not to assume anything about its type. // -// Functions to fine-tune how the metric registry works: EnableCollectChecks, -// PanicOnCollectError, Register, Unregister, SetMetricFamilyInjectionHook. -// // For custom metric collection, there are two entry points: Custom Metric // implementations and custom Collector implementations. A Metric is the // fundamental unit in the Prometheus data model: a sample at a point in time @@ -108,4 +106,14 @@ // A good example for a custom Collector is the ExpVarCollector included in this // package, which exports variables exported via the "expvar" package as // Prometheus metrics. +// +// The functions Register, Unregister, MustRegister, RegisterOrGet, and +// MustRegisterOrGet all act on the default registry. See the registry +// sub-package for working with custom registries. +// +// The functions Handler and UninstrumentedHandler create an HTTP handler to +// serve metrics from the default registry in the default way. The functions +// Push and PushAll push the metrics from the default registry via HTTP to a +// Pushgateway. See the promhttp sub-package to serve or push metrics in custom +// ways from custom registries. package prometheus diff --git a/prometheus/expvar.go b/prometheus/expvar_collector.go similarity index 81% rename from prometheus/expvar.go rename to prometheus/expvar_collector.go index 0f7630d53..18a99d5fa 100644 --- a/prometheus/expvar.go +++ b/prometheus/expvar_collector.go @@ -18,21 +18,21 @@ import ( "expvar" ) -// ExpvarCollector collects metrics from the expvar interface. It provides a -// quick way to expose numeric values that are already exported via expvar as -// Prometheus metrics. Note that the data models of expvar and Prometheus are -// fundamentally different, and that the ExpvarCollector is inherently -// slow. Thus, the ExpvarCollector is probably great for experiments and -// prototying, but you should seriously consider a more direct implementation of -// Prometheus metrics for monitoring production systems. -// -// Use NewExpvarCollector to create new instances. -type ExpvarCollector struct { +type expvarCollector struct { exports map[string]*Desc } -// NewExpvarCollector returns a newly allocated ExpvarCollector that still has -// to be registered with the Prometheus registry. +// NewExpvarCollector returns a newly allocated expvar Collector that still has +// to be registered with a Prometheus registry. +// +// An expvar Collector collects metrics from the expvar interface. It provides a +// quick way to expose numeric values that are already exported via expvar as +// Prometheus metrics. Note that the data models of expvar and Prometheus are +// fundamentally different, and that the expvar Collector is inherently slower +// than native Prometheus metrics. Thus, the expvar Collector is probably great +// for experiments and prototying, but you should seriously consider a more +// direct implementation of Prometheus metrics for monitoring production +// systems. // // The exports map has the following meaning: // @@ -59,21 +59,21 @@ type ExpvarCollector struct { // sample values. // // Anything that does not fit into the scheme above is silently ignored. -func NewExpvarCollector(exports map[string]*Desc) *ExpvarCollector { - return &ExpvarCollector{ +func NewExpvarCollector(exports map[string]*Desc) Collector { + return &expvarCollector{ exports: exports, } } // Describe implements Collector. -func (e *ExpvarCollector) Describe(ch chan<- *Desc) { +func (e *expvarCollector) Describe(ch chan<- *Desc) { for _, desc := range e.exports { ch <- desc } } // Collect implements Collector. -func (e *ExpvarCollector) Collect(ch chan<- Metric) { +func (e *expvarCollector) Collect(ch chan<- Metric) { for name, desc := range e.exports { var m Metric expVar := expvar.Get(name) diff --git a/prometheus/expvar_test.go b/prometheus/expvar_collector_test.go similarity index 100% rename from prometheus/expvar_test.go rename to prometheus/expvar_collector_test.go diff --git a/prometheus/go_collector.go b/prometheus/go_collector.go index b0d4fb95c..abc9d4ec4 100644 --- a/prometheus/go_collector.go +++ b/prometheus/go_collector.go @@ -17,7 +17,7 @@ type goCollector struct { // NewGoCollector returns a collector which exports metrics about the current // go process. -func NewGoCollector() *goCollector { +func NewGoCollector() Collector { return &goCollector{ goroutines: NewGauge(GaugeOpts{ Namespace: "go", diff --git a/prometheus/http.go b/prometheus/http.go index e078e3ed1..0e94ef3e1 100644 --- a/prometheus/http.go +++ b/prometheus/http.go @@ -23,6 +23,27 @@ import ( "time" ) +// 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. +// +// Please note the issues described in the doc comment of InstrumentHandler. You +// might want to consider using UninstrumentedHandler instead. +func Handler() http.Handler { + // TODO + return InstrumentHandler("prometheus", defRegistry) +} + +// UninstrumentedHandler works in the same way as Handler, but the returned HTTP +// handler is not instrumented. This is useful if no instrumentation is desired +// (for whatever reason) or if the instrumentation has to happen with a +// different handler name (or with a different instrumentation approach +// altogether). See the InstrumentHandler example. +func UninstrumentedHandler() http.Handler { + // TODO + return defRegistry +} + var instLabels = []string{"method", "code"} type nower interface { diff --git a/prometheus/process_collector.go b/prometheus/process_collector.go index d8cf0eda3..e31e62e78 100644 --- a/prometheus/process_collector.go +++ b/prometheus/process_collector.go @@ -28,7 +28,7 @@ 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. -func NewProcessCollector(pid int, namespace string) *processCollector { +func NewProcessCollector(pid int, namespace string) Collector { return NewProcessCollectorPIDFn( func() (int, error) { return pid, nil }, namespace, @@ -43,7 +43,7 @@ func NewProcessCollector(pid int, namespace string) *processCollector { func NewProcessCollectorPIDFn( pidFn func() (int, error), namespace string, -) *processCollector { +) Collector { c := processCollector{ pidFn: pidFn, collectFn: func(chan<- Metric) {}, diff --git a/prometheus/push.go b/prometheus/push.go index 5ec0a3ab3..c528e65b0 100644 --- a/prometheus/push.go +++ b/prometheus/push.go @@ -19,6 +19,8 @@ package prometheus +// TODO: Move partially to promhttp. + // Push triggers a metric collection by the default registry and pushes all // collected metrics to the Pushgateway specified by url. See the Pushgateway // documentation for detailed implications of the job and instance diff --git a/prometheus/registry.go b/prometheus/registry.go index f6ae51bed..7e1c36ee4 100644 --- a/prometheus/registry.go +++ b/prometheus/registry.go @@ -36,95 +36,183 @@ import ( "github.com/prometheus/common/expfmt" dto "github.com/prometheus/client_model/go" -) -var ( - defRegistry = newDefaultRegistry() - errAlreadyReg = errors.New("duplicate metrics collector registration attempted") + "github.com/prometheus/client_golang/prometheus" ) -// 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" +// DefaultRegistry is the default registry implicitly used by a number of +// convenience functions. It has a ProcessCollector and a GoCollector +// pre-registered. +var DefaultRegistry = New() - acceptEncodingHeader = "Accept-Encoding" - acceptHeader = "Accept" -) +func init() { + MustRegister(DefaultRegistry, NewProcessCollector(os.Getpid(), "")) + MustRegister(DefaultRegistry, NewGoCollector()) +} -// 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. +// NewRegistry creates a new vanilla Registry without any Collectors +// pre-registered. +func NewRegistry() Registry { + return ®istry{ + collectorsByID: map[uint64]Collector{}, + descIDs: map[uint64]struct{}{}, + dimHashesByName: map[string]uint64{}, + } +} + +// NewRegistryWithInjectionHook creates a registry with the provided hook to inject +// MetricFamilies. The hook 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 +// the injection results in inconsistent metrics, the Collect call will return +// an error. // -// Please note the issues described in the doc comment of InstrumentHandler. You -// might want to consider using UninstrumentedHandler instead. -func Handler() http.Handler { - return InstrumentHandler("prometheus", defRegistry) -} - -// UninstrumentedHandler works in the same way as Handler, but the returned HTTP -// handler is not instrumented. This is useful if no instrumentation is desired -// (for whatever reason) or if the instrumentation has to happen with a -// different handler name (or with a different instrumentation approach -// altogether). See the InstrumentHandler example. -func UninstrumentedHandler() http.Handler { - return defRegistry -} - -// 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. +// Sorting concerns: The caller is responsible for sorting the label pairs in +// each metric. However, the order of metrics in each MetricFamily and the order +// of MetricFamilies in the returned slice does not matter as those will be +// sorted by the registry. This sorting is required anyway after merging with +// the metric families collected conventionally. // -// 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 +// The function must be callable at any time and concurrently. +func NewRegistryWithInjectionHook(hook func() []*dto.MetricFamily) Registry { + r = NewRegistry() + r.metricFamilyInjectionHook = hook + return r } -// MustRegister works like Register but panics where Register would have -// returned an error. MustRegister is also Variadic, where Register only -// accepts a single Collector to register. -func MustRegister(m ...Collector) { - for i := range m { - if err := Register(m[i]); err != nil { +// NewPedanticRegistry returns a registry that checks during collection if the +// Descs provided by registered Collectors are consistent with their collected +// Metrics. +// +// Usually, a Registry will be happy as long as the union of all collected +// Metrics is consistent and valid even if some metrics are not consistent with +// any of the Descs provided by their Collector. Well-behaved Collectors will +// only return Metrics consistent with the provided Descs. To test the +// implementation of Collectors, this Registry can be used. +func NewPedanticRegistry() Registry { + r = NewRegistry() + r.collectChecksEnabled = true + return r +} + +// 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(prometheus.Collector) error + // Unregister unregisters the Collector that equals the Collector passed + // in as an argument. (Two Collectors are considered equal if their + // Describe method yields the same set of descriptors.) The function + // returns whether a Collector was unregistered. + // + // Note that even after unregistering, it will not be possible to + // register a new Collector that is inconsistent with the unregistered + // Collector, e.g. a Collector collecting metrics with the same name but + // a different help string. The rationale here is that the same registry + // instance must only collect consistent metrics throughout its + // lifetime. + Unregister(prometheus.Collector) bool + // Collect collects metrics from registered Collectors and returns them + // as lexicographically sorted MetricFamily protobufs. Even if an error + // occurs, Collect attempts to collect as many metrics as + // possible. Hence, if a non-nil error is returned, the returned + // MetricFamily slice could be nil (in case of a fatal error that + // prevented any meaningful metric collection) or contain a number of + // MetricFamily protobufs, some of which might be incomplete, and some + // might be missing altogether. The returned error (which might be a + // multierror.Error) explains the details. In any case, the MetricFamily + // protobufs are consistent and valid for Prometheus to ingest (e.g. no + // duplicate metrics, no invalid identifiers). In scenarios where + // complete collection is critical, the returned MetricFamily protobufs + // should be disregareded if the returned error is non-nil. + Collect() ([]*dto.MetricFamily, error) +} + +// MustRegisterWith registers the provided Collectors with the provided Registry +// and panics upon the first registration that causes an error. +func MustRegisterWith(r Registry, cs ...prometheus.Collector) { + for _, c := range cs { + if err := r.Register(c); err != nil { panic(err) } } } +// RegisterWithOrGet registers the provided Collector with the provided Registry +// but does not return an error if a Collector is registered that equals a +// previously registered Collector. (Two Collectors are considered equal if +// their Describe method yields the same set of descriptors.) Instead, the +// previously registered Collector is returned (which is helpful if the new and +// previously registered Collectors are equal but not identical, i.e. not +// pointers to the same object). +// +// As for RegisterWith, it is still not safe to call RegisteWithrOrGet with the +// same Collector multiple times concurrently. +func RegisterWithOrGet(r Registry, c prometheus.Collector) (prometheus.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 +} + +// MustRegisterOrGet works like RegisterOrGet but panics where RegisterOrGet +// would have returned an error. +func MustRegisterOrGet(r Registry, c prometheus.Collector) prometheus.Collector { + existing, err := RegisterOrGet(r, c) + if err != nil { + panic(err) + } + return existing +} + +// 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. +// +// It is in general not safe to register the same Collector multiple +// times concurrently. +// +// Note: This is merely a convenience wrapper around registry.Default.Register(c). +func Register(c Collector) error { + return registry.Default.Register(c) +} + +// MustRegister works like Register but panics where Register would have +// returned an error. MustRegister is also Variadic, where Register only +// accepts a single Collector to register. +// +// Note: This is merely a convenience wrapper around +// registry.MustRegister(registry.Default, cs...). +func MustRegister(cs ...Collector) { + registry.MustRegister(registry.Default, cs...) +} + // RegisterOrGet works like Register but does not return an error if a Collector // is registered that equals a previously registered Collector. (Two Collectors // are considered equal if their Describe method yields the same set of @@ -134,89 +222,122 @@ func MustRegister(m ...Collector) { // // 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) +// +// Note: This is merely a convenience wrapper around +// registry.RegisterOrGet(registry.Default, c). +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 +// MustRegisterOrGet works like RegisterOrGet but panics where RegisterOrGet +// would have returned an error. +// +// Note: This is merely a convenience wrapper around +// registry.MustRegisterOrGet(registry.Default, c). +func MustRegisterOrGet(c Collector) Collector { + return registry.MustRegisterOrGet(registry.Default, c) } // Unregister unregisters the Collector that equals the Collector passed in as -// an argument. (Two Collectors are considered equal if their Describe method -// yields the same set of descriptors.) The function returns whether a Collector -// was unregistered. -func Unregister(c Collector) bool { - return defRegistry.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). +// an argument from the default registry. (Two Collectors are considered equal +// if their Describe method yields the same set of descriptors.) The function +// returns whether a Collector was unregistered. // -// 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. +// Note that even after unregistering, it will not be possible to register a new +// Collector that is inconsistent with the unregistered Collector, e.g. a +// Collector collecting metrics with the same name but a different help +// string. // -// The function must be callable at any time and concurrently. -func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) { - defRegistry.metricFamilyInjectionHook = hook +// Note: This is merely a convenience wrapper around +// registry.Default.Unregister(c). +func Unregister(c Collector) bool { + return registry.Default.Unregister(c) } -// 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 +type AlreadyRegisteredError struct { + ExistingCollector, NewCollector prometheus.Collector } -// 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 +func (err AlreadyRegisteredError) Error() string { + return "duplicate metrics collector registration attempted" } -// 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 -// encountered. Note that pbutil.WriteDelimited and pbutil.MetricFamilyToText -// are encoders. -type encoder func(io.Writer, *dto.MetricFamily) (int, error) - type registry struct { mtx sync.RWMutex collectorsByID map[uint64]Collector // ID is a hash of the descIDs. descIDs map[uint64]struct{} dimHashesByName map[string]uint64 - bufPool chan *bytes.Buffer - metricFamilyPool chan *dto.MetricFamily - metricPool chan *dto.Metric metricFamilyInjectionHook func() []*dto.MetricFamily + collectChecksEnabled bool +} + +func (r *registry) Register(prometheus.Collector) error { + return nil // TODO +} - panicOnCollectError, collectChecksEnabled bool +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 +} + +// metricSorter is a sortable slice of *dto.Metric. +type metricSorter []*dto.Metric + +func (s metricSorter) Len() int { + return len(s) +} + +func (s metricSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s metricSorter) Less(i, j int) bool { + if len(s[i].Label) != len(s[j].Label) { + // This should not happen. The metrics are + // inconsistent. However, we have to deal with the fact, as + // people might use custom collectors or metric family injection + // to create inconsistent metrics. So let's simply compare the + // number of labels in this case. That will still yield + // reproducible sorting. + return len(s[i].Label) < len(s[j].Label) + } + for n, lp := range s[i].Label { + vi := lp.GetValue() + vj := s[j].Label[n].GetValue() + if vi != vj { + return vi < vj + } + } + + // We should never arrive here. Multiple metrics with the same + // label set in the same scrape will lead to undefined ingestion + // behavior. However, as above, we have to provide stable sorting + // here, even for inconsistent metrics. So sort equal metrics + // by their timestamp, with missing timestamps (implying "now") + // coming last. + if s[i].TimestampMs == nil { + return false + } + if s[j].TimestampMs == nil { + return true + } + return s[i].GetTimestampMs() < s[j].GetTimestampMs() +} + +// TODO port from here on. + +// 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 +// encountered. Note that pbutil.WriteDelimited and pbutil.MetricFamilyToText +// are encoders. +type encoder func(io.Writer, *dto.MetricFamily) (int, error) + func (r *registry) Register(c Collector) (Collector, error) { descChan := make(chan *Desc, capDescChan) go func() { @@ -665,21 +786,6 @@ func (r *registry) giveMetric(m *dto.Metric) { } func newRegistry() *registry { - return ®istry{ - collectorsByID: map[uint64]Collector{}, - descIDs: map[uint64]struct{}{}, - dimHashesByName: map[string]uint64{}, - bufPool: make(chan *bytes.Buffer, numBufs), - metricFamilyPool: make(chan *dto.MetricFamily, numMetricFamilies), - metricPool: make(chan *dto.Metric, numMetrics), - } -} - -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 @@ -696,46 +802,3 @@ func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) } return writer, "" } - -type metricSorter []*dto.Metric - -func (s metricSorter) Len() int { - return len(s) -} - -func (s metricSorter) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s metricSorter) Less(i, j int) bool { - if len(s[i].Label) != len(s[j].Label) { - // This should not happen. The metrics are - // inconsistent. However, we have to deal with the fact, as - // people might use custom collectors or metric family injection - // to create inconsistent metrics. So let's simply compare the - // number of labels in this case. That will still yield - // reproducible sorting. - return len(s[i].Label) < len(s[j].Label) - } - for n, lp := range s[i].Label { - vi := lp.GetValue() - vj := s[j].Label[n].GetValue() - if vi != vj { - return vi < vj - } - } - - // We should never arrive here. Multiple metrics with the same - // label set in the same scrape will lead to undefined ingestion - // behavior. However, as above, we have to provide stable sorting - // here, even for inconsistent metrics. So sort equal metrics - // by their timestamp, with missing timestamps (implying "now") - // coming last. - if s[i].TimestampMs == nil { - return false - } - if s[j].TimestampMs == nil { - return true - } - return s[i].GetTimestampMs() < s[j].GetTimestampMs() -}