mirror of
https://github.com/openfaas/faas.git
synced 2025-06-21 22:33:23 +00:00
Merge master into breakout_swarm
Signed-off-by: Alex Ellis <alexellis2@gmail.com>
This commit is contained in:
26
gateway/vendor/github.com/prometheus/client_golang/.gitignore
generated
vendored
Normal file
26
gateway/vendor/github.com/prometheus/client_golang/.gitignore
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
*~
|
||||
*#
|
||||
.build
|
9
gateway/vendor/github.com/prometheus/client_golang/.travis.yml
generated
vendored
Normal file
9
gateway/vendor/github.com/prometheus/client_golang/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.5.4
|
||||
- 1.6.2
|
||||
|
||||
script:
|
||||
- go test -short ./...
|
18
gateway/vendor/github.com/prometheus/client_golang/AUTHORS.md
generated
vendored
Normal file
18
gateway/vendor/github.com/prometheus/client_golang/AUTHORS.md
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
The Prometheus project was started by Matt T. Proud (emeritus) and
|
||||
Julius Volz in 2012.
|
||||
|
||||
Maintainers of this repository:
|
||||
|
||||
* Björn Rabenstein <beorn@soundcloud.com>
|
||||
|
||||
The following individuals have contributed code to this repository
|
||||
(listed in alphabetical order):
|
||||
|
||||
* Bernerd Schaefer <bj.schaefer@gmail.com>
|
||||
* Björn Rabenstein <beorn@soundcloud.com>
|
||||
* Daniel Bornkessel <daniel@soundcloud.com>
|
||||
* Jeff Younker <jeff@drinktomi.com>
|
||||
* Julius Volz <julius.volz@gmail.com>
|
||||
* Matt T. Proud <matt.proud@gmail.com>
|
||||
* Tobias Schmidt <ts@soundcloud.com>
|
||||
|
109
gateway/vendor/github.com/prometheus/client_golang/CHANGELOG.md
generated
vendored
Normal file
109
gateway/vendor/github.com/prometheus/client_golang/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
## 0.8.0 / 2016-08-17
|
||||
* [CHANGE] Registry is doing more consistency checks. This might break
|
||||
existing setups that used to export inconsistent metrics.
|
||||
* [CHANGE] Pushing to Pushgateway moved to package `push` and changed to allow
|
||||
arbitrary grouping.
|
||||
* [CHANGE] Removed `SelfCollector`.
|
||||
* [CHANGE] Removed `PanicOnCollectError` and `EnableCollectChecks` methods.
|
||||
* [CHANGE] Moved packages to the prometheus/common repo: `text`, `model`,
|
||||
`extraction`.
|
||||
* [CHANGE] Deprecated a number of functions.
|
||||
* [FEATURE] Allow custom registries. Added `Registerer` and `Gatherer`
|
||||
interfaces.
|
||||
* [FEATURE] Separated HTTP exposition, allowing custom HTTP handlers (package
|
||||
`promhttp`) and enabling the creation of other exposition mechanisms.
|
||||
* [FEATURE] `MustRegister` is variadic now, allowing registration of many
|
||||
collectors in one call.
|
||||
* [FEATURE] Added HTTP API v1 package.
|
||||
* [ENHANCEMENT] Numerous documentation improvements.
|
||||
* [ENHANCEMENT] Improved metric sorting.
|
||||
* [ENHANCEMENT] Inlined fnv64a hashing for improved performance.
|
||||
* [ENHANCEMENT] Several test improvements.
|
||||
* [BUGFIX] Handle collisions in MetricVec.
|
||||
|
||||
## 0.7.0 / 2015-07-27
|
||||
* [CHANGE] Rename ExporterLabelPrefix to ExportedLabelPrefix.
|
||||
* [BUGFIX] Closed gaps in metric consistency check.
|
||||
* [BUGFIX] Validate LabelName/LabelSet on JSON unmarshaling.
|
||||
* [ENHANCEMENT] Document the possibility to create "empty" metrics in
|
||||
a metric vector.
|
||||
* [ENHANCEMENT] Fix and clarify various doc comments and the README.md.
|
||||
* [ENHANCEMENT] (Kind of) solve "The Proxy Problem" of http.InstrumentHandler.
|
||||
* [ENHANCEMENT] Change responseWriterDelegator.written to int64.
|
||||
|
||||
## 0.6.0 / 2015-06-01
|
||||
* [CHANGE] Rename process_goroutines to go_goroutines.
|
||||
* [ENHANCEMENT] Validate label names during YAML decoding.
|
||||
* [ENHANCEMENT] Add LabelName regular expression.
|
||||
* [BUGFIX] Ensure alignment of struct members for 32-bit systems.
|
||||
|
||||
## 0.5.0 / 2015-05-06
|
||||
* [BUGFIX] Removed a weakness in the fingerprinting aka signature code.
|
||||
This makes fingerprinting slower and more allocation-heavy, but the
|
||||
weakness was too severe to be tolerated.
|
||||
* [CHANGE] As a result of the above, Metric.Fingerprint is now returning
|
||||
a different fingerprint. To keep the same fingerprint, the new method
|
||||
Metric.FastFingerprint was introduced, which will be used by the
|
||||
Prometheus server for storage purposes (implying that a collision
|
||||
detection has to be added, too).
|
||||
* [ENHANCEMENT] The Metric.Equal and Metric.Before do not depend on
|
||||
fingerprinting anymore, removing the possibility of an undetected
|
||||
fingerprint collision.
|
||||
* [FEATURE] The Go collector in the exposition library includes garbage
|
||||
collection stats.
|
||||
* [FEATURE] The exposition library allows to create constant "throw-away"
|
||||
summaries and histograms.
|
||||
* [CHANGE] A number of new reserved labels and prefixes.
|
||||
|
||||
## 0.4.0 / 2015-04-08
|
||||
* [CHANGE] Return NaN when Summaries have no observations yet.
|
||||
* [BUGFIX] Properly handle Summary decay upon Write().
|
||||
* [BUGFIX] Fix the documentation link to the consumption library.
|
||||
* [FEATURE] Allow the metric family injection hook to merge with existing
|
||||
metric families.
|
||||
* [ENHANCEMENT] Removed cgo dependency and conditional compilation of procfs.
|
||||
* [MAINTENANCE] Adjusted to changes in matttproud/golang_protobuf_extensions.
|
||||
|
||||
## 0.3.2 / 2015-03-11
|
||||
* [BUGFIX] Fixed the receiver type of COWMetric.Set(). This method is
|
||||
only used by the Prometheus server internally.
|
||||
* [CLEANUP] Added licenses of vendored code left out by godep.
|
||||
|
||||
## 0.3.1 / 2015-03-04
|
||||
* [ENHANCEMENT] Switched fingerprinting functions from own free list to
|
||||
sync.Pool.
|
||||
* [CHANGE] Makefile uses Go 1.4.2 now (only relevant for examples and tests).
|
||||
|
||||
## 0.3.0 / 2015-03-03
|
||||
* [CHANGE] Changed the fingerprinting for metrics. THIS WILL INVALIDATE ALL
|
||||
PERSISTED FINGERPRINTS. IF YOU COMPILE THE PROMETHEUS SERVER WITH THIS
|
||||
VERSION, YOU HAVE TO WIPE THE PREVIOUSLY CREATED STORAGE.
|
||||
* [CHANGE] LabelValuesToSignature removed. (Nobody had used it, and it was
|
||||
arguably broken.)
|
||||
* [CHANGE] Vendored dependencies. Those are only used by the Makefile. If
|
||||
client_golang is used as a library, the vendoring will stay out of your way.
|
||||
* [BUGFIX] Remove a weakness in the fingerprinting for metrics. (This made
|
||||
the fingerprinting change above necessary.)
|
||||
* [FEATURE] Added new fingerprinting functions SignatureForLabels and
|
||||
SignatureWithoutLabels to be used by the Prometheus server. These functions
|
||||
require fewer allocations than the ones currently used by the server.
|
||||
|
||||
## 0.2.0 / 2015-02-23
|
||||
* [FEATURE] Introduce new Histagram metric type.
|
||||
* [CHANGE] Ignore process collector errors for now (better error handling
|
||||
pending).
|
||||
* [CHANGE] Use clear error interface for process pidFn.
|
||||
* [BUGFIX] Fix Go download links for several archs and OSes.
|
||||
* [ENHANCEMENT] Massively improve Gauge and Counter performance.
|
||||
* [ENHANCEMENT] Catch illegal label names for summaries in histograms.
|
||||
* [ENHANCEMENT] Reduce allocations during fingerprinting.
|
||||
* [ENHANCEMENT] Remove cgo dependency. procfs package will only be included if
|
||||
both cgo is available and the build is for an OS with procfs.
|
||||
* [CLEANUP] Clean up code style issues.
|
||||
* [CLEANUP] Mark slow test as such and exclude them from travis.
|
||||
* [CLEANUP] Update protobuf library package name.
|
||||
* [CLEANUP] Updated vendoring of beorn7/perks.
|
||||
|
||||
## 0.1.0 / 2015-02-02
|
||||
* [CLEANUP] Introduced semantic versioning and changelog. From now on,
|
||||
changes will be reported in this file.
|
18
gateway/vendor/github.com/prometheus/client_golang/CONTRIBUTING.md
generated
vendored
Normal file
18
gateway/vendor/github.com/prometheus/client_golang/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# Contributing
|
||||
|
||||
Prometheus uses GitHub to manage reviews of pull requests.
|
||||
|
||||
* If you have a trivial fix or improvement, go ahead and create a pull
|
||||
request, addressing (with `@...`) one or more of the maintainers
|
||||
(see [AUTHORS.md](AUTHORS.md)) in the description of the pull request.
|
||||
|
||||
* If you plan to do something more involved, first discuss your ideas
|
||||
on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers).
|
||||
This will avoid unnecessary work and surely give you and us a good deal
|
||||
of inspiration.
|
||||
|
||||
* Relevant coding style guidelines are the [Go Code Review
|
||||
Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)
|
||||
and the _Formatting and style_ section of Peter Bourgon's [Go: Best
|
||||
Practices for Production
|
||||
Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style).
|
4
gateway/vendor/github.com/prometheus/client_golang/README.md
generated
vendored
4
gateway/vendor/github.com/prometheus/client_golang/README.md
generated
vendored
@ -1,7 +1,6 @@
|
||||
# Prometheus Go client library
|
||||
|
||||
[](https://travis-ci.org/prometheus/client_golang)
|
||||
[](https://goreportcard.com/report/github.com/prometheus/client_golang)
|
||||
|
||||
This is the [Go](http://golang.org) client library for
|
||||
[Prometheus](http://prometheus.io). It has two separate parts, one for
|
||||
@ -30,8 +29,7 @@ The
|
||||
[`api/prometheus` directory](https://github.com/prometheus/client_golang/tree/master/api/prometheus)
|
||||
contains the client for the
|
||||
[Prometheus HTTP API](http://prometheus.io/docs/querying/api/). It allows you
|
||||
to write Go applications that query time series data from a Prometheus
|
||||
server. It is still in alpha stage.
|
||||
to write Go applications that query time series data from a Prometheus server.
|
||||
|
||||
## Where is `model`, `extraction`, and `text`?
|
||||
|
||||
|
1
gateway/vendor/github.com/prometheus/client_golang/VERSION
generated
vendored
Normal file
1
gateway/vendor/github.com/prometheus/client_golang/VERSION
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
0.8.0
|
345
gateway/vendor/github.com/prometheus/client_golang/api/prometheus/api.go
generated
vendored
Normal file
345
gateway/vendor/github.com/prometheus/client_golang/api/prometheus/api.go
generated
vendored
Normal file
@ -0,0 +1,345 @@
|
||||
// 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 prometheus provides bindings to the Prometheus HTTP API:
|
||||
// http://prometheus.io/docs/querying/api/
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
)
|
||||
|
||||
const (
|
||||
statusAPIError = 422
|
||||
apiPrefix = "/api/v1"
|
||||
|
||||
epQuery = "/query"
|
||||
epQueryRange = "/query_range"
|
||||
epLabelValues = "/label/:name/values"
|
||||
epSeries = "/series"
|
||||
)
|
||||
|
||||
type ErrorType string
|
||||
|
||||
const (
|
||||
// The different API error types.
|
||||
ErrBadData ErrorType = "bad_data"
|
||||
ErrTimeout = "timeout"
|
||||
ErrCanceled = "canceled"
|
||||
ErrExec = "execution"
|
||||
ErrBadResponse = "bad_response"
|
||||
)
|
||||
|
||||
// Error is an error returned by the API.
|
||||
type Error struct {
|
||||
Type ErrorType
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Type, e.Msg)
|
||||
}
|
||||
|
||||
// CancelableTransport is like net.Transport but provides
|
||||
// per-request cancelation functionality.
|
||||
type CancelableTransport interface {
|
||||
http.RoundTripper
|
||||
CancelRequest(req *http.Request)
|
||||
}
|
||||
|
||||
var DefaultTransport CancelableTransport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
// Config defines configuration parameters for a new client.
|
||||
type Config struct {
|
||||
// The address of the Prometheus to connect to.
|
||||
Address string
|
||||
|
||||
// Transport is used by the Client to drive HTTP requests. If not
|
||||
// provided, DefaultTransport will be used.
|
||||
Transport CancelableTransport
|
||||
}
|
||||
|
||||
func (cfg *Config) transport() CancelableTransport {
|
||||
if cfg.Transport == nil {
|
||||
return DefaultTransport
|
||||
}
|
||||
return cfg.Transport
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
url(ep string, args map[string]string) *url.URL
|
||||
do(context.Context, *http.Request) (*http.Response, []byte, error)
|
||||
}
|
||||
|
||||
// New returns a new Client.
|
||||
//
|
||||
// It is safe to use the returned Client from multiple goroutines.
|
||||
func New(cfg Config) (Client, error) {
|
||||
u, err := url.Parse(cfg.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Path = strings.TrimRight(u.Path, "/") + apiPrefix
|
||||
|
||||
return &httpClient{
|
||||
endpoint: u,
|
||||
transport: cfg.transport(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type httpClient struct {
|
||||
endpoint *url.URL
|
||||
transport CancelableTransport
|
||||
}
|
||||
|
||||
func (c *httpClient) url(ep string, args map[string]string) *url.URL {
|
||||
p := path.Join(c.endpoint.Path, ep)
|
||||
|
||||
for arg, val := range args {
|
||||
arg = ":" + arg
|
||||
p = strings.Replace(p, arg, val, -1)
|
||||
}
|
||||
|
||||
u := *c.endpoint
|
||||
u.Path = p
|
||||
|
||||
return &u
|
||||
}
|
||||
|
||||
func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
|
||||
resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)
|
||||
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var body []byte
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = resp.Body.Close()
|
||||
<-done
|
||||
if err == nil {
|
||||
err = ctx.Err()
|
||||
}
|
||||
case <-done:
|
||||
}
|
||||
|
||||
return resp, body, err
|
||||
}
|
||||
|
||||
// apiClient wraps a regular client and processes successful API responses.
|
||||
// Successful also includes responses that errored at the API level.
|
||||
type apiClient struct {
|
||||
Client
|
||||
}
|
||||
|
||||
type apiResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
ErrorType ErrorType `json:"errorType"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
|
||||
resp, body, err := c.Client.do(ctx, req)
|
||||
if err != nil {
|
||||
return resp, body, err
|
||||
}
|
||||
|
||||
code := resp.StatusCode
|
||||
|
||||
if code/100 != 2 && code != statusAPIError {
|
||||
return resp, body, &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: fmt.Sprintf("bad response code %d", resp.StatusCode),
|
||||
}
|
||||
}
|
||||
|
||||
var result apiResponse
|
||||
|
||||
if err = json.Unmarshal(body, &result); err != nil {
|
||||
return resp, body, &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
if (code == statusAPIError) != (result.Status == "error") {
|
||||
err = &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
}
|
||||
}
|
||||
|
||||
if code == statusAPIError && result.Status == "error" {
|
||||
err = &Error{
|
||||
Type: result.ErrorType,
|
||||
Msg: result.Error,
|
||||
}
|
||||
}
|
||||
|
||||
return resp, []byte(result.Data), err
|
||||
}
|
||||
|
||||
// Range represents a sliced time range.
|
||||
type Range struct {
|
||||
// The boundaries of the time range.
|
||||
Start, End time.Time
|
||||
// The maximum time between two slices within the boundaries.
|
||||
Step time.Duration
|
||||
}
|
||||
|
||||
// queryResult contains result data for a query.
|
||||
type queryResult struct {
|
||||
Type model.ValueType `json:"resultType"`
|
||||
Result interface{} `json:"result"`
|
||||
|
||||
// The decoded value.
|
||||
v model.Value
|
||||
}
|
||||
|
||||
func (qr *queryResult) UnmarshalJSON(b []byte) error {
|
||||
v := struct {
|
||||
Type model.ValueType `json:"resultType"`
|
||||
Result json.RawMessage `json:"result"`
|
||||
}{}
|
||||
|
||||
err := json.Unmarshal(b, &v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch v.Type {
|
||||
case model.ValScalar:
|
||||
var sv model.Scalar
|
||||
err = json.Unmarshal(v.Result, &sv)
|
||||
qr.v = &sv
|
||||
|
||||
case model.ValVector:
|
||||
var vv model.Vector
|
||||
err = json.Unmarshal(v.Result, &vv)
|
||||
qr.v = vv
|
||||
|
||||
case model.ValMatrix:
|
||||
var mv model.Matrix
|
||||
err = json.Unmarshal(v.Result, &mv)
|
||||
qr.v = mv
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unexpected value type %q", v.Type)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// QueryAPI provides bindings the Prometheus's query API.
|
||||
type QueryAPI interface {
|
||||
// Query performs a query for the given time.
|
||||
Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
|
||||
// Query performs a query for the given range.
|
||||
QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
|
||||
}
|
||||
|
||||
// NewQueryAPI returns a new QueryAPI for the client.
|
||||
//
|
||||
// It is safe to use the returned QueryAPI from multiple goroutines.
|
||||
func NewQueryAPI(c Client) QueryAPI {
|
||||
return &httpQueryAPI{client: apiClient{c}}
|
||||
}
|
||||
|
||||
type httpQueryAPI struct {
|
||||
client Client
|
||||
}
|
||||
|
||||
func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
|
||||
u := h.client.url(epQuery, nil)
|
||||
q := u.Query()
|
||||
|
||||
q.Set("query", query)
|
||||
q.Set("time", ts.Format(time.RFC3339Nano))
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||
|
||||
_, body, err := h.client.do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var qres queryResult
|
||||
err = json.Unmarshal(body, &qres)
|
||||
|
||||
return model.Value(qres.v), err
|
||||
}
|
||||
|
||||
func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
|
||||
u := h.client.url(epQueryRange, nil)
|
||||
q := u.Query()
|
||||
|
||||
var (
|
||||
start = r.Start.Format(time.RFC3339Nano)
|
||||
end = r.End.Format(time.RFC3339Nano)
|
||||
step = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
|
||||
)
|
||||
|
||||
q.Set("query", query)
|
||||
q.Set("start", start)
|
||||
q.Set("end", end)
|
||||
q.Set("step", step)
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
req, _ := http.NewRequest("GET", u.String(), nil)
|
||||
|
||||
_, body, err := h.client.do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var qres queryResult
|
||||
err = json.Unmarshal(body, &qres)
|
||||
|
||||
return model.Value(qres.v), err
|
||||
}
|
453
gateway/vendor/github.com/prometheus/client_golang/api/prometheus/api_test.go
generated
vendored
Normal file
453
gateway/vendor/github.com/prometheus/client_golang/api/prometheus/api_test.go
generated
vendored
Normal file
@ -0,0 +1,453 @@
|
||||
// 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 prometheus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
c := Config{}
|
||||
if c.transport() != DefaultTransport {
|
||||
t.Fatalf("expected default transport for nil Transport field")
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
address string
|
||||
endpoint string
|
||||
args map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
address: "http://localhost:9090",
|
||||
endpoint: "/test",
|
||||
expected: "http://localhost:9090/test",
|
||||
},
|
||||
{
|
||||
address: "http://localhost",
|
||||
endpoint: "/test",
|
||||
expected: "http://localhost/test",
|
||||
},
|
||||
{
|
||||
address: "http://localhost:9090",
|
||||
endpoint: "test",
|
||||
expected: "http://localhost:9090/test",
|
||||
},
|
||||
{
|
||||
address: "http://localhost:9090/prefix",
|
||||
endpoint: "/test",
|
||||
expected: "http://localhost:9090/prefix/test",
|
||||
},
|
||||
{
|
||||
address: "https://localhost:9090/",
|
||||
endpoint: "/test/",
|
||||
expected: "https://localhost:9090/test",
|
||||
},
|
||||
{
|
||||
address: "http://localhost:9090",
|
||||
endpoint: "/test/:param",
|
||||
args: map[string]string{
|
||||
"param": "content",
|
||||
},
|
||||
expected: "http://localhost:9090/test/content",
|
||||
},
|
||||
{
|
||||
address: "http://localhost:9090",
|
||||
endpoint: "/test/:param/more/:param",
|
||||
args: map[string]string{
|
||||
"param": "content",
|
||||
},
|
||||
expected: "http://localhost:9090/test/content/more/content",
|
||||
},
|
||||
{
|
||||
address: "http://localhost:9090",
|
||||
endpoint: "/test/:param/more/:foo",
|
||||
args: map[string]string{
|
||||
"param": "content",
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: "http://localhost:9090/test/content/more/bar",
|
||||
},
|
||||
{
|
||||
address: "http://localhost:9090",
|
||||
endpoint: "/test/:param",
|
||||
args: map[string]string{
|
||||
"nonexistant": "content",
|
||||
},
|
||||
expected: "http://localhost:9090/test/:param",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
ep, err := url.Parse(test.address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hclient := &httpClient{
|
||||
endpoint: ep,
|
||||
transport: DefaultTransport,
|
||||
}
|
||||
|
||||
u := hclient.url(test.endpoint, test.args)
|
||||
if u.String() != test.expected {
|
||||
t.Errorf("unexpected result: got %s, want %s", u, test.expected)
|
||||
continue
|
||||
}
|
||||
|
||||
// The apiClient must return exactly the same result as the httpClient.
|
||||
aclient := &apiClient{hclient}
|
||||
|
||||
u = aclient.url(test.endpoint, test.args)
|
||||
if u.String() != test.expected {
|
||||
t.Errorf("unexpected result: got %s, want %s", u, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testClient struct {
|
||||
*testing.T
|
||||
|
||||
ch chan apiClientTest
|
||||
req *http.Request
|
||||
}
|
||||
|
||||
type apiClientTest struct {
|
||||
code int
|
||||
response interface{}
|
||||
expected string
|
||||
err *Error
|
||||
}
|
||||
|
||||
func (c *testClient) url(ep string, args map[string]string) *url.URL {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *testClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
|
||||
if ctx == nil {
|
||||
c.Fatalf("context was not passed down")
|
||||
}
|
||||
if req != c.req {
|
||||
c.Fatalf("request was not passed down")
|
||||
}
|
||||
|
||||
test := <-c.ch
|
||||
|
||||
var b []byte
|
||||
var err error
|
||||
|
||||
switch v := test.response.(type) {
|
||||
case string:
|
||||
b = []byte(v)
|
||||
default:
|
||||
b, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
resp := &http.Response{
|
||||
StatusCode: test.code,
|
||||
}
|
||||
|
||||
return resp, b, nil
|
||||
}
|
||||
|
||||
func TestAPIClientDo(t *testing.T) {
|
||||
tests := []apiClientTest{
|
||||
{
|
||||
response: &apiResponse{
|
||||
Status: "error",
|
||||
Data: json.RawMessage(`null`),
|
||||
ErrorType: ErrBadData,
|
||||
Error: "failed",
|
||||
},
|
||||
err: &Error{
|
||||
Type: ErrBadData,
|
||||
Msg: "failed",
|
||||
},
|
||||
code: statusAPIError,
|
||||
expected: `null`,
|
||||
},
|
||||
{
|
||||
response: &apiResponse{
|
||||
Status: "error",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
ErrorType: ErrTimeout,
|
||||
Error: "timed out",
|
||||
},
|
||||
err: &Error{
|
||||
Type: ErrTimeout,
|
||||
Msg: "timed out",
|
||||
},
|
||||
code: statusAPIError,
|
||||
expected: `test`,
|
||||
},
|
||||
{
|
||||
response: "bad json",
|
||||
err: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "bad response code 400",
|
||||
},
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
response: "bad json",
|
||||
err: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "invalid character 'b' looking for beginning of value",
|
||||
},
|
||||
code: statusAPIError,
|
||||
},
|
||||
{
|
||||
response: &apiResponse{
|
||||
Status: "success",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
},
|
||||
err: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
},
|
||||
code: statusAPIError,
|
||||
},
|
||||
{
|
||||
response: &apiResponse{
|
||||
Status: "success",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
ErrorType: ErrTimeout,
|
||||
Error: "timed out",
|
||||
},
|
||||
err: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
},
|
||||
code: statusAPIError,
|
||||
},
|
||||
{
|
||||
response: &apiResponse{
|
||||
Status: "error",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
ErrorType: ErrTimeout,
|
||||
Error: "timed out",
|
||||
},
|
||||
err: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
tc := &testClient{
|
||||
T: t,
|
||||
ch: make(chan apiClientTest, 1),
|
||||
req: &http.Request{},
|
||||
}
|
||||
client := &apiClient{tc}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
tc.ch <- test
|
||||
|
||||
_, body, err := client.do(context.Background(), tc.req)
|
||||
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("expected error %q but got none", test.err)
|
||||
continue
|
||||
}
|
||||
if test.err.Error() != err.Error() {
|
||||
t.Errorf("unexpected error: want %q, got %q", test.err, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpeceted error %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
want, got := test.expected, string(body)
|
||||
if want != got {
|
||||
t.Errorf("unexpected body: want %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type apiTestClient struct {
|
||||
*testing.T
|
||||
curTest apiTest
|
||||
}
|
||||
|
||||
type apiTest struct {
|
||||
do func() (interface{}, error)
|
||||
inErr error
|
||||
inRes interface{}
|
||||
|
||||
reqPath string
|
||||
reqParam url.Values
|
||||
reqMethod string
|
||||
res interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *apiTestClient) url(ep string, args map[string]string) *url.URL {
|
||||
u := &url.URL{
|
||||
Host: "test:9090",
|
||||
Path: apiPrefix + ep,
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func (c *apiTestClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
|
||||
|
||||
test := c.curTest
|
||||
|
||||
if req.URL.Path != test.reqPath {
|
||||
c.Errorf("unexpected request path: want %s, got %s", test.reqPath, req.URL.Path)
|
||||
}
|
||||
if req.Method != test.reqMethod {
|
||||
c.Errorf("unexpected request method: want %s, got %s", test.reqMethod, req.Method)
|
||||
}
|
||||
|
||||
b, err := json.Marshal(test.inRes)
|
||||
if err != nil {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
resp := &http.Response{}
|
||||
if test.inErr != nil {
|
||||
resp.StatusCode = statusAPIError
|
||||
} else {
|
||||
resp.StatusCode = http.StatusOK
|
||||
}
|
||||
|
||||
return resp, b, test.inErr
|
||||
}
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
|
||||
testTime := time.Now()
|
||||
|
||||
client := &apiTestClient{T: t}
|
||||
|
||||
queryApi := &httpQueryAPI{
|
||||
client: client,
|
||||
}
|
||||
|
||||
doQuery := func(q string, ts time.Time) func() (interface{}, error) {
|
||||
return func() (interface{}, error) {
|
||||
return queryApi.Query(context.Background(), q, ts)
|
||||
}
|
||||
}
|
||||
|
||||
doQueryRange := func(q string, rng Range) func() (interface{}, error) {
|
||||
return func() (interface{}, error) {
|
||||
return queryApi.QueryRange(context.Background(), q, rng)
|
||||
}
|
||||
}
|
||||
|
||||
queryTests := []apiTest{
|
||||
{
|
||||
do: doQuery("2", testTime),
|
||||
inRes: &queryResult{
|
||||
Type: model.ValScalar,
|
||||
Result: &model.Scalar{
|
||||
Value: 2,
|
||||
Timestamp: model.TimeFromUnix(testTime.Unix()),
|
||||
},
|
||||
},
|
||||
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/query",
|
||||
reqParam: url.Values{
|
||||
"query": []string{"2"},
|
||||
"time": []string{testTime.Format(time.RFC3339Nano)},
|
||||
},
|
||||
res: &model.Scalar{
|
||||
Value: 2,
|
||||
Timestamp: model.TimeFromUnix(testTime.Unix()),
|
||||
},
|
||||
},
|
||||
{
|
||||
do: doQuery("2", testTime),
|
||||
inErr: fmt.Errorf("some error"),
|
||||
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/query",
|
||||
reqParam: url.Values{
|
||||
"query": []string{"2"},
|
||||
"time": []string{testTime.Format(time.RFC3339Nano)},
|
||||
},
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
|
||||
{
|
||||
do: doQueryRange("2", Range{
|
||||
Start: testTime.Add(-time.Minute),
|
||||
End: testTime,
|
||||
Step: time.Minute,
|
||||
}),
|
||||
inErr: fmt.Errorf("some error"),
|
||||
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/query_range",
|
||||
reqParam: url.Values{
|
||||
"query": []string{"2"},
|
||||
"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
|
||||
"end": []string{testTime.Format(time.RFC3339Nano)},
|
||||
"step": []string{time.Minute.String()},
|
||||
},
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
}
|
||||
|
||||
var tests []apiTest
|
||||
tests = append(tests, queryTests...)
|
||||
|
||||
for _, test := range tests {
|
||||
client.curTest = test
|
||||
|
||||
res, err := test.do()
|
||||
|
||||
if test.err != nil {
|
||||
if err == nil {
|
||||
t.Errorf("expected error %q but got none", test.err)
|
||||
continue
|
||||
}
|
||||
if err.Error() != test.err.Error() {
|
||||
t.Errorf("unexpected error: want %s, got %s", test.err, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(res, test.res) {
|
||||
t.Errorf("unexpected result: want %v, got %v", test.res, res)
|
||||
}
|
||||
}
|
||||
}
|
103
gateway/vendor/github.com/prometheus/client_golang/examples/random/main.go
generated
vendored
Normal file
103
gateway/vendor/github.com/prometheus/client_golang/examples/random/main.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
// 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.
|
||||
|
||||
// A simple example exposing fictional RPC latencies with different types of
|
||||
// random distributions (uniform, normal, and exponential) as Prometheus
|
||||
// metrics.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var (
|
||||
addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
|
||||
uniformDomain = flag.Float64("uniform.domain", 200, "The domain for the uniform distribution.")
|
||||
normDomain = flag.Float64("normal.domain", 200, "The domain for the normal distribution.")
|
||||
normMean = flag.Float64("normal.mean", 10, "The mean for the normal distribution.")
|
||||
oscillationPeriod = flag.Duration("oscillation-period", 10*time.Minute, "The duration of the rate oscillation period.")
|
||||
)
|
||||
|
||||
var (
|
||||
// Create a summary to track fictional interservice RPC latencies for three
|
||||
// distinct services with different latency distributions. These services are
|
||||
// differentiated via a "service" label.
|
||||
rpcDurations = prometheus.NewSummaryVec(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "rpc_durations_microseconds",
|
||||
Help: "RPC latency distributions.",
|
||||
},
|
||||
[]string{"service"},
|
||||
)
|
||||
// The same as above, but now as a histogram, and only for the normal
|
||||
// distribution. The buckets are targeted to the parameters of the
|
||||
// normal distribution, with 20 buckets centered on the mean, each
|
||||
// half-sigma wide.
|
||||
rpcDurationsHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Name: "rpc_durations_histogram_microseconds",
|
||||
Help: "RPC latency distributions.",
|
||||
Buckets: prometheus.LinearBuckets(*normMean-5**normDomain, .5**normDomain, 20),
|
||||
})
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register the summary and the histogram with Prometheus's default registry.
|
||||
prometheus.MustRegister(rpcDurations)
|
||||
prometheus.MustRegister(rpcDurationsHistogram)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
oscillationFactor := func() float64 {
|
||||
return 2 + math.Sin(math.Sin(2*math.Pi*float64(time.Since(start))/float64(*oscillationPeriod)))
|
||||
}
|
||||
|
||||
// Periodically record some sample latencies for the three services.
|
||||
go func() {
|
||||
for {
|
||||
v := rand.Float64() * *uniformDomain
|
||||
rpcDurations.WithLabelValues("uniform").Observe(v)
|
||||
time.Sleep(time.Duration(100*oscillationFactor()) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
v := (rand.NormFloat64() * *normDomain) + *normMean
|
||||
rpcDurations.WithLabelValues("normal").Observe(v)
|
||||
rpcDurationsHistogram.Observe(v)
|
||||
time.Sleep(time.Duration(75*oscillationFactor()) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
v := rand.ExpFloat64()
|
||||
rpcDurations.WithLabelValues("exponential").Observe(v)
|
||||
time.Sleep(time.Duration(50*oscillationFactor()) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// Expose the registered metrics via HTTP.
|
||||
http.Handle("/metrics", prometheus.Handler())
|
||||
http.ListenAndServe(*addr, nil)
|
||||
}
|
30
gateway/vendor/github.com/prometheus/client_golang/examples/simple/main.go
generated
vendored
Normal file
30
gateway/vendor/github.com/prometheus/client_golang/examples/simple/main.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
// A minimal example of how to include Prometheus instrumentation.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
var addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
http.Handle("/metrics", prometheus.Handler())
|
||||
http.ListenAndServe(*addr, nil)
|
||||
}
|
1
gateway/vendor/github.com/prometheus/client_golang/prometheus/.gitignore
generated
vendored
Normal file
1
gateway/vendor/github.com/prometheus/client_golang/prometheus/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
command-line-arguments.test
|
183
gateway/vendor/github.com/prometheus/client_golang/prometheus/benchmark_test.go
generated
vendored
Normal file
183
gateway/vendor/github.com/prometheus/client_golang/prometheus/benchmark_test.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkCounterWithLabelValues(b *testing.B) {
|
||||
m := NewCounterVec(
|
||||
CounterOpts{
|
||||
Name: "benchmark_counter",
|
||||
Help: "A counter to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.WithLabelValues("eins", "zwei", "drei").Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCounterWithLabelValuesConcurrent(b *testing.B) {
|
||||
m := NewCounterVec(
|
||||
CounterOpts{
|
||||
Name: "benchmark_counter",
|
||||
Help: "A counter to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for j := 0; j < b.N/10; j++ {
|
||||
m.WithLabelValues("eins", "zwei", "drei").Inc()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkCounterWithMappedLabels(b *testing.B) {
|
||||
m := NewCounterVec(
|
||||
CounterOpts{
|
||||
Name: "benchmark_counter",
|
||||
Help: "A counter to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.With(Labels{"two": "zwei", "one": "eins", "three": "drei"}).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCounterWithPreparedMappedLabels(b *testing.B) {
|
||||
m := NewCounterVec(
|
||||
CounterOpts{
|
||||
Name: "benchmark_counter",
|
||||
Help: "A counter to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
labels := Labels{"two": "zwei", "one": "eins", "three": "drei"}
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.With(labels).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCounterNoLabels(b *testing.B) {
|
||||
m := NewCounter(CounterOpts{
|
||||
Name: "benchmark_counter",
|
||||
Help: "A counter to benchmark it.",
|
||||
})
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGaugeWithLabelValues(b *testing.B) {
|
||||
m := NewGaugeVec(
|
||||
GaugeOpts{
|
||||
Name: "benchmark_gauge",
|
||||
Help: "A gauge to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.WithLabelValues("eins", "zwei", "drei").Set(3.1415)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGaugeNoLabels(b *testing.B) {
|
||||
m := NewGauge(GaugeOpts{
|
||||
Name: "benchmark_gauge",
|
||||
Help: "A gauge to benchmark it.",
|
||||
})
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Set(3.1415)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSummaryWithLabelValues(b *testing.B) {
|
||||
m := NewSummaryVec(
|
||||
SummaryOpts{
|
||||
Name: "benchmark_summary",
|
||||
Help: "A summary to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.WithLabelValues("eins", "zwei", "drei").Observe(3.1415)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSummaryNoLabels(b *testing.B) {
|
||||
m := NewSummary(SummaryOpts{
|
||||
Name: "benchmark_summary",
|
||||
Help: "A summary to benchmark it.",
|
||||
},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Observe(3.1415)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHistogramWithLabelValues(b *testing.B) {
|
||||
m := NewHistogramVec(
|
||||
HistogramOpts{
|
||||
Name: "benchmark_histogram",
|
||||
Help: "A histogram to benchmark it.",
|
||||
},
|
||||
[]string{"one", "two", "three"},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.WithLabelValues("eins", "zwei", "drei").Observe(3.1415)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHistogramNoLabels(b *testing.B) {
|
||||
m := NewHistogram(HistogramOpts{
|
||||
Name: "benchmark_histogram",
|
||||
Help: "A histogram to benchmark it.",
|
||||
},
|
||||
)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
m.Observe(3.1415)
|
||||
}
|
||||
}
|
68
gateway/vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
68
gateway/vendor/github.com/prometheus/client_golang/prometheus/counter.go
generated
vendored
@ -30,8 +30,16 @@ type Counter interface {
|
||||
Metric
|
||||
Collector
|
||||
|
||||
// Inc increments the counter by 1. Use Add to increment it by arbitrary
|
||||
// non-negative values.
|
||||
// Set is used to set the Counter to an arbitrary value. It is only used
|
||||
// if you have to transfer a value from an external counter into this
|
||||
// Prometheus metric. Do not use it for regular handling of a
|
||||
// Prometheus counter (as it can be used to break the contract of
|
||||
// monotonically increasing values).
|
||||
//
|
||||
// Deprecated: Use NewConstMetric to create a counter for an external
|
||||
// value. A Counter should never be set.
|
||||
Set(float64)
|
||||
// Inc increments the counter by 1.
|
||||
Inc()
|
||||
// Add adds the given value to the counter. It panics if the value is <
|
||||
// 0.
|
||||
@ -74,11 +82,12 @@ func (c *counter) Add(v float64) {
|
||||
// CounterVec embeds MetricVec. See there for a full list of methods with
|
||||
// detailed documentation.
|
||||
type CounterVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
||||
// partitioned by the given label names.
|
||||
// partitioned by the given label names. At least one label name must be
|
||||
// provided.
|
||||
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
desc := NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
@ -87,7 +96,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &CounterVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
result := &counter{value: value{
|
||||
desc: desc,
|
||||
valType: CounterValue,
|
||||
@ -99,51 +108,22 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Counter for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Counter is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Counter to only
|
||||
// create the new Counter but leave it at its starting value 0. See also the
|
||||
// SummaryVec example.
|
||||
//
|
||||
// Keeping the Counter for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Counter from the CounterVec. In that case,
|
||||
// the Counter will still exist, but it will not be exported anymore, even if a
|
||||
// Counter with the same label values is created later.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
// GetMetricWithLabelValues replaces the method of the same name in
|
||||
// MetricVec. The difference is that this method returns a Counter and not a
|
||||
// Metric so that no type conversion is required.
|
||||
func (m *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
||||
metric, err := m.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Counter), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Counter for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new Counter is created. Implications of
|
||||
// creating a Counter without using it and keeping the Counter for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc.
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
||||
// difference is that this method returns a Counter and not a Metric so that no
|
||||
// type conversion is required.
|
||||
func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||
metric, err := m.metricVec.getMetricWith(labels)
|
||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Counter), err
|
||||
}
|
||||
@ -155,14 +135,14 @@ func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||
// error, WithLabelValues allows shortcuts like
|
||||
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||
func (m *CounterVec) WithLabelValues(lvs ...string) Counter {
|
||||
return m.metricVec.withLabelValues(lvs...).(Counter)
|
||||
return m.MetricVec.WithLabelValues(lvs...).(Counter)
|
||||
}
|
||||
|
||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||
// returned an error. By not returning an error, With allows shortcuts like
|
||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
||||
func (m *CounterVec) With(labels Labels) Counter {
|
||||
return m.metricVec.with(labels).(Counter)
|
||||
return m.MetricVec.With(labels).(Counter)
|
||||
}
|
||||
|
||||
// CounterFunc is a Counter whose value is determined at collect time by calling a
|
||||
|
58
gateway/vendor/github.com/prometheus/client_golang/prometheus/counter_test.go
generated
vendored
Normal file
58
gateway/vendor/github.com/prometheus/client_golang/prometheus/counter_test.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
func TestCounterAdd(t *testing.T) {
|
||||
counter := NewCounter(CounterOpts{
|
||||
Name: "test",
|
||||
Help: "test help",
|
||||
ConstLabels: Labels{"a": "1", "b": "2"},
|
||||
}).(*counter)
|
||||
counter.Inc()
|
||||
if expected, got := 1., math.Float64frombits(counter.valBits); expected != got {
|
||||
t.Errorf("Expected %f, got %f.", expected, got)
|
||||
}
|
||||
counter.Add(42)
|
||||
if expected, got := 43., math.Float64frombits(counter.valBits); expected != got {
|
||||
t.Errorf("Expected %f, got %f.", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := "counter cannot decrease in value", decreaseCounter(counter).Error(); expected != got {
|
||||
t.Errorf("Expected error %q, got %q.", expected, got)
|
||||
}
|
||||
|
||||
m := &dto.Metric{}
|
||||
counter.Write(m)
|
||||
|
||||
if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > counter:<value:43 > `, m.String(); expected != got {
|
||||
t.Errorf("expected %q, got %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func decreaseCounter(c *counter) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = e.(error)
|
||||
}
|
||||
}()
|
||||
c.Add(-1)
|
||||
return nil
|
||||
}
|
13
gateway/vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
13
gateway/vendor/github.com/prometheus/client_golang/prometheus/desc.go
generated
vendored
@ -16,15 +16,20 @@ package prometheus
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
var (
|
||||
metricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
|
||||
labelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
|
||||
)
|
||||
|
||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||
// label names.
|
||||
const reservedLabelPrefix = "__"
|
||||
@ -73,7 +78,7 @@ type Desc struct {
|
||||
// Help string. Each Desc with the same fqName must have the same
|
||||
// dimHash.
|
||||
dimHash uint64
|
||||
// err is an error that occurred during construction. It is reported on
|
||||
// err is an error that occured during construction. It is reported on
|
||||
// registration time.
|
||||
err error
|
||||
}
|
||||
@ -98,7 +103,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
|
||||
d.err = errors.New("empty help string")
|
||||
return d
|
||||
}
|
||||
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||
if !metricNameRE.MatchString(fqName) {
|
||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||
return d
|
||||
}
|
||||
@ -195,6 +200,6 @@ func (d *Desc) String() string {
|
||||
}
|
||||
|
||||
func checkLabelName(l string) bool {
|
||||
return model.LabelName(l).IsValid() &&
|
||||
return labelNameRE.MatchString(l) &&
|
||||
!strings.HasPrefix(l, reservedLabelPrefix)
|
||||
}
|
||||
|
65
gateway/vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
65
gateway/vendor/github.com/prometheus/client_golang/prometheus/doc.go
generated
vendored
@ -17,7 +17,7 @@
|
||||
// Pushgateway (package push).
|
||||
//
|
||||
// All exported functions and methods are safe to be used concurrently unless
|
||||
// specified otherwise.
|
||||
//specified otherwise.
|
||||
//
|
||||
// A Basic Example
|
||||
//
|
||||
@ -26,7 +26,6 @@
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "log"
|
||||
// "net/http"
|
||||
//
|
||||
// "github.com/prometheus/client_golang/prometheus"
|
||||
@ -60,7 +59,7 @@
|
||||
// // The Handler function provides a default handler to expose metrics
|
||||
// // via an HTTP server. "/metrics" is the usual endpoint for that.
|
||||
// http.Handle("/metrics", promhttp.Handler())
|
||||
// log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
// http.ListenAndServe(":8080", nil)
|
||||
// }
|
||||
//
|
||||
//
|
||||
@ -70,7 +69,7 @@
|
||||
// Metrics
|
||||
//
|
||||
// The number of exported identifiers in this package might appear a bit
|
||||
// overwhelming. However, in addition to the basic plumbing shown in the example
|
||||
// overwhelming. Hovever, in addition to the basic plumbing shown in the example
|
||||
// above, you only need to understand the different metric types and their
|
||||
// vector versions for basic usage.
|
||||
//
|
||||
@ -96,8 +95,8 @@
|
||||
// SummaryVec, HistogramVec, and UntypedVec are not.
|
||||
//
|
||||
// To create instances of Metrics and their vector versions, you need a suitable
|
||||
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts, HistogramOpts, or
|
||||
// UntypedOpts.
|
||||
// …Opts struct, i.e. GaugeOpts, CounterOpts, SummaryOpts,
|
||||
// HistogramOpts, or UntypedOpts.
|
||||
//
|
||||
// Custom Collectors and constant Metrics
|
||||
//
|
||||
@ -115,8 +114,8 @@
|
||||
// Metric instances “on the fly” using NewConstMetric, NewConstHistogram, and
|
||||
// NewConstSummary (and their respective Must… versions). That will happen in
|
||||
// the Collect method. The Describe method has to return separate Desc
|
||||
// instances, representative of the “throw-away” metrics to be created later.
|
||||
// NewDesc comes in handy to create those Desc instances.
|
||||
// instances, representative of the “throw-away” metrics to be created
|
||||
// later. NewDesc comes in handy to create those Desc instances.
|
||||
//
|
||||
// The Collector example illustrates the use case. You can also look at the
|
||||
// source code of the processCollector (mirroring process metrics), the
|
||||
@ -130,32 +129,32 @@
|
||||
// Advanced Uses of the Registry
|
||||
//
|
||||
// While MustRegister is the by far most common way of registering a Collector,
|
||||
// sometimes you might want to handle the errors the registration might cause.
|
||||
// As suggested by the name, MustRegister panics if an error occurs. With the
|
||||
// Register function, the error is returned and can be handled.
|
||||
// sometimes you might want to handle the errors the registration might
|
||||
// cause. As suggested by the name, MustRegister panics if an error occurs. With
|
||||
// the Register function, the error is returned and can be handled.
|
||||
//
|
||||
// An error is returned if the registered Collector is incompatible or
|
||||
// inconsistent with already registered metrics. The registry aims for
|
||||
// consistency of the collected metrics according to the Prometheus data model.
|
||||
// Inconsistencies are ideally detected at registration time, not at collect
|
||||
// time. The former will usually be detected at start-up time of a program,
|
||||
// while the latter will only happen at scrape time, possibly not even on the
|
||||
// first scrape if the inconsistency only becomes relevant later. That is the
|
||||
// main reason why a Collector and a Metric have to describe themselves to the
|
||||
// registry.
|
||||
// consistency of the collected metrics according to the Prometheus data
|
||||
// model. Inconsistencies are ideally detected at registration time, not at
|
||||
// collect time. The former will usually be detected at start-up time of a
|
||||
// program, while the latter will only happen at scrape time, possibly not even
|
||||
// on the first scrape if the inconsistency only becomes relevant later. That is
|
||||
// the main reason why a Collector and a Metric have to describe themselves to
|
||||
// the registry.
|
||||
//
|
||||
// So far, everything we did operated on the so-called default registry, as it
|
||||
// can be found in the global DefaultRegistry variable. With NewRegistry, you
|
||||
// can create a custom registry, or you can even implement the Registerer or
|
||||
// Gatherer interfaces yourself. The methods Register and Unregister work in the
|
||||
// same way on a custom registry as the global functions Register and Unregister
|
||||
// on the default registry.
|
||||
// Gatherer interfaces yourself. The methods Register and Unregister work in
|
||||
// the same way on a custom registry as the global functions Register and
|
||||
// Unregister on the default registry.
|
||||
//
|
||||
// There are a number of uses for custom registries: You can use registries with
|
||||
// special properties, see NewPedanticRegistry. You can avoid global state, as
|
||||
// it is imposed by the DefaultRegistry. You can use multiple registries at the
|
||||
// same time to expose different metrics in different ways. You can use separate
|
||||
// registries for testing purposes.
|
||||
// There are a number of uses for custom registries: You can use registries
|
||||
// with special properties, see NewPedanticRegistry. You can avoid global state,
|
||||
// as it is imposed by the DefaultRegistry. You can use multiple registries at
|
||||
// the same time to expose different metrics in different ways. You can use
|
||||
// separate registries for testing purposes.
|
||||
//
|
||||
// Also note that the DefaultRegistry comes registered with a Collector for Go
|
||||
// runtime metrics (via NewGoCollector) and a Collector for process metrics (via
|
||||
@ -167,20 +166,16 @@
|
||||
// The Registry implements the Gatherer interface. The caller of the Gather
|
||||
// method can then expose the gathered metrics in some way. Usually, the metrics
|
||||
// are served via HTTP on the /metrics endpoint. That's happening in the example
|
||||
// above. The tools to expose metrics via HTTP are in the promhttp sub-package.
|
||||
// (The top-level functions in the prometheus package are deprecated.)
|
||||
// above. The tools to expose metrics via HTTP are in the promhttp
|
||||
// sub-package. (The top-level functions in the prometheus package are
|
||||
// deprecated.)
|
||||
//
|
||||
// Pushing to the Pushgateway
|
||||
//
|
||||
// Function for pushing to the Pushgateway can be found in the push sub-package.
|
||||
//
|
||||
// Graphite Bridge
|
||||
//
|
||||
// Functions and examples to push metrics from a Gatherer to Graphite can be
|
||||
// found in the graphite sub-package.
|
||||
//
|
||||
// Other Means of Exposition
|
||||
//
|
||||
// More ways of exposing metrics can easily be added by following the approaches
|
||||
// of the existing implementations.
|
||||
// More ways of exposing metrics can easily be added. Sending metrics to
|
||||
// Graphite would be an example that will soon be implemented.
|
||||
package prometheus
|
||||
|
118
gateway/vendor/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
generated
vendored
Normal file
118
gateway/vendor/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus_test
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
// ClusterManager is an example for a system that might have been built without
|
||||
// Prometheus in mind. It models a central manager of jobs running in a
|
||||
// cluster. To turn it into something that collects Prometheus metrics, we
|
||||
// simply add the two methods required for the Collector interface.
|
||||
//
|
||||
// An additional challenge is that multiple instances of the ClusterManager are
|
||||
// run within the same binary, each in charge of a different zone. We need to
|
||||
// make use of ConstLabels to be able to register each ClusterManager instance
|
||||
// with Prometheus.
|
||||
type ClusterManager struct {
|
||||
Zone string
|
||||
OOMCountDesc *prometheus.Desc
|
||||
RAMUsageDesc *prometheus.Desc
|
||||
// ... many more fields
|
||||
}
|
||||
|
||||
// ReallyExpensiveAssessmentOfTheSystemState is a mock for the data gathering a
|
||||
// real cluster manager would have to do. Since it may actually be really
|
||||
// expensive, it must only be called once per collection. This implementation,
|
||||
// obviously, only returns some made-up data.
|
||||
func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() (
|
||||
oomCountByHost map[string]int, ramUsageByHost map[string]float64,
|
||||
) {
|
||||
// Just example fake data.
|
||||
oomCountByHost = map[string]int{
|
||||
"foo.example.org": 42,
|
||||
"bar.example.org": 2001,
|
||||
}
|
||||
ramUsageByHost = map[string]float64{
|
||||
"foo.example.org": 6.023e23,
|
||||
"bar.example.org": 3.14,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Describe simply sends the two Descs in the struct to the channel.
|
||||
func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- c.OOMCountDesc
|
||||
ch <- c.RAMUsageDesc
|
||||
}
|
||||
|
||||
// Collect first triggers the ReallyExpensiveAssessmentOfTheSystemState. Then it
|
||||
// creates constant metrics for each host on the fly based on the returned data.
|
||||
//
|
||||
// Note that Collect could be called concurrently, so we depend on
|
||||
// ReallyExpensiveAssessmentOfTheSystemState to be concurrency-safe.
|
||||
func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
|
||||
oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState()
|
||||
for host, oomCount := range oomCountByHost {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.OOMCountDesc,
|
||||
prometheus.CounterValue,
|
||||
float64(oomCount),
|
||||
host,
|
||||
)
|
||||
}
|
||||
for host, ramUsage := range ramUsageByHost {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.RAMUsageDesc,
|
||||
prometheus.GaugeValue,
|
||||
ramUsage,
|
||||
host,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NewClusterManager creates the two Descs OOMCountDesc and RAMUsageDesc. Note
|
||||
// that the zone is set as a ConstLabel. (It's different in each instance of the
|
||||
// ClusterManager, but constant over the lifetime of an instance.) Then there is
|
||||
// a variable label "host", since we want to partition the collected metrics by
|
||||
// host. Since all Descs created in this way are consistent across instances,
|
||||
// with a guaranteed distinction by the "zone" label, we can register different
|
||||
// ClusterManager instances with the same registry.
|
||||
func NewClusterManager(zone string) *ClusterManager {
|
||||
return &ClusterManager{
|
||||
Zone: zone,
|
||||
OOMCountDesc: prometheus.NewDesc(
|
||||
"clustermanager_oom_crashes_total",
|
||||
"Number of OOM crashes.",
|
||||
[]string{"host"},
|
||||
prometheus.Labels{"zone": zone},
|
||||
),
|
||||
RAMUsageDesc: prometheus.NewDesc(
|
||||
"clustermanager_ram_usage_bytes",
|
||||
"RAM usage as reported to the cluster manager.",
|
||||
[]string{"host"},
|
||||
prometheus.Labels{"zone": zone},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleCollector() {
|
||||
workerDB := NewClusterManager("db")
|
||||
workerCA := NewClusterManager("ca")
|
||||
|
||||
// Since we are dealing with custom Collector implementations, it might
|
||||
// be a good idea to try it out with a pedantic registry.
|
||||
reg := prometheus.NewPedanticRegistry()
|
||||
reg.MustRegister(workerDB)
|
||||
reg.MustRegister(workerCA)
|
||||
}
|
751
gateway/vendor/github.com/prometheus/client_golang/prometheus/examples_test.go
generated
vendored
Normal file
751
gateway/vendor/github.com/prometheus/client_golang/prometheus/examples_test.go
generated
vendored
Normal file
@ -0,0 +1,751 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func ExampleGauge() {
|
||||
opsQueued := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Namespace: "our_company",
|
||||
Subsystem: "blob_storage",
|
||||
Name: "ops_queued",
|
||||
Help: "Number of blob storage operations waiting to be processed.",
|
||||
})
|
||||
prometheus.MustRegister(opsQueued)
|
||||
|
||||
// 10 operations queued by the goroutine managing incoming requests.
|
||||
opsQueued.Add(10)
|
||||
// A worker goroutine has picked up a waiting operation.
|
||||
opsQueued.Dec()
|
||||
// And once more...
|
||||
opsQueued.Dec()
|
||||
}
|
||||
|
||||
func ExampleGaugeVec() {
|
||||
opsQueued := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "our_company",
|
||||
Subsystem: "blob_storage",
|
||||
Name: "ops_queued",
|
||||
Help: "Number of blob storage operations waiting to be processed, partitioned by user and type.",
|
||||
},
|
||||
[]string{
|
||||
// Which user has requested the operation?
|
||||
"user",
|
||||
// Of what type is the operation?
|
||||
"type",
|
||||
},
|
||||
)
|
||||
prometheus.MustRegister(opsQueued)
|
||||
|
||||
// Increase a value using compact (but order-sensitive!) WithLabelValues().
|
||||
opsQueued.WithLabelValues("bob", "put").Add(4)
|
||||
// Increase a value with a map using WithLabels. More verbose, but order
|
||||
// doesn't matter anymore.
|
||||
opsQueued.With(prometheus.Labels{"type": "delete", "user": "alice"}).Inc()
|
||||
}
|
||||
|
||||
func ExampleGaugeFunc() {
|
||||
if err := prometheus.Register(prometheus.NewGaugeFunc(
|
||||
prometheus.GaugeOpts{
|
||||
Subsystem: "runtime",
|
||||
Name: "goroutines_count",
|
||||
Help: "Number of goroutines that currently exist.",
|
||||
},
|
||||
func() float64 { return float64(runtime.NumGoroutine()) },
|
||||
)); err == nil {
|
||||
fmt.Println("GaugeFunc 'goroutines_count' registered.")
|
||||
}
|
||||
// Note that the count of goroutines is a gauge (and not a counter) as
|
||||
// it can go up and down.
|
||||
|
||||
// Output:
|
||||
// GaugeFunc 'goroutines_count' registered.
|
||||
}
|
||||
|
||||
func ExampleCounter() {
|
||||
pushCounter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "repository_pushes", // Note: No help string...
|
||||
})
|
||||
err := prometheus.Register(pushCounter) // ... so this will return an error.
|
||||
if err != nil {
|
||||
fmt.Println("Push counter couldn't be registered, no counting will happen:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Try it once more, this time with a help string.
|
||||
pushCounter = prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "repository_pushes",
|
||||
Help: "Number of pushes to external repository.",
|
||||
})
|
||||
err = prometheus.Register(pushCounter)
|
||||
if err != nil {
|
||||
fmt.Println("Push counter couldn't be registered AGAIN, no counting will happen:", err)
|
||||
return
|
||||
}
|
||||
|
||||
pushComplete := make(chan struct{})
|
||||
// TODO: Start a goroutine that performs repository pushes and reports
|
||||
// each completion via the channel.
|
||||
for _ = range pushComplete {
|
||||
pushCounter.Inc()
|
||||
}
|
||||
// Output:
|
||||
// Push counter couldn't be registered, no counting will happen: descriptor Desc{fqName: "repository_pushes", help: "", constLabels: {}, variableLabels: []} is invalid: empty help string
|
||||
}
|
||||
|
||||
func ExampleCounterVec() {
|
||||
httpReqs := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "http_requests_total",
|
||||
Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
|
||||
},
|
||||
[]string{"code", "method"},
|
||||
)
|
||||
prometheus.MustRegister(httpReqs)
|
||||
|
||||
httpReqs.WithLabelValues("404", "POST").Add(42)
|
||||
|
||||
// If you have to access the same set of labels very frequently, it
|
||||
// might be good to retrieve the metric only once and keep a handle to
|
||||
// it. But beware of deletion of that metric, see below!
|
||||
m := httpReqs.WithLabelValues("200", "GET")
|
||||
for i := 0; i < 1000000; i++ {
|
||||
m.Inc()
|
||||
}
|
||||
// Delete a metric from the vector. If you have previously kept a handle
|
||||
// to that metric (as above), future updates via that handle will go
|
||||
// unseen (even if you re-create a metric with the same label set
|
||||
// later).
|
||||
httpReqs.DeleteLabelValues("200", "GET")
|
||||
// Same thing with the more verbose Labels syntax.
|
||||
httpReqs.Delete(prometheus.Labels{"method": "GET", "code": "200"})
|
||||
}
|
||||
|
||||
func ExampleInstrumentHandler() {
|
||||
// Handle the "/doc" endpoint with the standard http.FileServer handler.
|
||||
// By wrapping the handler with InstrumentHandler, request count,
|
||||
// request and response sizes, and request latency are automatically
|
||||
// exported to Prometheus, partitioned by HTTP status code and method
|
||||
// and by the handler name (here "fileserver").
|
||||
http.Handle("/doc", prometheus.InstrumentHandler(
|
||||
"fileserver", http.FileServer(http.Dir("/usr/share/doc")),
|
||||
))
|
||||
// The Prometheus handler still has to be registered to handle the
|
||||
// "/metrics" endpoint. The handler returned by prometheus.Handler() is
|
||||
// already instrumented - with "prometheus" as the handler name. In this
|
||||
// example, we want the handler name to be "metrics", so we instrument
|
||||
// the uninstrumented Prometheus handler ourselves.
|
||||
http.Handle("/metrics", prometheus.InstrumentHandler(
|
||||
"metrics", prometheus.UninstrumentedHandler(),
|
||||
))
|
||||
}
|
||||
|
||||
func ExampleLabelPairSorter() {
|
||||
labelPairs := []*dto.LabelPair{
|
||||
&dto.LabelPair{Name: proto.String("status"), Value: proto.String("404")},
|
||||
&dto.LabelPair{Name: proto.String("method"), Value: proto.String("get")},
|
||||
}
|
||||
|
||||
sort.Sort(prometheus.LabelPairSorter(labelPairs))
|
||||
|
||||
fmt.Println(labelPairs)
|
||||
// Output:
|
||||
// [name:"method" value:"get" name:"status" value:"404" ]
|
||||
}
|
||||
|
||||
func ExampleRegister() {
|
||||
// Imagine you have a worker pool and want to count the tasks completed.
|
||||
taskCounter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Subsystem: "worker_pool",
|
||||
Name: "completed_tasks_total",
|
||||
Help: "Total number of tasks completed.",
|
||||
})
|
||||
// This will register fine.
|
||||
if err := prometheus.Register(taskCounter); err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("taskCounter registered.")
|
||||
}
|
||||
// Don't forget to tell the HTTP server about the Prometheus handler.
|
||||
// (In a real program, you still need to start the HTTP server...)
|
||||
http.Handle("/metrics", prometheus.Handler())
|
||||
|
||||
// Now you can start workers and give every one of them a pointer to
|
||||
// taskCounter and let it increment it whenever it completes a task.
|
||||
taskCounter.Inc() // This has to happen somewhere in the worker code.
|
||||
|
||||
// But wait, you want to see how individual workers perform. So you need
|
||||
// a vector of counters, with one element for each worker.
|
||||
taskCounterVec := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Subsystem: "worker_pool",
|
||||
Name: "completed_tasks_total",
|
||||
Help: "Total number of tasks completed.",
|
||||
},
|
||||
[]string{"worker_id"},
|
||||
)
|
||||
|
||||
// Registering will fail because we already have a metric of that name.
|
||||
if err := prometheus.Register(taskCounterVec); err != nil {
|
||||
fmt.Println("taskCounterVec not registered:", err)
|
||||
} else {
|
||||
fmt.Println("taskCounterVec registered.")
|
||||
}
|
||||
|
||||
// To fix, first unregister the old taskCounter.
|
||||
if prometheus.Unregister(taskCounter) {
|
||||
fmt.Println("taskCounter unregistered.")
|
||||
}
|
||||
|
||||
// Try registering taskCounterVec again.
|
||||
if err := prometheus.Register(taskCounterVec); err != nil {
|
||||
fmt.Println("taskCounterVec not registered:", err)
|
||||
} else {
|
||||
fmt.Println("taskCounterVec registered.")
|
||||
}
|
||||
// Bummer! Still doesn't work.
|
||||
|
||||
// Prometheus will not allow you to ever export metrics with
|
||||
// inconsistent help strings or label names. After unregistering, the
|
||||
// unregistered metrics will cease to show up in the /metrics HTTP
|
||||
// response, but the registry still remembers that those metrics had
|
||||
// been exported before. For this example, we will now choose a
|
||||
// different name. (In a real program, you would obviously not export
|
||||
// the obsolete metric in the first place.)
|
||||
taskCounterVec = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Subsystem: "worker_pool",
|
||||
Name: "completed_tasks_by_id",
|
||||
Help: "Total number of tasks completed.",
|
||||
},
|
||||
[]string{"worker_id"},
|
||||
)
|
||||
if err := prometheus.Register(taskCounterVec); err != nil {
|
||||
fmt.Println("taskCounterVec not registered:", err)
|
||||
} else {
|
||||
fmt.Println("taskCounterVec registered.")
|
||||
}
|
||||
// Finally it worked!
|
||||
|
||||
// The workers have to tell taskCounterVec their id to increment the
|
||||
// right element in the metric vector.
|
||||
taskCounterVec.WithLabelValues("42").Inc() // Code from worker 42.
|
||||
|
||||
// Each worker could also keep a reference to their own counter element
|
||||
// around. Pick the counter at initialization time of the worker.
|
||||
myCounter := taskCounterVec.WithLabelValues("42") // From worker 42 initialization code.
|
||||
myCounter.Inc() // Somewhere in the code of that worker.
|
||||
|
||||
// Note that something like WithLabelValues("42", "spurious arg") would
|
||||
// panic (because you have provided too many label values). If you want
|
||||
// to get an error instead, use GetMetricWithLabelValues(...) instead.
|
||||
notMyCounter, err := taskCounterVec.GetMetricWithLabelValues("42", "spurious arg")
|
||||
if err != nil {
|
||||
fmt.Println("Worker initialization failed:", err)
|
||||
}
|
||||
if notMyCounter == nil {
|
||||
fmt.Println("notMyCounter is nil.")
|
||||
}
|
||||
|
||||
// A different (and somewhat tricky) approach is to use
|
||||
// ConstLabels. ConstLabels are pairs of label names and label values
|
||||
// that never change. You might ask what those labels are good for (and
|
||||
// rightfully so - if they never change, they could as well be part of
|
||||
// the metric name). There are essentially two use-cases: The first is
|
||||
// if labels are constant throughout the lifetime of a binary execution,
|
||||
// but they vary over time or between different instances of a running
|
||||
// binary. The second is what we have here: Each worker creates and
|
||||
// registers an own Counter instance where the only difference is in the
|
||||
// value of the ConstLabels. Those Counters can all be registered
|
||||
// because the different ConstLabel values guarantee that each worker
|
||||
// will increment a different Counter metric.
|
||||
counterOpts := prometheus.CounterOpts{
|
||||
Subsystem: "worker_pool",
|
||||
Name: "completed_tasks",
|
||||
Help: "Total number of tasks completed.",
|
||||
ConstLabels: prometheus.Labels{"worker_id": "42"},
|
||||
}
|
||||
taskCounterForWorker42 := prometheus.NewCounter(counterOpts)
|
||||
if err := prometheus.Register(taskCounterForWorker42); err != nil {
|
||||
fmt.Println("taskCounterVForWorker42 not registered:", err)
|
||||
} else {
|
||||
fmt.Println("taskCounterForWorker42 registered.")
|
||||
}
|
||||
// Obviously, in real code, taskCounterForWorker42 would be a member
|
||||
// variable of a worker struct, and the "42" would be retrieved with a
|
||||
// GetId() method or something. The Counter would be created and
|
||||
// registered in the initialization code of the worker.
|
||||
|
||||
// For the creation of the next Counter, we can recycle
|
||||
// counterOpts. Just change the ConstLabels.
|
||||
counterOpts.ConstLabels = prometheus.Labels{"worker_id": "2001"}
|
||||
taskCounterForWorker2001 := prometheus.NewCounter(counterOpts)
|
||||
if err := prometheus.Register(taskCounterForWorker2001); err != nil {
|
||||
fmt.Println("taskCounterVForWorker2001 not registered:", err)
|
||||
} else {
|
||||
fmt.Println("taskCounterForWorker2001 registered.")
|
||||
}
|
||||
|
||||
taskCounterForWorker2001.Inc()
|
||||
taskCounterForWorker42.Inc()
|
||||
taskCounterForWorker2001.Inc()
|
||||
|
||||
// Yet another approach would be to turn the workers themselves into
|
||||
// Collectors and register them. See the Collector example for details.
|
||||
|
||||
// Output:
|
||||
// taskCounter registered.
|
||||
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
|
||||
// taskCounter unregistered.
|
||||
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
|
||||
// taskCounterVec registered.
|
||||
// Worker initialization failed: inconsistent label cardinality
|
||||
// notMyCounter is nil.
|
||||
// taskCounterForWorker42 registered.
|
||||
// taskCounterForWorker2001 registered.
|
||||
}
|
||||
|
||||
func ExampleSummary() {
|
||||
temps := prometheus.NewSummary(prometheus.SummaryOpts{
|
||||
Name: "pond_temperature_celsius",
|
||||
Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
|
||||
})
|
||||
|
||||
// Simulate some observations.
|
||||
for i := 0; i < 1000; i++ {
|
||||
temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
|
||||
}
|
||||
|
||||
// Just for demonstration, let's check the state of the summary by
|
||||
// (ab)using its Write method (which is usually only used by Prometheus
|
||||
// internally).
|
||||
metric := &dto.Metric{}
|
||||
temps.Write(metric)
|
||||
fmt.Println(proto.MarshalTextString(metric))
|
||||
|
||||
// Output:
|
||||
// summary: <
|
||||
// sample_count: 1000
|
||||
// sample_sum: 29969.50000000001
|
||||
// quantile: <
|
||||
// quantile: 0.5
|
||||
// value: 31.1
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.9
|
||||
// value: 41.3
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.99
|
||||
// value: 41.9
|
||||
// >
|
||||
// >
|
||||
}
|
||||
|
||||
func ExampleSummaryVec() {
|
||||
temps := prometheus.NewSummaryVec(
|
||||
prometheus.SummaryOpts{
|
||||
Name: "pond_temperature_celsius",
|
||||
Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
|
||||
},
|
||||
[]string{"species"},
|
||||
)
|
||||
|
||||
// Simulate some observations.
|
||||
for i := 0; i < 1000; i++ {
|
||||
temps.WithLabelValues("litoria-caerulea").Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
|
||||
temps.WithLabelValues("lithobates-catesbeianus").Observe(32 + math.Floor(100*math.Cos(float64(i)*0.11))/10)
|
||||
}
|
||||
|
||||
// Create a Summary without any observations.
|
||||
temps.WithLabelValues("leiopelma-hochstetteri")
|
||||
|
||||
// Just for demonstration, let's check the state of the summary vector
|
||||
// by registering it with a custom registry and then let it collect the
|
||||
// metrics.
|
||||
reg := prometheus.NewRegistry()
|
||||
reg.MustRegister(temps)
|
||||
|
||||
metricFamilies, err := reg.Gather()
|
||||
if err != nil || len(metricFamilies) != 1 {
|
||||
panic("unexpected behavior of custom test registry")
|
||||
}
|
||||
fmt.Println(proto.MarshalTextString(metricFamilies[0]))
|
||||
|
||||
// Output:
|
||||
// name: "pond_temperature_celsius"
|
||||
// help: "The temperature of the frog pond."
|
||||
// type: SUMMARY
|
||||
// metric: <
|
||||
// label: <
|
||||
// name: "species"
|
||||
// value: "leiopelma-hochstetteri"
|
||||
// >
|
||||
// summary: <
|
||||
// sample_count: 0
|
||||
// sample_sum: 0
|
||||
// quantile: <
|
||||
// quantile: 0.5
|
||||
// value: nan
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.9
|
||||
// value: nan
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.99
|
||||
// value: nan
|
||||
// >
|
||||
// >
|
||||
// >
|
||||
// metric: <
|
||||
// label: <
|
||||
// name: "species"
|
||||
// value: "lithobates-catesbeianus"
|
||||
// >
|
||||
// summary: <
|
||||
// sample_count: 1000
|
||||
// sample_sum: 31956.100000000017
|
||||
// quantile: <
|
||||
// quantile: 0.5
|
||||
// value: 32.4
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.9
|
||||
// value: 41.4
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.99
|
||||
// value: 41.9
|
||||
// >
|
||||
// >
|
||||
// >
|
||||
// metric: <
|
||||
// label: <
|
||||
// name: "species"
|
||||
// value: "litoria-caerulea"
|
||||
// >
|
||||
// summary: <
|
||||
// sample_count: 1000
|
||||
// sample_sum: 29969.50000000001
|
||||
// quantile: <
|
||||
// quantile: 0.5
|
||||
// value: 31.1
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.9
|
||||
// value: 41.3
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.99
|
||||
// value: 41.9
|
||||
// >
|
||||
// >
|
||||
// >
|
||||
}
|
||||
|
||||
func ExampleNewConstSummary() {
|
||||
desc := prometheus.NewDesc(
|
||||
"http_request_duration_seconds",
|
||||
"A summary of the HTTP request durations.",
|
||||
[]string{"code", "method"},
|
||||
prometheus.Labels{"owner": "example"},
|
||||
)
|
||||
|
||||
// Create a constant summary from values we got from a 3rd party telemetry system.
|
||||
s := prometheus.MustNewConstSummary(
|
||||
desc,
|
||||
4711, 403.34,
|
||||
map[float64]float64{0.5: 42.3, 0.9: 323.3},
|
||||
"200", "get",
|
||||
)
|
||||
|
||||
// Just for demonstration, let's check the state of the summary by
|
||||
// (ab)using its Write method (which is usually only used by Prometheus
|
||||
// internally).
|
||||
metric := &dto.Metric{}
|
||||
s.Write(metric)
|
||||
fmt.Println(proto.MarshalTextString(metric))
|
||||
|
||||
// Output:
|
||||
// label: <
|
||||
// name: "code"
|
||||
// value: "200"
|
||||
// >
|
||||
// label: <
|
||||
// name: "method"
|
||||
// value: "get"
|
||||
// >
|
||||
// label: <
|
||||
// name: "owner"
|
||||
// value: "example"
|
||||
// >
|
||||
// summary: <
|
||||
// sample_count: 4711
|
||||
// sample_sum: 403.34
|
||||
// quantile: <
|
||||
// quantile: 0.5
|
||||
// value: 42.3
|
||||
// >
|
||||
// quantile: <
|
||||
// quantile: 0.9
|
||||
// value: 323.3
|
||||
// >
|
||||
// >
|
||||
}
|
||||
|
||||
func ExampleHistogram() {
|
||||
temps := prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Name: "pond_temperature_celsius",
|
||||
Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
|
||||
Buckets: prometheus.LinearBuckets(20, 5, 5), // 5 buckets, each 5 centigrade wide.
|
||||
})
|
||||
|
||||
// Simulate some observations.
|
||||
for i := 0; i < 1000; i++ {
|
||||
temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
|
||||
}
|
||||
|
||||
// Just for demonstration, let's check the state of the histogram by
|
||||
// (ab)using its Write method (which is usually only used by Prometheus
|
||||
// internally).
|
||||
metric := &dto.Metric{}
|
||||
temps.Write(metric)
|
||||
fmt.Println(proto.MarshalTextString(metric))
|
||||
|
||||
// Output:
|
||||
// histogram: <
|
||||
// sample_count: 1000
|
||||
// sample_sum: 29969.50000000001
|
||||
// bucket: <
|
||||
// cumulative_count: 192
|
||||
// upper_bound: 20
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 366
|
||||
// upper_bound: 25
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 501
|
||||
// upper_bound: 30
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 638
|
||||
// upper_bound: 35
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 816
|
||||
// upper_bound: 40
|
||||
// >
|
||||
// >
|
||||
}
|
||||
|
||||
func ExampleNewConstHistogram() {
|
||||
desc := prometheus.NewDesc(
|
||||
"http_request_duration_seconds",
|
||||
"A histogram of the HTTP request durations.",
|
||||
[]string{"code", "method"},
|
||||
prometheus.Labels{"owner": "example"},
|
||||
)
|
||||
|
||||
// Create a constant histogram from values we got from a 3rd party telemetry system.
|
||||
h := prometheus.MustNewConstHistogram(
|
||||
desc,
|
||||
4711, 403.34,
|
||||
map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233},
|
||||
"200", "get",
|
||||
)
|
||||
|
||||
// Just for demonstration, let's check the state of the histogram by
|
||||
// (ab)using its Write method (which is usually only used by Prometheus
|
||||
// internally).
|
||||
metric := &dto.Metric{}
|
||||
h.Write(metric)
|
||||
fmt.Println(proto.MarshalTextString(metric))
|
||||
|
||||
// Output:
|
||||
// label: <
|
||||
// name: "code"
|
||||
// value: "200"
|
||||
// >
|
||||
// label: <
|
||||
// name: "method"
|
||||
// value: "get"
|
||||
// >
|
||||
// label: <
|
||||
// name: "owner"
|
||||
// value: "example"
|
||||
// >
|
||||
// histogram: <
|
||||
// sample_count: 4711
|
||||
// sample_sum: 403.34
|
||||
// bucket: <
|
||||
// cumulative_count: 121
|
||||
// upper_bound: 25
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 2403
|
||||
// upper_bound: 50
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 3221
|
||||
// upper_bound: 100
|
||||
// >
|
||||
// bucket: <
|
||||
// cumulative_count: 4233
|
||||
// upper_bound: 200
|
||||
// >
|
||||
// >
|
||||
}
|
||||
|
||||
func ExampleAlreadyRegisteredError() {
|
||||
reqCounter := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "requests_total",
|
||||
Help: "The total number of requests served.",
|
||||
})
|
||||
if err := prometheus.Register(reqCounter); err != nil {
|
||||
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
||||
// A counter for that metric has been registered before.
|
||||
// Use the old counter from now on.
|
||||
reqCounter = are.ExistingCollector.(prometheus.Counter)
|
||||
} else {
|
||||
// Something else went wrong!
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleGatherers() {
|
||||
reg := prometheus.NewRegistry()
|
||||
temp := prometheus.NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "temperature_kelvin",
|
||||
Help: "Temperature in Kelvin.",
|
||||
},
|
||||
[]string{"location"},
|
||||
)
|
||||
reg.MustRegister(temp)
|
||||
temp.WithLabelValues("outside").Set(273.14)
|
||||
temp.WithLabelValues("inside").Set(298.44)
|
||||
|
||||
var parser expfmt.TextParser
|
||||
|
||||
text := `
|
||||
# TYPE humidity_percent gauge
|
||||
# HELP humidity_percent Humidity in %.
|
||||
humidity_percent{location="outside"} 45.4
|
||||
humidity_percent{location="inside"} 33.2
|
||||
# TYPE temperature_kelvin gauge
|
||||
# HELP temperature_kelvin Temperature in Kelvin.
|
||||
temperature_kelvin{location="somewhere else"} 4.5
|
||||
`
|
||||
|
||||
parseText := func() ([]*dto.MetricFamily, error) {
|
||||
parsed, err := parser.TextToMetricFamilies(strings.NewReader(text))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []*dto.MetricFamily
|
||||
for _, mf := range parsed {
|
||||
result = append(result, mf)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
gatherers := prometheus.Gatherers{
|
||||
reg,
|
||||
prometheus.GathererFunc(parseText),
|
||||
}
|
||||
|
||||
gathering, err := gatherers.Gather()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
for _, mf := range gathering {
|
||||
if _, err := expfmt.MetricFamilyToText(out, mf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
fmt.Print(out.String())
|
||||
fmt.Println("----------")
|
||||
|
||||
// Note how the temperature_kelvin metric family has been merged from
|
||||
// different sources. Now try
|
||||
text = `
|
||||
# TYPE humidity_percent gauge
|
||||
# HELP humidity_percent Humidity in %.
|
||||
humidity_percent{location="outside"} 45.4
|
||||
humidity_percent{location="inside"} 33.2
|
||||
# TYPE temperature_kelvin gauge
|
||||
# HELP temperature_kelvin Temperature in Kelvin.
|
||||
# Duplicate metric:
|
||||
temperature_kelvin{location="outside"} 265.3
|
||||
# Wrong labels:
|
||||
temperature_kelvin 4.5
|
||||
`
|
||||
|
||||
gathering, err = gatherers.Gather()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Note that still as many metrics as possible are returned:
|
||||
out.Reset()
|
||||
for _, mf := range gathering {
|
||||
if _, err := expfmt.MetricFamilyToText(out, mf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
fmt.Print(out.String())
|
||||
|
||||
// Output:
|
||||
// # HELP humidity_percent Humidity in %.
|
||||
// # TYPE humidity_percent gauge
|
||||
// humidity_percent{location="inside"} 33.2
|
||||
// humidity_percent{location="outside"} 45.4
|
||||
// # HELP temperature_kelvin Temperature in Kelvin.
|
||||
// # TYPE temperature_kelvin gauge
|
||||
// temperature_kelvin{location="inside"} 298.44
|
||||
// temperature_kelvin{location="outside"} 273.14
|
||||
// temperature_kelvin{location="somewhere else"} 4.5
|
||||
// ----------
|
||||
// 2 error(s) occurred:
|
||||
// * collected metric temperature_kelvin label:<name:"location" value:"outside" > gauge:<value:265.3 > was collected before with the same name and label values
|
||||
// * collected metric temperature_kelvin gauge:<value:4.5 > has label dimensions inconsistent with previously collected metrics in the same metric family
|
||||
// # HELP humidity_percent Humidity in %.
|
||||
// # TYPE humidity_percent gauge
|
||||
// humidity_percent{location="inside"} 33.2
|
||||
// humidity_percent{location="outside"} 45.4
|
||||
// # HELP temperature_kelvin Temperature in Kelvin.
|
||||
// # TYPE temperature_kelvin gauge
|
||||
// temperature_kelvin{location="inside"} 298.44
|
||||
// temperature_kelvin{location="outside"} 273.14
|
||||
}
|
97
gateway/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector_test.go
generated
vendored
Normal file
97
gateway/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector_test.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus_test
|
||||
|
||||
import (
|
||||
"expvar"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func ExampleExpvarCollector() {
|
||||
expvarCollector := prometheus.NewExpvarCollector(map[string]*prometheus.Desc{
|
||||
"memstats": prometheus.NewDesc(
|
||||
"expvar_memstats",
|
||||
"All numeric memstats as one metric family. Not a good role-model, actually... ;-)",
|
||||
[]string{"type"}, nil,
|
||||
),
|
||||
"lone-int": prometheus.NewDesc(
|
||||
"expvar_lone_int",
|
||||
"Just an expvar int as an example.",
|
||||
nil, nil,
|
||||
),
|
||||
"http-request-map": prometheus.NewDesc(
|
||||
"expvar_http_request_total",
|
||||
"How many http requests processed, partitioned by status code and http method.",
|
||||
[]string{"code", "method"}, nil,
|
||||
),
|
||||
})
|
||||
prometheus.MustRegister(expvarCollector)
|
||||
|
||||
// The Prometheus part is done here. But to show that this example is
|
||||
// doing anything, we have to manually export something via expvar. In
|
||||
// real-life use-cases, some library would already have exported via
|
||||
// expvar what we want to re-export as Prometheus metrics.
|
||||
expvar.NewInt("lone-int").Set(42)
|
||||
expvarMap := expvar.NewMap("http-request-map")
|
||||
var (
|
||||
expvarMap1, expvarMap2 expvar.Map
|
||||
expvarInt11, expvarInt12, expvarInt21, expvarInt22 expvar.Int
|
||||
)
|
||||
expvarMap1.Init()
|
||||
expvarMap2.Init()
|
||||
expvarInt11.Set(3)
|
||||
expvarInt12.Set(13)
|
||||
expvarInt21.Set(11)
|
||||
expvarInt22.Set(212)
|
||||
expvarMap1.Set("POST", &expvarInt11)
|
||||
expvarMap1.Set("GET", &expvarInt12)
|
||||
expvarMap2.Set("POST", &expvarInt21)
|
||||
expvarMap2.Set("GET", &expvarInt22)
|
||||
expvarMap.Set("404", &expvarMap1)
|
||||
expvarMap.Set("200", &expvarMap2)
|
||||
// Results in the following expvar map:
|
||||
// "http-request-count": {"200": {"POST": 11, "GET": 212}, "404": {"POST": 3, "GET": 13}}
|
||||
|
||||
// Let's see what the scrape would yield, but exclude the memstats metrics.
|
||||
metricStrings := []string{}
|
||||
metric := dto.Metric{}
|
||||
metricChan := make(chan prometheus.Metric)
|
||||
go func() {
|
||||
expvarCollector.Collect(metricChan)
|
||||
close(metricChan)
|
||||
}()
|
||||
for m := range metricChan {
|
||||
if strings.Index(m.Desc().String(), "expvar_memstats") == -1 {
|
||||
metric.Reset()
|
||||
m.Write(&metric)
|
||||
metricStrings = append(metricStrings, metric.String())
|
||||
}
|
||||
}
|
||||
sort.Strings(metricStrings)
|
||||
for _, s := range metricStrings {
|
||||
fmt.Println(strings.TrimRight(s, " "))
|
||||
}
|
||||
// Output:
|
||||
// label:<name:"code" value:"200" > label:<name:"method" value:"GET" > untyped:<value:212 >
|
||||
// label:<name:"code" value:"200" > label:<name:"method" value:"POST" > untyped:<value:11 >
|
||||
// label:<name:"code" value:"404" > label:<name:"method" value:"GET" > untyped:<value:13 >
|
||||
// label:<name:"code" value:"404" > label:<name:"method" value:"POST" > untyped:<value:3 >
|
||||
// untyped:<value:42 >
|
||||
}
|
69
gateway/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
69
gateway/vendor/github.com/prometheus/client_golang/prometheus/gauge.go
generated
vendored
@ -27,21 +27,16 @@ type Gauge interface {
|
||||
|
||||
// Set sets the Gauge to an arbitrary value.
|
||||
Set(float64)
|
||||
// Inc increments the Gauge by 1. Use Add to increment it by arbitrary
|
||||
// values.
|
||||
// Inc increments the Gauge by 1.
|
||||
Inc()
|
||||
// Dec decrements the Gauge by 1. Use Sub to decrement it by arbitrary
|
||||
// values.
|
||||
// Dec decrements the Gauge by 1.
|
||||
Dec()
|
||||
// Add adds the given value to the Gauge. (The value can be negative,
|
||||
// resulting in a decrease of the Gauge.)
|
||||
// Add adds the given value to the Gauge. (The value can be
|
||||
// negative, resulting in a decrease of the Gauge.)
|
||||
Add(float64)
|
||||
// Sub subtracts the given value from the Gauge. (The value can be
|
||||
// negative, resulting in an increase of the Gauge.)
|
||||
Sub(float64)
|
||||
|
||||
// SetToCurrentTime sets the Gauge to the current Unix time in seconds.
|
||||
SetToCurrentTime()
|
||||
}
|
||||
|
||||
// GaugeOpts is an alias for Opts. See there for doc comments.
|
||||
@ -63,11 +58,12 @@ func NewGauge(opts GaugeOpts) Gauge {
|
||||
// (e.g. number of operations queued, partitioned by user and operation
|
||||
// type). Create instances with NewGaugeVec.
|
||||
type GaugeVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
|
||||
// partitioned by the given label names.
|
||||
// partitioned by the given label names. At least one label name must be
|
||||
// provided.
|
||||
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
desc := NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
@ -76,57 +72,28 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &GaugeVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
return newValue(desc, GaugeValue, 0, lvs...)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Gauge for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Gauge is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Gauge to only
|
||||
// create the new Gauge but leave it at its starting value 0. See also the
|
||||
// SummaryVec example.
|
||||
//
|
||||
// Keeping the Gauge for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Gauge from the GaugeVec. In that case, the
|
||||
// Gauge will still exist, but it will not be exported anymore, even if a
|
||||
// Gauge with the same label values is created later. See also the CounterVec
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// GetMetricWithLabelValues replaces the method of the same name in
|
||||
// MetricVec. The difference is that this method returns a Gauge and not a
|
||||
// Metric so that no type conversion is required.
|
||||
func (m *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
||||
metric, err := m.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Gauge), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Gauge for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new Gauge is created. Implications of
|
||||
// creating a Gauge without using it and keeping the Gauge for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc.
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
||||
// difference is that this method returns a Gauge and not a Metric so that no
|
||||
// type conversion is required.
|
||||
func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||
metric, err := m.metricVec.getMetricWith(labels)
|
||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Gauge), err
|
||||
}
|
||||
@ -138,14 +105,14 @@ func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||
// error, WithLabelValues allows shortcuts like
|
||||
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||
func (m *GaugeVec) WithLabelValues(lvs ...string) Gauge {
|
||||
return m.metricVec.withLabelValues(lvs...).(Gauge)
|
||||
return m.MetricVec.WithLabelValues(lvs...).(Gauge)
|
||||
}
|
||||
|
||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||
// returned an error. By not returning an error, With allows shortcuts like
|
||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
||||
func (m *GaugeVec) With(labels Labels) Gauge {
|
||||
return m.metricVec.with(labels).(Gauge)
|
||||
return m.MetricVec.With(labels).(Gauge)
|
||||
}
|
||||
|
||||
// GaugeFunc is a Gauge whose value is determined at collect time by calling a
|
||||
|
182
gateway/vendor/github.com/prometheus/client_golang/prometheus/gauge_test.go
generated
vendored
Normal file
182
gateway/vendor/github.com/prometheus/client_golang/prometheus/gauge_test.go
generated
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
func listenGaugeStream(vals, result chan float64, done chan struct{}) {
|
||||
var sum float64
|
||||
outer:
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
close(vals)
|
||||
for v := range vals {
|
||||
sum += v
|
||||
}
|
||||
break outer
|
||||
case v := <-vals:
|
||||
sum += v
|
||||
}
|
||||
}
|
||||
result <- sum
|
||||
close(result)
|
||||
}
|
||||
|
||||
func TestGaugeConcurrency(t *testing.T) {
|
||||
it := func(n uint32) bool {
|
||||
mutations := int(n % 10000)
|
||||
concLevel := int(n%15 + 1)
|
||||
|
||||
var start, end sync.WaitGroup
|
||||
start.Add(1)
|
||||
end.Add(concLevel)
|
||||
|
||||
sStream := make(chan float64, mutations*concLevel)
|
||||
result := make(chan float64)
|
||||
done := make(chan struct{})
|
||||
|
||||
go listenGaugeStream(sStream, result, done)
|
||||
go func() {
|
||||
end.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
gge := NewGauge(GaugeOpts{
|
||||
Name: "test_gauge",
|
||||
Help: "no help can be found here",
|
||||
})
|
||||
for i := 0; i < concLevel; i++ {
|
||||
vals := make([]float64, mutations)
|
||||
for j := 0; j < mutations; j++ {
|
||||
vals[j] = rand.Float64() - 0.5
|
||||
}
|
||||
|
||||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for _, v := range vals {
|
||||
sStream <- v
|
||||
gge.Add(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
}
|
||||
start.Done()
|
||||
|
||||
if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 {
|
||||
t.Fatalf("expected approx. %f, got %f", expected, got)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(it, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaugeVecConcurrency(t *testing.T) {
|
||||
it := func(n uint32) bool {
|
||||
mutations := int(n % 10000)
|
||||
concLevel := int(n%15 + 1)
|
||||
vecLength := int(n%5 + 1)
|
||||
|
||||
var start, end sync.WaitGroup
|
||||
start.Add(1)
|
||||
end.Add(concLevel)
|
||||
|
||||
sStreams := make([]chan float64, vecLength)
|
||||
results := make([]chan float64, vecLength)
|
||||
done := make(chan struct{})
|
||||
|
||||
for i := 0; i < vecLength; i++ {
|
||||
sStreams[i] = make(chan float64, mutations*concLevel)
|
||||
results[i] = make(chan float64)
|
||||
go listenGaugeStream(sStreams[i], results[i], done)
|
||||
}
|
||||
|
||||
go func() {
|
||||
end.Wait()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
gge := NewGaugeVec(
|
||||
GaugeOpts{
|
||||
Name: "test_gauge",
|
||||
Help: "no help can be found here",
|
||||
},
|
||||
[]string{"label"},
|
||||
)
|
||||
for i := 0; i < concLevel; i++ {
|
||||
vals := make([]float64, mutations)
|
||||
pick := make([]int, mutations)
|
||||
for j := 0; j < mutations; j++ {
|
||||
vals[j] = rand.Float64() - 0.5
|
||||
pick[j] = rand.Intn(vecLength)
|
||||
}
|
||||
|
||||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for i, v := range vals {
|
||||
sStreams[pick[i]] <- v
|
||||
gge.WithLabelValues(string('A' + pick[i])).Add(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
}
|
||||
start.Done()
|
||||
|
||||
for i := range sStreams {
|
||||
if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 {
|
||||
t.Fatalf("expected approx. %f, got %f", expected, got)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(it, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaugeFunc(t *testing.T) {
|
||||
gf := NewGaugeFunc(
|
||||
GaugeOpts{
|
||||
Name: "test_name",
|
||||
Help: "test help",
|
||||
ConstLabels: Labels{"a": "1", "b": "2"},
|
||||
},
|
||||
func() float64 { return 3.1415 },
|
||||
)
|
||||
|
||||
if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: []}`, gf.Desc().String(); expected != got {
|
||||
t.Errorf("expected %q, got %q", expected, got)
|
||||
}
|
||||
|
||||
m := &dto.Metric{}
|
||||
gf.Write(m)
|
||||
|
||||
if expected, got := `label:<name:"a" value:"1" > label:<name:"b" value:"2" > gauge:<value:3.1415 > `, m.String(); expected != got {
|
||||
t.Errorf("expected %q, got %q", expected, got)
|
||||
}
|
||||
}
|
43
gateway/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
43
gateway/vendor/github.com/prometheus/client_golang/prometheus/go_collector.go
generated
vendored
@ -8,9 +8,8 @@ import (
|
||||
)
|
||||
|
||||
type goCollector struct {
|
||||
goroutinesDesc *Desc
|
||||
threadsDesc *Desc
|
||||
gcDesc *Desc
|
||||
goroutines Gauge
|
||||
gcDesc *Desc
|
||||
|
||||
// metrics to describe and collect
|
||||
metrics memStatsMetrics
|
||||
@ -20,14 +19,11 @@ type goCollector struct {
|
||||
// go process.
|
||||
func NewGoCollector() Collector {
|
||||
return &goCollector{
|
||||
goroutinesDesc: NewDesc(
|
||||
"go_goroutines",
|
||||
"Number of goroutines that currently exist.",
|
||||
nil, nil),
|
||||
threadsDesc: NewDesc(
|
||||
"go_threads",
|
||||
"Number of OS threads created",
|
||||
nil, nil),
|
||||
goroutines: NewGauge(GaugeOpts{
|
||||
Namespace: "go",
|
||||
Name: "goroutines",
|
||||
Help: "Number of goroutines that currently exist.",
|
||||
}),
|
||||
gcDesc: NewDesc(
|
||||
"go_gc_duration_seconds",
|
||||
"A summary of the GC invocation durations.",
|
||||
@ -52,7 +48,7 @@ func NewGoCollector() Collector {
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("sys_bytes"),
|
||||
"Number of bytes obtained from system.",
|
||||
"Number of bytes obtained by system. Sum of all system allocations.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
|
||||
@ -115,12 +111,12 @@ func NewGoCollector() Collector {
|
||||
valType: GaugeValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_released_bytes"),
|
||||
"Number of heap bytes released to OS.",
|
||||
memstatNamespace("heap_released_bytes_total"),
|
||||
"Total number of heap bytes released to OS.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
|
||||
valType: GaugeValue,
|
||||
valType: CounterValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("heap_objects"),
|
||||
@ -217,14 +213,6 @@ func NewGoCollector() Collector {
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return float64(ms.LastGC) / 1e9 },
|
||||
valType: GaugeValue,
|
||||
}, {
|
||||
desc: NewDesc(
|
||||
memstatNamespace("gc_cpu_fraction"),
|
||||
"The fraction of this program's available CPU time used by the GC since the program started.",
|
||||
nil, nil,
|
||||
),
|
||||
eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction },
|
||||
valType: GaugeValue,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -236,9 +224,9 @@ func memstatNamespace(s string) string {
|
||||
|
||||
// Describe returns all descriptions of the collector.
|
||||
func (c *goCollector) Describe(ch chan<- *Desc) {
|
||||
ch <- c.goroutinesDesc
|
||||
ch <- c.threadsDesc
|
||||
ch <- c.goroutines.Desc()
|
||||
ch <- c.gcDesc
|
||||
|
||||
for _, i := range c.metrics {
|
||||
ch <- i.desc
|
||||
}
|
||||
@ -246,9 +234,8 @@ func (c *goCollector) Describe(ch chan<- *Desc) {
|
||||
|
||||
// Collect returns the current state of all metrics of the collector.
|
||||
func (c *goCollector) Collect(ch chan<- Metric) {
|
||||
ch <- MustNewConstMetric(c.goroutinesDesc, GaugeValue, float64(runtime.NumGoroutine()))
|
||||
n, _ := runtime.ThreadCreateProfile(nil)
|
||||
ch <- MustNewConstMetric(c.threadsDesc, GaugeValue, float64(n))
|
||||
c.goroutines.Set(float64(runtime.NumGoroutine()))
|
||||
ch <- c.goroutines
|
||||
|
||||
var stats debug.GCStats
|
||||
stats.PauseQuantiles = make([]time.Duration, 5)
|
||||
|
123
gateway/vendor/github.com/prometheus/client_golang/prometheus/go_collector_test.go
generated
vendored
Normal file
123
gateway/vendor/github.com/prometheus/client_golang/prometheus/go_collector_test.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
func TestGoCollector(t *testing.T) {
|
||||
var (
|
||||
c = NewGoCollector()
|
||||
ch = make(chan Metric)
|
||||
waitc = make(chan struct{})
|
||||
closec = make(chan struct{})
|
||||
old = -1
|
||||
)
|
||||
defer close(closec)
|
||||
|
||||
go func() {
|
||||
c.Collect(ch)
|
||||
go func(c <-chan struct{}) {
|
||||
<-c
|
||||
}(closec)
|
||||
<-waitc
|
||||
c.Collect(ch)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case metric := <-ch:
|
||||
switch m := metric.(type) {
|
||||
// Attention, this also catches Counter...
|
||||
case Gauge:
|
||||
pb := &dto.Metric{}
|
||||
m.Write(pb)
|
||||
if pb.GetGauge() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if old == -1 {
|
||||
old = int(pb.GetGauge().GetValue())
|
||||
close(waitc)
|
||||
continue
|
||||
}
|
||||
|
||||
if diff := int(pb.GetGauge().GetValue()) - old; diff != 1 {
|
||||
// TODO: This is flaky in highly concurrent situations.
|
||||
t.Errorf("want 1 new goroutine, got %d", diff)
|
||||
}
|
||||
|
||||
// GoCollector performs two sends per call.
|
||||
// On line 27 we need to receive the second send
|
||||
// to shut down cleanly.
|
||||
<-ch
|
||||
return
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatalf("expected collect timed out")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGCCollector(t *testing.T) {
|
||||
var (
|
||||
c = NewGoCollector()
|
||||
ch = make(chan Metric)
|
||||
waitc = make(chan struct{})
|
||||
closec = make(chan struct{})
|
||||
oldGC uint64
|
||||
oldPause float64
|
||||
)
|
||||
defer close(closec)
|
||||
|
||||
go func() {
|
||||
c.Collect(ch)
|
||||
// force GC
|
||||
runtime.GC()
|
||||
<-waitc
|
||||
c.Collect(ch)
|
||||
}()
|
||||
|
||||
first := true
|
||||
for {
|
||||
select {
|
||||
case metric := <-ch:
|
||||
switch m := metric.(type) {
|
||||
case *constSummary, *value:
|
||||
pb := &dto.Metric{}
|
||||
m.Write(pb)
|
||||
if pb.GetSummary() == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(pb.GetSummary().Quantile) != 5 {
|
||||
t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile))
|
||||
}
|
||||
for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} {
|
||||
if *pb.GetSummary().Quantile[idx].Quantile != want {
|
||||
t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want)
|
||||
}
|
||||
}
|
||||
if first {
|
||||
first = false
|
||||
oldGC = *pb.GetSummary().SampleCount
|
||||
oldPause = *pb.GetSummary().SampleSum
|
||||
close(waitc)
|
||||
continue
|
||||
}
|
||||
if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 {
|
||||
t.Errorf("want 1 new garbage collection run, got %d", diff)
|
||||
}
|
||||
if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 {
|
||||
t.Errorf("want moar pause, got %f", diff)
|
||||
}
|
||||
return
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatalf("expected collect timed out")
|
||||
}
|
||||
}
|
||||
}
|
69
gateway/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
69
gateway/vendor/github.com/prometheus/client_golang/prometheus/histogram.go
generated
vendored
@ -287,11 +287,12 @@ func (h *histogram) Write(out *dto.Metric) error {
|
||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||
// instances with NewHistogramVec.
|
||||
type HistogramVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
|
||||
// partitioned by the given label names.
|
||||
// partitioned by the given label names. At least one label name must be
|
||||
// provided.
|
||||
func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||
desc := NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
@ -300,60 +301,30 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &HistogramVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
return newHistogram(desc, opts, lvs...)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Histogram for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Histogram is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Histogram to only
|
||||
// create the new Histogram but leave it at its starting value, a Histogram without
|
||||
// any observations.
|
||||
//
|
||||
// Keeping the Histogram for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Histogram from the HistogramVec. In that case, the
|
||||
// Histogram will still exist, but it will not be exported anymore, even if a
|
||||
// Histogram with the same label values is created later. See also the CounterVec
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
metric, err := m.metricVec.getMetricWithLabelValues(lvs...)
|
||||
// GetMetricWithLabelValues replaces the method of the same name in
|
||||
// MetricVec. The difference is that this method returns a Histogram and not a
|
||||
// Metric so that no type conversion is required.
|
||||
func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Histogram, error) {
|
||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
return metric.(Histogram), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Histogram for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new Histogram is created. Implications of
|
||||
// creating a Histogram without using it and keeping the Histogram for later use
|
||||
// are the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc.
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (m *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
metric, err := m.metricVec.getMetricWith(labels)
|
||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
||||
// difference is that this method returns a Histogram and not a Metric so that no
|
||||
// type conversion is required.
|
||||
func (m *HistogramVec) GetMetricWith(labels Labels) (Histogram, error) {
|
||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
return metric.(Histogram), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@ -362,15 +333,15 @@ func (m *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
||||
// error, WithLabelValues allows shortcuts like
|
||||
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
||||
func (m *HistogramVec) WithLabelValues(lvs ...string) Observer {
|
||||
return m.metricVec.withLabelValues(lvs...).(Observer)
|
||||
func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram {
|
||||
return m.MetricVec.WithLabelValues(lvs...).(Histogram)
|
||||
}
|
||||
|
||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||
// returned an error. By not returning an error, With allows shortcuts like
|
||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
||||
func (m *HistogramVec) With(labels Labels) Observer {
|
||||
return m.metricVec.with(labels).(Observer)
|
||||
func (m *HistogramVec) With(labels Labels) Histogram {
|
||||
return m.MetricVec.With(labels).(Histogram)
|
||||
}
|
||||
|
||||
type constHistogram struct {
|
||||
|
326
gateway/vendor/github.com/prometheus/client_golang/prometheus/histogram_test.go
generated
vendored
Normal file
326
gateway/vendor/github.com/prometheus/client_golang/prometheus/histogram_test.go
generated
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
// 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 prometheus
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
func benchmarkHistogramObserve(w int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(w)
|
||||
|
||||
g := new(sync.WaitGroup)
|
||||
g.Add(1)
|
||||
|
||||
s := NewHistogram(HistogramOpts{})
|
||||
|
||||
for i := 0; i < w; i++ {
|
||||
go func() {
|
||||
g.Wait()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.Observe(float64(i))
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
g.Done()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkHistogramObserve1(b *testing.B) {
|
||||
benchmarkHistogramObserve(1, b)
|
||||
}
|
||||
|
||||
func BenchmarkHistogramObserve2(b *testing.B) {
|
||||
benchmarkHistogramObserve(2, b)
|
||||
}
|
||||
|
||||
func BenchmarkHistogramObserve4(b *testing.B) {
|
||||
benchmarkHistogramObserve(4, b)
|
||||
}
|
||||
|
||||
func BenchmarkHistogramObserve8(b *testing.B) {
|
||||
benchmarkHistogramObserve(8, b)
|
||||
}
|
||||
|
||||
func benchmarkHistogramWrite(w int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(w)
|
||||
|
||||
g := new(sync.WaitGroup)
|
||||
g.Add(1)
|
||||
|
||||
s := NewHistogram(HistogramOpts{})
|
||||
|
||||
for i := 0; i < 1000000; i++ {
|
||||
s.Observe(float64(i))
|
||||
}
|
||||
|
||||
for j := 0; j < w; j++ {
|
||||
outs := make([]dto.Metric, b.N)
|
||||
|
||||
go func(o []dto.Metric) {
|
||||
g.Wait()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.Write(&o[i])
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}(outs)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
g.Done()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkHistogramWrite1(b *testing.B) {
|
||||
benchmarkHistogramWrite(1, b)
|
||||
}
|
||||
|
||||
func BenchmarkHistogramWrite2(b *testing.B) {
|
||||
benchmarkHistogramWrite(2, b)
|
||||
}
|
||||
|
||||
func BenchmarkHistogramWrite4(b *testing.B) {
|
||||
benchmarkHistogramWrite(4, b)
|
||||
}
|
||||
|
||||
func BenchmarkHistogramWrite8(b *testing.B) {
|
||||
benchmarkHistogramWrite(8, b)
|
||||
}
|
||||
|
||||
// Intentionally adding +Inf here to test if that case is handled correctly.
|
||||
// Also, getCumulativeCounts depends on it.
|
||||
var testBuckets = []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)}
|
||||
|
||||
func TestHistogramConcurrency(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short mode.")
|
||||
}
|
||||
|
||||
rand.Seed(42)
|
||||
|
||||
it := func(n uint32) bool {
|
||||
mutations := int(n%1e4 + 1e4)
|
||||
concLevel := int(n%5 + 1)
|
||||
total := mutations * concLevel
|
||||
|
||||
var start, end sync.WaitGroup
|
||||
start.Add(1)
|
||||
end.Add(concLevel)
|
||||
|
||||
sum := NewHistogram(HistogramOpts{
|
||||
Name: "test_histogram",
|
||||
Help: "helpless",
|
||||
Buckets: testBuckets,
|
||||
})
|
||||
|
||||
allVars := make([]float64, total)
|
||||
var sampleSum float64
|
||||
for i := 0; i < concLevel; i++ {
|
||||
vals := make([]float64, mutations)
|
||||
for j := 0; j < mutations; j++ {
|
||||
v := rand.NormFloat64()
|
||||
vals[j] = v
|
||||
allVars[i*mutations+j] = v
|
||||
sampleSum += v
|
||||
}
|
||||
|
||||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for _, v := range vals {
|
||||
sum.Observe(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
}
|
||||
sort.Float64s(allVars)
|
||||
start.Done()
|
||||
end.Wait()
|
||||
|
||||
m := &dto.Metric{}
|
||||
sum.Write(m)
|
||||
if got, want := int(*m.Histogram.SampleCount), total; got != want {
|
||||
t.Errorf("got sample count %d, want %d", got, want)
|
||||
}
|
||||
if got, want := *m.Histogram.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 {
|
||||
t.Errorf("got sample sum %f, want %f", got, want)
|
||||
}
|
||||
|
||||
wantCounts := getCumulativeCounts(allVars)
|
||||
|
||||
if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
|
||||
t.Errorf("got %d buckets in protobuf, want %d", got, want)
|
||||
}
|
||||
for i, wantBound := range testBuckets {
|
||||
if i == len(testBuckets)-1 {
|
||||
break // No +Inf bucket in protobuf.
|
||||
}
|
||||
if gotBound := *m.Histogram.Bucket[i].UpperBound; gotBound != wantBound {
|
||||
t.Errorf("got bound %f, want %f", gotBound, wantBound)
|
||||
}
|
||||
if gotCount, wantCount := *m.Histogram.Bucket[i].CumulativeCount, wantCounts[i]; gotCount != wantCount {
|
||||
t.Errorf("got count %d, want %d", gotCount, wantCount)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(it, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistogramVecConcurrency(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short mode.")
|
||||
}
|
||||
|
||||
rand.Seed(42)
|
||||
|
||||
objectives := make([]float64, 0, len(DefObjectives))
|
||||
for qu := range DefObjectives {
|
||||
|
||||
objectives = append(objectives, qu)
|
||||
}
|
||||
sort.Float64s(objectives)
|
||||
|
||||
it := func(n uint32) bool {
|
||||
mutations := int(n%1e4 + 1e4)
|
||||
concLevel := int(n%7 + 1)
|
||||
vecLength := int(n%3 + 1)
|
||||
|
||||
var start, end sync.WaitGroup
|
||||
start.Add(1)
|
||||
end.Add(concLevel)
|
||||
|
||||
his := NewHistogramVec(
|
||||
HistogramOpts{
|
||||
Name: "test_histogram",
|
||||
Help: "helpless",
|
||||
Buckets: []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)},
|
||||
},
|
||||
[]string{"label"},
|
||||
)
|
||||
|
||||
allVars := make([][]float64, vecLength)
|
||||
sampleSums := make([]float64, vecLength)
|
||||
for i := 0; i < concLevel; i++ {
|
||||
vals := make([]float64, mutations)
|
||||
picks := make([]int, mutations)
|
||||
for j := 0; j < mutations; j++ {
|
||||
v := rand.NormFloat64()
|
||||
vals[j] = v
|
||||
pick := rand.Intn(vecLength)
|
||||
picks[j] = pick
|
||||
allVars[pick] = append(allVars[pick], v)
|
||||
sampleSums[pick] += v
|
||||
}
|
||||
|
||||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for i, v := range vals {
|
||||
his.WithLabelValues(string('A' + picks[i])).Observe(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
}
|
||||
for _, vars := range allVars {
|
||||
sort.Float64s(vars)
|
||||
}
|
||||
start.Done()
|
||||
end.Wait()
|
||||
|
||||
for i := 0; i < vecLength; i++ {
|
||||
m := &dto.Metric{}
|
||||
s := his.WithLabelValues(string('A' + i))
|
||||
s.Write(m)
|
||||
|
||||
if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
|
||||
t.Errorf("got %d buckets in protobuf, want %d", got, want)
|
||||
}
|
||||
if got, want := int(*m.Histogram.SampleCount), len(allVars[i]); got != want {
|
||||
t.Errorf("got sample count %d, want %d", got, want)
|
||||
}
|
||||
if got, want := *m.Histogram.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 {
|
||||
t.Errorf("got sample sum %f, want %f", got, want)
|
||||
}
|
||||
|
||||
wantCounts := getCumulativeCounts(allVars[i])
|
||||
|
||||
for j, wantBound := range testBuckets {
|
||||
if j == len(testBuckets)-1 {
|
||||
break // No +Inf bucket in protobuf.
|
||||
}
|
||||
if gotBound := *m.Histogram.Bucket[j].UpperBound; gotBound != wantBound {
|
||||
t.Errorf("got bound %f, want %f", gotBound, wantBound)
|
||||
}
|
||||
if gotCount, wantCount := *m.Histogram.Bucket[j].CumulativeCount, wantCounts[j]; gotCount != wantCount {
|
||||
t.Errorf("got count %d, want %d", gotCount, wantCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(it, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getCumulativeCounts(vars []float64) []uint64 {
|
||||
counts := make([]uint64, len(testBuckets))
|
||||
for _, v := range vars {
|
||||
for i := len(testBuckets) - 1; i >= 0; i-- {
|
||||
if v > testBuckets[i] {
|
||||
break
|
||||
}
|
||||
counts[i]++
|
||||
}
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
func TestBuckets(t *testing.T) {
|
||||
got := LinearBuckets(-15, 5, 6)
|
||||
want := []float64{-15, -10, -5, 0, 5, 10}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("linear buckets: got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
got = ExponentialBuckets(100, 1.2, 3)
|
||||
want = []float64{100, 120, 144}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("linear buckets: got %v, want %v", got, want)
|
||||
}
|
||||
}
|
108
gateway/vendor/github.com/prometheus/client_golang/prometheus/http.go
generated
vendored
108
gateway/vendor/github.com/prometheus/client_golang/prometheus/http.go
generated
vendored
@ -62,8 +62,7 @@ func giveBuf(buf *bytes.Buffer) {
|
||||
//
|
||||
// Deprecated: Please note the issues described in the doc comment of
|
||||
// InstrumentHandler. You might want to consider using promhttp.Handler instead
|
||||
// (which is not instrumented, but can be instrumented with the tooling provided
|
||||
// in package promhttp).
|
||||
// (which is non instrumented).
|
||||
func Handler() http.Handler {
|
||||
return InstrumentHandler("prometheus", UninstrumentedHandler())
|
||||
}
|
||||
@ -159,8 +158,7 @@ func nowSeries(t ...time.Time) nower {
|
||||
// value. http_requests_total is a metric vector partitioned by HTTP method
|
||||
// (label name "method") and HTTP status code (label name "code").
|
||||
//
|
||||
// Deprecated: InstrumentHandler has several issues. Use the tooling provided in
|
||||
// package promhttp instead. The issues are the following:
|
||||
// Deprecated: InstrumentHandler has several issues:
|
||||
//
|
||||
// - It uses Summaries rather than Histograms. Summaries are not useful if
|
||||
// aggregation across multiple instances is required.
|
||||
@ -174,8 +172,9 @@ func nowSeries(t ...time.Time) nower {
|
||||
// httputil.ReverseProxy is a prominent example for a handler
|
||||
// performing such writes.
|
||||
//
|
||||
// - It has additional issues with HTTP/2, cf.
|
||||
// https://github.com/prometheus/client_golang/issues/272.
|
||||
// Upcoming versions of this package will provide ways of instrumenting HTTP
|
||||
// handlers that are more flexible and have fewer issues. Please prefer direct
|
||||
// instrumentation in the meantime.
|
||||
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
|
||||
return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
|
||||
}
|
||||
@ -185,13 +184,12 @@ func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFun
|
||||
// issues).
|
||||
//
|
||||
// Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
|
||||
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||
// InstrumentHandler is.
|
||||
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||
return InstrumentHandlerFuncWithOpts(
|
||||
SummaryOpts{
|
||||
Subsystem: "http",
|
||||
ConstLabels: Labels{"handler": handlerName},
|
||||
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
|
||||
},
|
||||
handlerFunc,
|
||||
)
|
||||
@ -224,7 +222,7 @@ func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWri
|
||||
// SummaryOpts.
|
||||
//
|
||||
// Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
|
||||
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||
// InstrumentHandler is.
|
||||
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
|
||||
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
|
||||
}
|
||||
@ -235,7 +233,7 @@ func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.Hand
|
||||
// SummaryOpts are used.
|
||||
//
|
||||
// Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
|
||||
// as InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||
// as InstrumentHandler is.
|
||||
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
|
||||
reqCnt := NewCounterVec(
|
||||
CounterOpts{
|
||||
@ -247,52 +245,34 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
|
||||
},
|
||||
instLabels,
|
||||
)
|
||||
if err := Register(reqCnt); err != nil {
|
||||
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||
reqCnt = are.ExistingCollector.(*CounterVec)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
opts.Name = "request_duration_microseconds"
|
||||
opts.Help = "The HTTP request latencies in microseconds."
|
||||
reqDur := NewSummary(opts)
|
||||
if err := Register(reqDur); err != nil {
|
||||
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||
reqDur = are.ExistingCollector.(Summary)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
opts.Name = "request_size_bytes"
|
||||
opts.Help = "The HTTP request sizes in bytes."
|
||||
reqSz := NewSummary(opts)
|
||||
if err := Register(reqSz); err != nil {
|
||||
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||
reqSz = are.ExistingCollector.(Summary)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
opts.Name = "response_size_bytes"
|
||||
opts.Help = "The HTTP response sizes in bytes."
|
||||
resSz := NewSummary(opts)
|
||||
if err := Register(resSz); err != nil {
|
||||
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||
resSz = are.ExistingCollector.(Summary)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
|
||||
regReqDur := MustRegisterOrGet(reqDur).(Summary)
|
||||
regReqSz := MustRegisterOrGet(reqSz).(Summary)
|
||||
regResSz := MustRegisterOrGet(resSz).(Summary)
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
now := time.Now()
|
||||
|
||||
delegate := &responseWriterDelegator{ResponseWriter: w}
|
||||
out := computeApproximateRequestSize(r)
|
||||
out := make(chan int)
|
||||
urlLen := 0
|
||||
if r.URL != nil {
|
||||
urlLen = len(r.URL.String())
|
||||
}
|
||||
go computeApproximateRequestSize(r, out, urlLen)
|
||||
|
||||
_, cn := w.(http.CloseNotifier)
|
||||
_, fl := w.(http.Flusher)
|
||||
@ -310,44 +290,30 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
|
||||
|
||||
method := sanitizeMethod(r.Method)
|
||||
code := sanitizeCode(delegate.status)
|
||||
reqCnt.WithLabelValues(method, code).Inc()
|
||||
reqDur.Observe(elapsed)
|
||||
resSz.Observe(float64(delegate.written))
|
||||
reqSz.Observe(float64(<-out))
|
||||
regReqCnt.WithLabelValues(method, code).Inc()
|
||||
regReqDur.Observe(elapsed)
|
||||
regResSz.Observe(float64(delegate.written))
|
||||
regReqSz.Observe(float64(<-out))
|
||||
})
|
||||
}
|
||||
|
||||
func computeApproximateRequestSize(r *http.Request) <-chan int {
|
||||
// Get URL length in current go routine for avoiding a race condition.
|
||||
// HandlerFunc that runs in parallel may modify the URL.
|
||||
s := 0
|
||||
if r.URL != nil {
|
||||
s += len(r.URL.String())
|
||||
func computeApproximateRequestSize(r *http.Request, out chan int, s int) {
|
||||
s += len(r.Method)
|
||||
s += len(r.Proto)
|
||||
for name, values := range r.Header {
|
||||
s += len(name)
|
||||
for _, value := range values {
|
||||
s += len(value)
|
||||
}
|
||||
}
|
||||
s += len(r.Host)
|
||||
|
||||
out := make(chan int, 1)
|
||||
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
|
||||
|
||||
go func() {
|
||||
s += len(r.Method)
|
||||
s += len(r.Proto)
|
||||
for name, values := range r.Header {
|
||||
s += len(name)
|
||||
for _, value := range values {
|
||||
s += len(value)
|
||||
}
|
||||
}
|
||||
s += len(r.Host)
|
||||
|
||||
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
|
||||
|
||||
if r.ContentLength != -1 {
|
||||
s += int(r.ContentLength)
|
||||
}
|
||||
out <- s
|
||||
close(out)
|
||||
}()
|
||||
|
||||
return out
|
||||
if r.ContentLength != -1 {
|
||||
s += int(r.ContentLength)
|
||||
}
|
||||
out <- s
|
||||
}
|
||||
|
||||
type responseWriterDelegator struct {
|
||||
|
121
gateway/vendor/github.com/prometheus/client_golang/prometheus/http_test.go
generated
vendored
Normal file
121
gateway/vendor/github.com/prometheus/client_golang/prometheus/http_test.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
type respBody string
|
||||
|
||||
func (b respBody) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusTeapot)
|
||||
w.Write([]byte(b))
|
||||
}
|
||||
|
||||
func TestInstrumentHandler(t *testing.T) {
|
||||
defer func(n nower) {
|
||||
now = n.(nower)
|
||||
}(now)
|
||||
|
||||
instant := time.Now()
|
||||
end := instant.Add(30 * time.Second)
|
||||
now = nowSeries(instant, end)
|
||||
respBody := respBody("Howdy there!")
|
||||
|
||||
hndlr := InstrumentHandler("test-handler", respBody)
|
||||
|
||||
opts := SummaryOpts{
|
||||
Subsystem: "http",
|
||||
ConstLabels: Labels{"handler": "test-handler"},
|
||||
}
|
||||
|
||||
reqCnt := MustRegisterOrGet(NewCounterVec(
|
||||
CounterOpts{
|
||||
Namespace: opts.Namespace,
|
||||
Subsystem: opts.Subsystem,
|
||||
Name: "requests_total",
|
||||
Help: "Total number of HTTP requests made.",
|
||||
ConstLabels: opts.ConstLabels,
|
||||
},
|
||||
instLabels,
|
||||
)).(*CounterVec)
|
||||
|
||||
opts.Name = "request_duration_microseconds"
|
||||
opts.Help = "The HTTP request latencies in microseconds."
|
||||
reqDur := MustRegisterOrGet(NewSummary(opts)).(Summary)
|
||||
|
||||
opts.Name = "request_size_bytes"
|
||||
opts.Help = "The HTTP request sizes in bytes."
|
||||
MustRegisterOrGet(NewSummary(opts))
|
||||
|
||||
opts.Name = "response_size_bytes"
|
||||
opts.Help = "The HTTP response sizes in bytes."
|
||||
MustRegisterOrGet(NewSummary(opts))
|
||||
|
||||
reqCnt.Reset()
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
req := &http.Request{
|
||||
Method: "GET",
|
||||
}
|
||||
|
||||
hndlr.ServeHTTP(resp, req)
|
||||
|
||||
if resp.Code != http.StatusTeapot {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusTeapot, resp.Code)
|
||||
}
|
||||
if string(resp.Body.Bytes()) != "Howdy there!" {
|
||||
t.Fatalf("expected body %s, got %s", "Howdy there!", string(resp.Body.Bytes()))
|
||||
}
|
||||
|
||||
out := &dto.Metric{}
|
||||
reqDur.Write(out)
|
||||
if want, got := "test-handler", out.Label[0].GetValue(); want != got {
|
||||
t.Errorf("want label value %q in reqDur, got %q", want, got)
|
||||
}
|
||||
if want, got := uint64(1), out.Summary.GetSampleCount(); want != got {
|
||||
t.Errorf("want sample count %d in reqDur, got %d", want, got)
|
||||
}
|
||||
|
||||
out.Reset()
|
||||
if want, got := 1, len(reqCnt.children); want != got {
|
||||
t.Errorf("want %d children in reqCnt, got %d", want, got)
|
||||
}
|
||||
cnt, err := reqCnt.GetMetricWithLabelValues("get", "418")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cnt.Write(out)
|
||||
if want, got := "418", out.Label[0].GetValue(); want != got {
|
||||
t.Errorf("want label value %q in reqCnt, got %q", want, got)
|
||||
}
|
||||
if want, got := "test-handler", out.Label[1].GetValue(); want != got {
|
||||
t.Errorf("want label value %q in reqCnt, got %q", want, got)
|
||||
}
|
||||
if want, got := "get", out.Label[2].GetValue(); want != got {
|
||||
t.Errorf("want label value %q in reqCnt, got %q", want, got)
|
||||
}
|
||||
if out.Counter == nil {
|
||||
t.Fatal("expected non-nil counter in reqCnt")
|
||||
}
|
||||
if want, got := 1., out.Counter.GetValue(); want != got {
|
||||
t.Errorf("want reqCnt of %f, got %f", want, got)
|
||||
}
|
||||
}
|
35
gateway/vendor/github.com/prometheus/client_golang/prometheus/metric_test.go
generated
vendored
Normal file
35
gateway/vendor/github.com/prometheus/client_golang/prometheus/metric_test.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBuildFQName(t *testing.T) {
|
||||
scenarios := []struct{ namespace, subsystem, name, result string }{
|
||||
{"a", "b", "c", "a_b_c"},
|
||||
{"", "b", "c", "b_c"},
|
||||
{"a", "", "c", "a_c"},
|
||||
{"", "", "c", "c"},
|
||||
{"a", "b", "", ""},
|
||||
{"a", "", "", ""},
|
||||
{"", "b", "", ""},
|
||||
{" ", "", "", ""},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
if want, got := s.result, BuildFQName(s.namespace, s.subsystem, s.name); want != got {
|
||||
t.Errorf("%d. want %s, got %s", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
50
gateway/vendor/github.com/prometheus/client_golang/prometheus/observer.go
generated
vendored
50
gateway/vendor/github.com/prometheus/client_golang/prometheus/observer.go
generated
vendored
@ -1,50 +0,0 @@
|
||||
// Copyright 2017 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
// Observer is the interface that wraps the Observe method, which is used by
|
||||
// Histogram and Summary to add observations.
|
||||
type Observer interface {
|
||||
Observe(float64)
|
||||
}
|
||||
|
||||
// The ObserverFunc type is an adapter to allow the use of ordinary
|
||||
// functions as Observers. If f is a function with the appropriate
|
||||
// signature, ObserverFunc(f) is an Observer that calls f.
|
||||
//
|
||||
// This adapter is usually used in connection with the Timer type, and there are
|
||||
// two general use cases:
|
||||
//
|
||||
// The most common one is to use a Gauge as the Observer for a Timer.
|
||||
// See the "Gauge" Timer example.
|
||||
//
|
||||
// The more advanced use case is to create a function that dynamically decides
|
||||
// which Observer to use for observing the duration. See the "Complex" Timer
|
||||
// example.
|
||||
type ObserverFunc func(float64)
|
||||
|
||||
// Observe calls f(value). It implements Observer.
|
||||
func (f ObserverFunc) Observe(value float64) {
|
||||
f(value)
|
||||
}
|
||||
|
||||
// ObserverVec is an interface implemented by `HistogramVec` and `SummaryVec`.
|
||||
type ObserverVec interface {
|
||||
GetMetricWith(Labels) (Observer, error)
|
||||
GetMetricWithLabelValues(lvs ...string) (Observer, error)
|
||||
With(Labels) Observer
|
||||
WithLabelValues(...string) Observer
|
||||
|
||||
Collector
|
||||
}
|
104
gateway/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
104
gateway/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
generated
vendored
@ -19,10 +19,10 @@ type processCollector struct {
|
||||
pid int
|
||||
collectFn func(chan<- Metric)
|
||||
pidFn func() (int, error)
|
||||
cpuTotal *Desc
|
||||
openFDs, maxFDs *Desc
|
||||
vsize, rss *Desc
|
||||
startTime *Desc
|
||||
cpuTotal Counter
|
||||
openFDs, maxFDs Gauge
|
||||
vsize, rss Gauge
|
||||
startTime Gauge
|
||||
}
|
||||
|
||||
// NewProcessCollector returns a collector which exports the current state of
|
||||
@ -44,45 +44,40 @@ func NewProcessCollectorPIDFn(
|
||||
pidFn func() (int, error),
|
||||
namespace string,
|
||||
) Collector {
|
||||
ns := ""
|
||||
if len(namespace) > 0 {
|
||||
ns = namespace + "_"
|
||||
}
|
||||
|
||||
c := processCollector{
|
||||
pidFn: pidFn,
|
||||
collectFn: func(chan<- Metric) {},
|
||||
|
||||
cpuTotal: NewDesc(
|
||||
ns+"process_cpu_seconds_total",
|
||||
"Total user and system CPU time spent in seconds.",
|
||||
nil, nil,
|
||||
),
|
||||
openFDs: NewDesc(
|
||||
ns+"process_open_fds",
|
||||
"Number of open file descriptors.",
|
||||
nil, nil,
|
||||
),
|
||||
maxFDs: NewDesc(
|
||||
ns+"process_max_fds",
|
||||
"Maximum number of open file descriptors.",
|
||||
nil, nil,
|
||||
),
|
||||
vsize: NewDesc(
|
||||
ns+"process_virtual_memory_bytes",
|
||||
"Virtual memory size in bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
rss: NewDesc(
|
||||
ns+"process_resident_memory_bytes",
|
||||
"Resident memory size in bytes.",
|
||||
nil, nil,
|
||||
),
|
||||
startTime: NewDesc(
|
||||
ns+"process_start_time_seconds",
|
||||
"Start time of the process since unix epoch in seconds.",
|
||||
nil, nil,
|
||||
),
|
||||
cpuTotal: NewCounter(CounterOpts{
|
||||
Namespace: namespace,
|
||||
Name: "process_cpu_seconds_total",
|
||||
Help: "Total user and system CPU time spent in seconds.",
|
||||
}),
|
||||
openFDs: NewGauge(GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: "process_open_fds",
|
||||
Help: "Number of open file descriptors.",
|
||||
}),
|
||||
maxFDs: NewGauge(GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: "process_max_fds",
|
||||
Help: "Maximum number of open file descriptors.",
|
||||
}),
|
||||
vsize: NewGauge(GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: "process_virtual_memory_bytes",
|
||||
Help: "Virtual memory size in bytes.",
|
||||
}),
|
||||
rss: NewGauge(GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: "process_resident_memory_bytes",
|
||||
Help: "Resident memory size in bytes.",
|
||||
}),
|
||||
startTime: NewGauge(GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Name: "process_start_time_seconds",
|
||||
Help: "Start time of the process since unix epoch in seconds.",
|
||||
}),
|
||||
}
|
||||
|
||||
// Set up process metric collection if supported by the runtime.
|
||||
@ -95,12 +90,12 @@ func NewProcessCollectorPIDFn(
|
||||
|
||||
// Describe returns all descriptions of the collector.
|
||||
func (c *processCollector) Describe(ch chan<- *Desc) {
|
||||
ch <- c.cpuTotal
|
||||
ch <- c.openFDs
|
||||
ch <- c.maxFDs
|
||||
ch <- c.vsize
|
||||
ch <- c.rss
|
||||
ch <- c.startTime
|
||||
ch <- c.cpuTotal.Desc()
|
||||
ch <- c.openFDs.Desc()
|
||||
ch <- c.maxFDs.Desc()
|
||||
ch <- c.vsize.Desc()
|
||||
ch <- c.rss.Desc()
|
||||
ch <- c.startTime.Desc()
|
||||
}
|
||||
|
||||
// Collect returns the current state of all metrics of the collector.
|
||||
@ -122,19 +117,26 @@ func (c *processCollector) processCollect(ch chan<- Metric) {
|
||||
}
|
||||
|
||||
if stat, err := p.NewStat(); err == nil {
|
||||
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime())
|
||||
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory()))
|
||||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory()))
|
||||
c.cpuTotal.Set(stat.CPUTime())
|
||||
ch <- c.cpuTotal
|
||||
c.vsize.Set(float64(stat.VirtualMemory()))
|
||||
ch <- c.vsize
|
||||
c.rss.Set(float64(stat.ResidentMemory()))
|
||||
ch <- c.rss
|
||||
|
||||
if startTime, err := stat.StartTime(); err == nil {
|
||||
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime)
|
||||
c.startTime.Set(startTime)
|
||||
ch <- c.startTime
|
||||
}
|
||||
}
|
||||
|
||||
if fds, err := p.FileDescriptorsLen(); err == nil {
|
||||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds))
|
||||
c.openFDs.Set(float64(fds))
|
||||
ch <- c.openFDs
|
||||
}
|
||||
|
||||
if limits, err := p.NewLimits(); err == nil {
|
||||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles))
|
||||
c.maxFDs.Set(float64(limits.OpenFiles))
|
||||
ch <- c.maxFDs
|
||||
}
|
||||
}
|
||||
|
58
gateway/vendor/github.com/prometheus/client_golang/prometheus/process_collector_test.go
generated
vendored
Normal file
58
gateway/vendor/github.com/prometheus/client_golang/prometheus/process_collector_test.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
"github.com/prometheus/procfs"
|
||||
)
|
||||
|
||||
func TestProcessCollector(t *testing.T) {
|
||||
if _, err := procfs.Self(); err != nil {
|
||||
t.Skipf("skipping TestProcessCollector, procfs not available: %s", err)
|
||||
}
|
||||
|
||||
registry := NewRegistry()
|
||||
if err := registry.Register(NewProcessCollector(os.Getpid(), "")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := registry.Register(NewProcessCollectorPIDFn(
|
||||
func() (int, error) { return os.Getpid(), nil }, "foobar"),
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mfs, err := registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, mf := range mfs {
|
||||
if _, err := expfmt.MetricFamilyToText(&buf, mf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, re := range []*regexp.Regexp{
|
||||
regexp.MustCompile("process_cpu_seconds_total [0-9]"),
|
||||
regexp.MustCompile("process_max_fds [1-9]"),
|
||||
regexp.MustCompile("process_open_fds [1-9]"),
|
||||
regexp.MustCompile("process_virtual_memory_bytes [1-9]"),
|
||||
regexp.MustCompile("process_resident_memory_bytes [1-9]"),
|
||||
regexp.MustCompile("process_start_time_seconds [0-9.]{10,}"),
|
||||
regexp.MustCompile("foobar_process_cpu_seconds_total [0-9]"),
|
||||
regexp.MustCompile("foobar_process_max_fds [1-9]"),
|
||||
regexp.MustCompile("foobar_process_open_fds [1-9]"),
|
||||
regexp.MustCompile("foobar_process_virtual_memory_bytes [1-9]"),
|
||||
regexp.MustCompile("foobar_process_resident_memory_bytes [1-9]"),
|
||||
regexp.MustCompile("foobar_process_start_time_seconds [0-9.]{10,}"),
|
||||
} {
|
||||
if !re.Match(buf.Bytes()) {
|
||||
t.Errorf("want body to match %s\n%s", re, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
201
gateway/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
generated
vendored
Normal file
201
gateway/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright 2016 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
// Package promhttp contains functions to create http.Handler instances to
|
||||
// expose Prometheus metrics via HTTP. In later versions of this package, it
|
||||
// will also contain tooling to instrument instances of http.Handler and
|
||||
// http.RoundTripper.
|
||||
//
|
||||
// promhttp.Handler acts on the prometheus.DefaultGatherer. With HandlerFor,
|
||||
// you can create a handler for a custom registry or anything that implements
|
||||
// the Gatherer interface. It also allows to create handlers that act
|
||||
// differently on errors or allow to log errors.
|
||||
package promhttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
contentTypeHeader = "Content-Type"
|
||||
contentLengthHeader = "Content-Length"
|
||||
contentEncodingHeader = "Content-Encoding"
|
||||
acceptEncodingHeader = "Accept-Encoding"
|
||||
)
|
||||
|
||||
var bufPool sync.Pool
|
||||
|
||||
func getBuf() *bytes.Buffer {
|
||||
buf := bufPool.Get()
|
||||
if buf == nil {
|
||||
return &bytes.Buffer{}
|
||||
}
|
||||
return buf.(*bytes.Buffer)
|
||||
}
|
||||
|
||||
func giveBuf(buf *bytes.Buffer) {
|
||||
buf.Reset()
|
||||
bufPool.Put(buf)
|
||||
}
|
||||
|
||||
// Handler returns an HTTP handler for the prometheus.DefaultGatherer. The
|
||||
// Handler uses the default HandlerOpts, i.e. report the first error as an HTTP
|
||||
// error, no error logging, and compression if requested by the client.
|
||||
//
|
||||
// If you want to create a Handler for the DefaultGatherer with different
|
||||
// HandlerOpts, create it with HandlerFor with prometheus.DefaultGatherer and
|
||||
// your desired HandlerOpts.
|
||||
func Handler() http.Handler {
|
||||
return HandlerFor(prometheus.DefaultGatherer, HandlerOpts{})
|
||||
}
|
||||
|
||||
// HandlerFor returns an http.Handler for the provided Gatherer. The behavior
|
||||
// of the Handler is defined by the provided HandlerOpts.
|
||||
func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
mfs, err := reg.Gather()
|
||||
if err != nil {
|
||||
if opts.ErrorLog != nil {
|
||||
opts.ErrorLog.Println("error gathering metrics:", err)
|
||||
}
|
||||
switch opts.ErrorHandling {
|
||||
case PanicOnError:
|
||||
panic(err)
|
||||
case ContinueOnError:
|
||||
if len(mfs) == 0 {
|
||||
http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
case HTTPErrorOnError:
|
||||
http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
contentType := expfmt.Negotiate(req.Header)
|
||||
buf := getBuf()
|
||||
defer giveBuf(buf)
|
||||
writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
|
||||
enc := expfmt.NewEncoder(writer, contentType)
|
||||
var lastErr error
|
||||
for _, mf := range mfs {
|
||||
if err := enc.Encode(mf); err != nil {
|
||||
lastErr = err
|
||||
if opts.ErrorLog != nil {
|
||||
opts.ErrorLog.Println("error encoding metric family:", err)
|
||||
}
|
||||
switch opts.ErrorHandling {
|
||||
case PanicOnError:
|
||||
panic(err)
|
||||
case ContinueOnError:
|
||||
// Handled later.
|
||||
case HTTPErrorOnError:
|
||||
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if closer, ok := writer.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
if lastErr != nil && buf.Len() == 0 {
|
||||
http.Error(w, "No metrics encoded, last error:\n\n"+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
header := w.Header()
|
||||
header.Set(contentTypeHeader, string(contentType))
|
||||
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
|
||||
if encoding != "" {
|
||||
header.Set(contentEncodingHeader, encoding)
|
||||
}
|
||||
w.Write(buf.Bytes())
|
||||
// TODO(beorn7): Consider streaming serving of metrics.
|
||||
})
|
||||
}
|
||||
|
||||
// HandlerErrorHandling defines how a Handler serving metrics will handle
|
||||
// errors.
|
||||
type HandlerErrorHandling int
|
||||
|
||||
// These constants cause handlers serving metrics to behave as described if
|
||||
// errors are encountered.
|
||||
const (
|
||||
// Serve an HTTP status code 500 upon the first error
|
||||
// encountered. Report the error message in the body.
|
||||
HTTPErrorOnError HandlerErrorHandling = iota
|
||||
// Ignore errors and try to serve as many metrics as possible. However,
|
||||
// if no metrics can be served, serve an HTTP status code 500 and the
|
||||
// last error message in the body. Only use this in deliberate "best
|
||||
// effort" metrics collection scenarios. It is recommended to at least
|
||||
// log errors (by providing an ErrorLog in HandlerOpts) to not mask
|
||||
// errors completely.
|
||||
ContinueOnError
|
||||
// Panic upon the first error encountered (useful for "crash only" apps).
|
||||
PanicOnError
|
||||
)
|
||||
|
||||
// Logger is the minimal interface HandlerOpts needs for logging. Note that
|
||||
// log.Logger from the standard library implements this interface, and it is
|
||||
// easy to implement by custom loggers, if they don't do so already anyway.
|
||||
type Logger interface {
|
||||
Println(v ...interface{})
|
||||
}
|
||||
|
||||
// HandlerOpts specifies options how to serve metrics via an http.Handler. The
|
||||
// zero value of HandlerOpts is a reasonable default.
|
||||
type HandlerOpts struct {
|
||||
// ErrorLog specifies an optional logger for errors collecting and
|
||||
// serving metrics. If nil, errors are not logged at all.
|
||||
ErrorLog Logger
|
||||
// ErrorHandling defines how errors are handled. Note that errors are
|
||||
// logged regardless of the configured ErrorHandling provided ErrorLog
|
||||
// is not nil.
|
||||
ErrorHandling HandlerErrorHandling
|
||||
// If DisableCompression is true, the handler will never compress the
|
||||
// response, even if requested by the client.
|
||||
DisableCompression bool
|
||||
}
|
||||
|
||||
// decorateWriter wraps a writer to handle gzip compression if requested. It
|
||||
// returns the decorated writer and the appropriate "Content-Encoding" header
|
||||
// (which is empty if no compression is enabled).
|
||||
func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) {
|
||||
if compressionDisabled {
|
||||
return writer, ""
|
||||
}
|
||||
header := request.Header.Get(acceptEncodingHeader)
|
||||
parts := strings.Split(header, ",")
|
||||
for _, part := range parts {
|
||||
part := strings.TrimSpace(part)
|
||||
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
|
||||
return gzip.NewWriter(writer), "gzip"
|
||||
}
|
||||
}
|
||||
return writer, ""
|
||||
}
|
137
gateway/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
generated
vendored
Normal file
137
gateway/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright 2016 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package promhttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type errorCollector struct{}
|
||||
|
||||
func (e errorCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- prometheus.NewDesc("invalid_metric", "not helpful", nil, nil)
|
||||
}
|
||||
|
||||
func (e errorCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc("invalid_metric", "not helpful", nil, nil),
|
||||
errors.New("collect error"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestHandlerErrorHandling(t *testing.T) {
|
||||
|
||||
// Create a registry that collects a MetricFamily with two elements,
|
||||
// another with one, and reports an error.
|
||||
reg := prometheus.NewRegistry()
|
||||
|
||||
cnt := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "the_count",
|
||||
Help: "Ah-ah-ah! Thunder and lightning!",
|
||||
})
|
||||
reg.MustRegister(cnt)
|
||||
|
||||
cntVec := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "name",
|
||||
Help: "docstring",
|
||||
ConstLabels: prometheus.Labels{"constname": "constvalue"},
|
||||
},
|
||||
[]string{"labelname"},
|
||||
)
|
||||
cntVec.WithLabelValues("val1").Inc()
|
||||
cntVec.WithLabelValues("val2").Inc()
|
||||
reg.MustRegister(cntVec)
|
||||
|
||||
reg.MustRegister(errorCollector{})
|
||||
|
||||
logBuf := &bytes.Buffer{}
|
||||
logger := log.New(logBuf, "", 0)
|
||||
|
||||
writer := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/", nil)
|
||||
request.Header.Add("Accept", "test/plain")
|
||||
|
||||
errorHandler := HandlerFor(reg, HandlerOpts{
|
||||
ErrorLog: logger,
|
||||
ErrorHandling: HTTPErrorOnError,
|
||||
})
|
||||
continueHandler := HandlerFor(reg, HandlerOpts{
|
||||
ErrorLog: logger,
|
||||
ErrorHandling: ContinueOnError,
|
||||
})
|
||||
panicHandler := HandlerFor(reg, HandlerOpts{
|
||||
ErrorLog: logger,
|
||||
ErrorHandling: PanicOnError,
|
||||
})
|
||||
wantMsg := `error gathering metrics: error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: []}: collect error
|
||||
`
|
||||
wantErrorBody := `An error has occurred during metrics gathering:
|
||||
|
||||
error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: []}: collect error
|
||||
`
|
||||
wantOKBody := `# HELP name docstring
|
||||
# TYPE name counter
|
||||
name{constname="constvalue",labelname="val1"} 1
|
||||
name{constname="constvalue",labelname="val2"} 1
|
||||
# HELP the_count Ah-ah-ah! Thunder and lightning!
|
||||
# TYPE the_count counter
|
||||
the_count 0
|
||||
`
|
||||
|
||||
errorHandler.ServeHTTP(writer, request)
|
||||
if got, want := writer.Code, http.StatusInternalServerError; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
if got := logBuf.String(); got != wantMsg {
|
||||
t.Errorf("got log message:\n%s\nwant log mesage:\n%s\n", got, wantMsg)
|
||||
}
|
||||
if got := writer.Body.String(); got != wantErrorBody {
|
||||
t.Errorf("got body:\n%s\nwant body:\n%s\n", got, wantErrorBody)
|
||||
}
|
||||
logBuf.Reset()
|
||||
writer.Body.Reset()
|
||||
writer.Code = http.StatusOK
|
||||
|
||||
continueHandler.ServeHTTP(writer, request)
|
||||
if got, want := writer.Code, http.StatusOK; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
if got := logBuf.String(); got != wantMsg {
|
||||
t.Errorf("got log message %q, want %q", got, wantMsg)
|
||||
}
|
||||
if got := writer.Body.String(); got != wantOKBody {
|
||||
t.Errorf("got body %q, want %q", got, wantOKBody)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err == nil {
|
||||
t.Error("expected panic from panicHandler")
|
||||
}
|
||||
}()
|
||||
panicHandler.ServeHTTP(writer, request)
|
||||
}
|
56
gateway/vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
generated
vendored
Normal file
56
gateway/vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2016 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/push"
|
||||
)
|
||||
|
||||
func ExampleCollectors() {
|
||||
completionTime := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "db_backup_last_completion_timestamp_seconds",
|
||||
Help: "The timestamp of the last succesful completion of a DB backup.",
|
||||
})
|
||||
completionTime.Set(float64(time.Now().Unix()))
|
||||
if err := push.Collectors(
|
||||
"db_backup", push.HostnameGroupingKey(),
|
||||
"http://pushgateway:9091",
|
||||
completionTime,
|
||||
); err != nil {
|
||||
fmt.Println("Could not push completion time to Pushgateway:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRegistry() {
|
||||
registry := prometheus.NewRegistry()
|
||||
|
||||
completionTime := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "db_backup_last_completion_timestamp_seconds",
|
||||
Help: "The timestamp of the last succesful completion of a DB backup.",
|
||||
})
|
||||
registry.MustRegister(completionTime)
|
||||
|
||||
completionTime.Set(float64(time.Now().Unix()))
|
||||
if err := push.FromGatherer(
|
||||
"db_backup", push.HostnameGroupingKey(),
|
||||
"http://pushgateway:9091",
|
||||
registry,
|
||||
); err != nil {
|
||||
fmt.Println("Could not push completion time to Pushgateway:", err)
|
||||
}
|
||||
}
|
172
gateway/vendor/github.com/prometheus/client_golang/prometheus/push/push.go
generated
vendored
Normal file
172
gateway/vendor/github.com/prometheus/client_golang/prometheus/push/push.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
// 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.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
// Package push provides functions to push metrics to a Pushgateway. The metrics
|
||||
// to push are either collected from a provided registry, or from explicitly
|
||||
// listed collectors.
|
||||
//
|
||||
// See the documentation of the Pushgateway to understand the meaning of the
|
||||
// grouping parameters and the differences between push.Registry and
|
||||
// push.Collectors on the one hand and push.AddRegistry and push.AddCollectors
|
||||
// on the other hand: https://github.com/prometheus/pushgateway
|
||||
package push
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const contentTypeHeader = "Content-Type"
|
||||
|
||||
// FromGatherer triggers a metric collection by the provided Gatherer (which is
|
||||
// usually implemented by a prometheus.Registry) and pushes all gathered metrics
|
||||
// to the Pushgateway specified by url, using the provided job name and the
|
||||
// (optional) further grouping labels (the grouping map may be nil). See the
|
||||
// Pushgateway documentation for detailed implications of the job and other
|
||||
// grouping labels. Neither the job name nor any grouping label value may
|
||||
// contain a "/". The metrics pushed must not contain a job label of their own
|
||||
// nor any of the grouping labels.
|
||||
//
|
||||
// You can use just host:port or ip:port as url, in which case 'http://' is
|
||||
// added automatically. You can also include the schema in the URL. However, do
|
||||
// not include the '/metrics/jobs/...' part.
|
||||
//
|
||||
// Note that all previously pushed metrics with the same job and other grouping
|
||||
// labels will be replaced with the metrics pushed by this call. (It uses HTTP
|
||||
// method 'PUT' to push to the Pushgateway.)
|
||||
func FromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
|
||||
return push(job, grouping, url, g, "PUT")
|
||||
}
|
||||
|
||||
// AddFromGatherer works like FromGatherer, but only previously pushed metrics
|
||||
// with the same name (and the same job and other grouping labels) will be
|
||||
// replaced. (It uses HTTP method 'POST' to push to the Pushgateway.)
|
||||
func AddFromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
|
||||
return push(job, grouping, url, g, "POST")
|
||||
}
|
||||
|
||||
func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
|
||||
if !strings.Contains(pushURL, "://") {
|
||||
pushURL = "http://" + pushURL
|
||||
}
|
||||
if strings.HasSuffix(pushURL, "/") {
|
||||
pushURL = pushURL[:len(pushURL)-1]
|
||||
}
|
||||
|
||||
if strings.Contains(job, "/") {
|
||||
return fmt.Errorf("job contains '/': %s", job)
|
||||
}
|
||||
urlComponents := []string{url.QueryEscape(job)}
|
||||
for ln, lv := range grouping {
|
||||
if !model.LabelNameRE.MatchString(ln) {
|
||||
return fmt.Errorf("grouping label has invalid name: %s", ln)
|
||||
}
|
||||
if strings.Contains(lv, "/") {
|
||||
return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
|
||||
}
|
||||
urlComponents = append(urlComponents, ln, lv)
|
||||
}
|
||||
pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))
|
||||
|
||||
mfs, err := g.Gather()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
|
||||
// Check for pre-existing grouping labels:
|
||||
for _, mf := range mfs {
|
||||
for _, m := range mf.GetMetric() {
|
||||
for _, l := range m.GetLabel() {
|
||||
if l.GetName() == "job" {
|
||||
return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
|
||||
}
|
||||
if _, ok := grouping[l.GetName()]; ok {
|
||||
return fmt.Errorf(
|
||||
"pushed metric %s (%s) already contains grouping label %s",
|
||||
mf.GetName(), m, l.GetName(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
enc.Encode(mf)
|
||||
}
|
||||
req, err := http.NewRequest(method, pushURL, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 202 {
|
||||
body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only.
|
||||
return fmt.Errorf("unexpected status code %d while pushing to %s: %s", resp.StatusCode, pushURL, body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collectors works like FromGatherer, but it does not use a Gatherer. Instead,
|
||||
// it collects from the provided collectors directly. It is a convenient way to
|
||||
// push only a few metrics.
|
||||
func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
|
||||
return pushCollectors(job, grouping, url, "PUT", collectors...)
|
||||
}
|
||||
|
||||
// AddCollectors works like AddFromGatherer, but it does not use a Gatherer.
|
||||
// Instead, it collects from the provided collectors directly. It is a
|
||||
// convenient way to push only a few metrics.
|
||||
func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
|
||||
return pushCollectors(job, grouping, url, "POST", collectors...)
|
||||
}
|
||||
|
||||
func pushCollectors(job string, grouping map[string]string, url, method string, collectors ...prometheus.Collector) error {
|
||||
r := prometheus.NewRegistry()
|
||||
for _, collector := range collectors {
|
||||
if err := r.Register(collector); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return push(job, grouping, url, r, method)
|
||||
}
|
||||
|
||||
// HostnameGroupingKey returns a label map with the only entry
|
||||
// {instance="<hostname>"}. This can be conveniently used as the grouping
|
||||
// parameter if metrics should be pushed with the hostname as label. The
|
||||
// returned map is created upon each call so that the caller is free to add more
|
||||
// labels to the map.
|
||||
func HostnameGroupingKey() map[string]string {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return map[string]string{"instance": "unknown"}
|
||||
}
|
||||
return map[string]string{"instance": hostname}
|
||||
}
|
176
gateway/vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
generated
vendored
Normal file
176
gateway/vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright 2016 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func TestPush(t *testing.T) {
|
||||
|
||||
var (
|
||||
lastMethod string
|
||||
lastBody []byte
|
||||
lastPath string
|
||||
)
|
||||
|
||||
host, err := os.Hostname()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Fake a Pushgateway that always responds with 202.
|
||||
pgwOK := httptest.NewServer(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
lastMethod = r.Method
|
||||
var err error
|
||||
lastBody, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lastPath = r.URL.EscapedPath()
|
||||
w.Header().Set("Content-Type", `text/plain; charset=utf-8`)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}),
|
||||
)
|
||||
defer pgwOK.Close()
|
||||
|
||||
// Fake a Pushgateway that always responds with 500.
|
||||
pgwErr := httptest.NewServer(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "fake error", http.StatusInternalServerError)
|
||||
}),
|
||||
)
|
||||
defer pgwErr.Close()
|
||||
|
||||
metric1 := prometheus.NewCounter(prometheus.CounterOpts{
|
||||
Name: "testname1",
|
||||
Help: "testhelp1",
|
||||
})
|
||||
metric2 := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "testname2",
|
||||
Help: "testhelp2",
|
||||
ConstLabels: prometheus.Labels{"foo": "bar", "dings": "bums"},
|
||||
})
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
reg.MustRegister(metric1)
|
||||
reg.MustRegister(metric2)
|
||||
|
||||
mfs, err := reg.Gather()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
|
||||
|
||||
for _, mf := range mfs {
|
||||
if err := enc.Encode(mf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
wantBody := buf.Bytes()
|
||||
|
||||
// PushCollectors, all good.
|
||||
if err := Collectors("testjob", HostnameGroupingKey(), pgwOK.URL, metric1, metric2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "PUT" {
|
||||
t.Error("want method PUT for PushCollectors, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
if lastPath != "/metrics/job/testjob/instance/"+host {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// PushAddCollectors, with nil grouping, all good.
|
||||
if err := AddCollectors("testjob", nil, pgwOK.URL, metric1, metric2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "POST" {
|
||||
t.Error("want method POST for PushAddCollectors, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
if lastPath != "/metrics/job/testjob" {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// PushCollectors with a broken PGW.
|
||||
if err := Collectors("testjob", nil, pgwErr.URL, metric1, metric2); err == nil {
|
||||
t.Error("push to broken Pushgateway succeeded")
|
||||
} else {
|
||||
if got, want := err.Error(), "unexpected status code 500 while pushing to "+pgwErr.URL+"/metrics/job/testjob: fake error\n"; got != want {
|
||||
t.Errorf("got error %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// PushCollectors with invalid grouping or job.
|
||||
if err := Collectors("testjob", map[string]string{"foo": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
|
||||
t.Error("push with grouping contained in metrics succeeded")
|
||||
}
|
||||
if err := Collectors("test/job", nil, pgwErr.URL, metric1, metric2); err == nil {
|
||||
t.Error("push with invalid job value succeeded")
|
||||
}
|
||||
if err := Collectors("testjob", map[string]string{"foo/bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
|
||||
t.Error("push with invalid grouping succeeded")
|
||||
}
|
||||
if err := Collectors("testjob", map[string]string{"foo-bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
|
||||
t.Error("push with invalid grouping succeeded")
|
||||
}
|
||||
|
||||
// Push registry, all good.
|
||||
if err := FromGatherer("testjob", HostnameGroupingKey(), pgwOK.URL, reg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "PUT" {
|
||||
t.Error("want method PUT for Push, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
|
||||
// PushAdd registry, all good.
|
||||
if err := AddFromGatherer("testjob", map[string]string{"a": "x", "b": "y"}, pgwOK.URL, reg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "POST" {
|
||||
t.Error("want method POSTT for PushAdd, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
if lastPath != "/metrics/job/testjob/a/x/b/y" && lastPath != "/metrics/job/testjob/b/y/a/x" {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
}
|
59
gateway/vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
59
gateway/vendor/github.com/prometheus/client_golang/prometheus/registry.go
generated
vendored
@ -80,7 +80,7 @@ func NewPedanticRegistry() *Registry {
|
||||
|
||||
// Registerer is the interface for the part of a registry in charge of
|
||||
// registering and unregistering. Users of custom registries should use
|
||||
// Registerer as type for registration purposes (rather than the Registry type
|
||||
// Registerer as type for registration purposes (rather then the Registry type
|
||||
// directly). In that way, they are free to use custom Registerer implementation
|
||||
// (e.g. for testing purposes).
|
||||
type Registerer interface {
|
||||
@ -152,6 +152,38 @@ func MustRegister(cs ...Collector) {
|
||||
DefaultRegisterer.MustRegister(cs...)
|
||||
}
|
||||
|
||||
// RegisterOrGet registers the provided Collector with the DefaultRegisterer and
|
||||
// returns the Collector, unless an equal Collector was registered before, in
|
||||
// which case that Collector is returned.
|
||||
//
|
||||
// Deprecated: RegisterOrGet is merely a convenience function for the
|
||||
// implementation as described in the documentation for
|
||||
// AlreadyRegisteredError. As the use case is relatively rare, this function
|
||||
// will be removed in a future version of this package to clean up the
|
||||
// namespace.
|
||||
func RegisterOrGet(c Collector) (Collector, error) {
|
||||
if err := Register(c); err != nil {
|
||||
if are, ok := err.(AlreadyRegisteredError); ok {
|
||||
return are.ExistingCollector, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// MustRegisterOrGet behaves like RegisterOrGet but panics instead of returning
|
||||
// an error.
|
||||
//
|
||||
// Deprecated: This is deprecated for the same reason RegisterOrGet is. See
|
||||
// there for details.
|
||||
func MustRegisterOrGet(c Collector) Collector {
|
||||
c, err := RegisterOrGet(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Unregister removes the registration of the provided Collector from the
|
||||
// DefaultRegisterer.
|
||||
//
|
||||
@ -169,6 +201,25 @@ func (gf GathererFunc) Gather() ([]*dto.MetricFamily, error) {
|
||||
return gf()
|
||||
}
|
||||
|
||||
// SetMetricFamilyInjectionHook replaces the DefaultGatherer with one that
|
||||
// gathers from the previous DefaultGatherers but then merges the MetricFamily
|
||||
// protobufs returned from the provided hook function with the MetricFamily
|
||||
// protobufs returned from the original DefaultGatherer.
|
||||
//
|
||||
// Deprecated: This function manipulates the DefaultGatherer variable. Consider
|
||||
// the implications, i.e. don't do this concurrently with any uses of the
|
||||
// DefaultGatherer. In the rare cases where you need to inject MetricFamily
|
||||
// protobufs directly, it is recommended to use a custom Registry and combine it
|
||||
// with a custom Gatherer using the Gatherers type (see
|
||||
// there). SetMetricFamilyInjectionHook only exists for compatibility reasons
|
||||
// with previous versions of this package.
|
||||
func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) {
|
||||
DefaultGatherer = Gatherers{
|
||||
DefaultGatherer,
|
||||
GathererFunc(func() ([]*dto.MetricFamily, error) { return hook(), nil }),
|
||||
}
|
||||
}
|
||||
|
||||
// AlreadyRegisteredError is returned by the Register method if the Collector to
|
||||
// be registered has already been registered before, or a different Collector
|
||||
// that collects the same metrics has been registered before. Registration fails
|
||||
@ -243,7 +294,7 @@ func (r *Registry) Register(c Collector) error {
|
||||
}()
|
||||
r.mtx.Lock()
|
||||
defer r.mtx.Unlock()
|
||||
// Conduct various tests...
|
||||
// Coduct various tests...
|
||||
for desc := range descChan {
|
||||
|
||||
// Is the descriptor valid at all?
|
||||
@ -396,7 +447,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||
|
||||
// Drain metricChan in case of premature return.
|
||||
defer func() {
|
||||
for range metricChan {
|
||||
for _ = range metricChan {
|
||||
}
|
||||
}()
|
||||
|
||||
@ -632,7 +683,7 @@ func (s metricSorter) Less(i, j int) bool {
|
||||
return s[i].GetTimestampMs() < s[j].GetTimestampMs()
|
||||
}
|
||||
|
||||
// normalizeMetricFamilies returns a MetricFamily slice with empty
|
||||
// normalizeMetricFamilies returns a MetricFamily slice whith empty
|
||||
// MetricFamilies pruned and the remaining MetricFamilies sorted by name within
|
||||
// the slice, with the contained Metrics sorted within each MetricFamily.
|
||||
func normalizeMetricFamilies(metricFamiliesByName map[string]*dto.MetricFamily) []*dto.MetricFamily {
|
||||
|
545
gateway/vendor/github.com/prometheus/client_golang/prometheus/registry_test.go
generated
vendored
Normal file
545
gateway/vendor/github.com/prometheus/client_golang/prometheus/registry_test.go
generated
vendored
Normal file
@ -0,0 +1,545 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package prometheus_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func testHandler(t testing.TB) {
|
||||
|
||||
metricVec := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "name",
|
||||
Help: "docstring",
|
||||
ConstLabels: prometheus.Labels{"constname": "constvalue"},
|
||||
},
|
||||
[]string{"labelname"},
|
||||
)
|
||||
|
||||
metricVec.WithLabelValues("val1").Inc()
|
||||
metricVec.WithLabelValues("val2").Inc()
|
||||
|
||||
externalMetricFamily := &dto.MetricFamily{
|
||||
Name: proto.String("externalname"),
|
||||
Help: proto.String("externaldocstring"),
|
||||
Type: dto.MetricType_COUNTER.Enum(),
|
||||
Metric: []*dto.Metric{
|
||||
{
|
||||
Label: []*dto.LabelPair{
|
||||
{
|
||||
Name: proto.String("externalconstname"),
|
||||
Value: proto.String("externalconstvalue"),
|
||||
},
|
||||
{
|
||||
Name: proto.String("externallabelname"),
|
||||
Value: proto.String("externalval1"),
|
||||
},
|
||||
},
|
||||
Counter: &dto.Counter{
|
||||
Value: proto.Float64(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
externalBuf := &bytes.Buffer{}
|
||||
enc := expfmt.NewEncoder(externalBuf, expfmt.FmtProtoDelim)
|
||||
if err := enc.Encode(externalMetricFamily); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
externalMetricFamilyAsBytes := externalBuf.Bytes()
|
||||
externalMetricFamilyAsText := []byte(`# HELP externalname externaldocstring
|
||||
# TYPE externalname counter
|
||||
externalname{externalconstname="externalconstvalue",externallabelname="externalval1"} 1
|
||||
`)
|
||||
externalMetricFamilyAsProtoText := []byte(`name: "externalname"
|
||||
help: "externaldocstring"
|
||||
type: COUNTER
|
||||
metric: <
|
||||
label: <
|
||||
name: "externalconstname"
|
||||
value: "externalconstvalue"
|
||||
>
|
||||
label: <
|
||||
name: "externallabelname"
|
||||
value: "externalval1"
|
||||
>
|
||||
counter: <
|
||||
value: 1
|
||||
>
|
||||
>
|
||||
|
||||
`)
|
||||
externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric:<label:<name:"externalconstname" value:"externalconstvalue" > label:<name:"externallabelname" value:"externalval1" > counter:<value:1 > >
|
||||
`)
|
||||
|
||||
expectedMetricFamily := &dto.MetricFamily{
|
||||
Name: proto.String("name"),
|
||||
Help: proto.String("docstring"),
|
||||
Type: dto.MetricType_COUNTER.Enum(),
|
||||
Metric: []*dto.Metric{
|
||||
{
|
||||
Label: []*dto.LabelPair{
|
||||
{
|
||||
Name: proto.String("constname"),
|
||||
Value: proto.String("constvalue"),
|
||||
},
|
||||
{
|
||||
Name: proto.String("labelname"),
|
||||
Value: proto.String("val1"),
|
||||
},
|
||||
},
|
||||
Counter: &dto.Counter{
|
||||
Value: proto.Float64(1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: []*dto.LabelPair{
|
||||
{
|
||||
Name: proto.String("constname"),
|
||||
Value: proto.String("constvalue"),
|
||||
},
|
||||
{
|
||||
Name: proto.String("labelname"),
|
||||
Value: proto.String("val2"),
|
||||
},
|
||||
},
|
||||
Counter: &dto.Counter{
|
||||
Value: proto.Float64(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
enc = expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
|
||||
if err := enc.Encode(expectedMetricFamily); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedMetricFamilyAsBytes := buf.Bytes()
|
||||
expectedMetricFamilyAsText := []byte(`# HELP name docstring
|
||||
# TYPE name counter
|
||||
name{constname="constvalue",labelname="val1"} 1
|
||||
name{constname="constvalue",labelname="val2"} 1
|
||||
`)
|
||||
expectedMetricFamilyAsProtoText := []byte(`name: "name"
|
||||
help: "docstring"
|
||||
type: COUNTER
|
||||
metric: <
|
||||
label: <
|
||||
name: "constname"
|
||||
value: "constvalue"
|
||||
>
|
||||
label: <
|
||||
name: "labelname"
|
||||
value: "val1"
|
||||
>
|
||||
counter: <
|
||||
value: 1
|
||||
>
|
||||
>
|
||||
metric: <
|
||||
label: <
|
||||
name: "constname"
|
||||
value: "constvalue"
|
||||
>
|
||||
label: <
|
||||
name: "labelname"
|
||||
value: "val2"
|
||||
>
|
||||
counter: <
|
||||
value: 1
|
||||
>
|
||||
>
|
||||
|
||||
`)
|
||||
expectedMetricFamilyAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val1" > counter:<value:1 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val2" > counter:<value:1 > >
|
||||
`)
|
||||
|
||||
externalMetricFamilyWithSameName := &dto.MetricFamily{
|
||||
Name: proto.String("name"),
|
||||
Help: proto.String("docstring"),
|
||||
Type: dto.MetricType_COUNTER.Enum(),
|
||||
Metric: []*dto.Metric{
|
||||
{
|
||||
Label: []*dto.LabelPair{
|
||||
{
|
||||
Name: proto.String("constname"),
|
||||
Value: proto.String("constvalue"),
|
||||
},
|
||||
{
|
||||
Name: proto.String("labelname"),
|
||||
Value: proto.String("different_val"),
|
||||
},
|
||||
},
|
||||
Counter: &dto.Counter{
|
||||
Value: proto.Float64(42),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedMetricFamilyMergedWithExternalAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"different_val" > counter:<value:42 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val1" > counter:<value:1 > > metric:<label:<name:"constname" value:"constvalue" > label:<name:"labelname" value:"val2" > counter:<value:1 > >
|
||||
`)
|
||||
|
||||
type output struct {
|
||||
headers map[string]string
|
||||
body []byte
|
||||
}
|
||||
|
||||
var scenarios = []struct {
|
||||
headers map[string]string
|
||||
out output
|
||||
collector prometheus.Collector
|
||||
externalMF []*dto.MetricFamily
|
||||
}{
|
||||
{ // 0
|
||||
headers: map[string]string{
|
||||
"Accept": "foo/bar;q=0.2, dings/bums;q=0.8",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: []byte{},
|
||||
},
|
||||
},
|
||||
{ // 1
|
||||
headers: map[string]string{
|
||||
"Accept": "foo/bar;q=0.2, application/quark;q=0.8",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: []byte{},
|
||||
},
|
||||
},
|
||||
{ // 2
|
||||
headers: map[string]string{
|
||||
"Accept": "foo/bar;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.8",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: []byte{},
|
||||
},
|
||||
},
|
||||
{ // 3
|
||||
headers: map[string]string{
|
||||
"Accept": "text/plain;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.8",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||
},
|
||||
body: []byte{},
|
||||
},
|
||||
},
|
||||
{ // 4
|
||||
headers: map[string]string{
|
||||
"Accept": "application/json",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: expectedMetricFamilyAsText,
|
||||
},
|
||||
collector: metricVec,
|
||||
},
|
||||
{ // 5
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||
},
|
||||
body: expectedMetricFamilyAsBytes,
|
||||
},
|
||||
collector: metricVec,
|
||||
},
|
||||
{ // 6
|
||||
headers: map[string]string{
|
||||
"Accept": "application/json",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: externalMetricFamilyAsText,
|
||||
},
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 7
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||
},
|
||||
body: externalMetricFamilyAsBytes,
|
||||
},
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 8
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||
},
|
||||
body: bytes.Join(
|
||||
[][]byte{
|
||||
externalMetricFamilyAsBytes,
|
||||
expectedMetricFamilyAsBytes,
|
||||
},
|
||||
[]byte{},
|
||||
),
|
||||
},
|
||||
collector: metricVec,
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 9
|
||||
headers: map[string]string{
|
||||
"Accept": "text/plain",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: []byte{},
|
||||
},
|
||||
},
|
||||
{ // 10
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: expectedMetricFamilyAsText,
|
||||
},
|
||||
collector: metricVec,
|
||||
},
|
||||
{ // 11
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5;version=0.0.4",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `text/plain; version=0.0.4`,
|
||||
},
|
||||
body: bytes.Join(
|
||||
[][]byte{
|
||||
externalMetricFamilyAsText,
|
||||
expectedMetricFamilyAsText,
|
||||
},
|
||||
[]byte{},
|
||||
),
|
||||
},
|
||||
collector: metricVec,
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 12
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.2, text/plain;q=0.5;version=0.0.2",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
|
||||
},
|
||||
body: bytes.Join(
|
||||
[][]byte{
|
||||
externalMetricFamilyAsBytes,
|
||||
expectedMetricFamilyAsBytes,
|
||||
},
|
||||
[]byte{},
|
||||
),
|
||||
},
|
||||
collector: metricVec,
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 13
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=text;q=0.5, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.4",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`,
|
||||
},
|
||||
body: bytes.Join(
|
||||
[][]byte{
|
||||
externalMetricFamilyAsProtoText,
|
||||
expectedMetricFamilyAsProtoText,
|
||||
},
|
||||
[]byte{},
|
||||
),
|
||||
},
|
||||
collector: metricVec,
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 14
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`,
|
||||
},
|
||||
body: bytes.Join(
|
||||
[][]byte{
|
||||
externalMetricFamilyAsProtoCompactText,
|
||||
expectedMetricFamilyAsProtoCompactText,
|
||||
},
|
||||
[]byte{},
|
||||
),
|
||||
},
|
||||
collector: metricVec,
|
||||
externalMF: []*dto.MetricFamily{externalMetricFamily},
|
||||
},
|
||||
{ // 15
|
||||
headers: map[string]string{
|
||||
"Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text",
|
||||
},
|
||||
out: output{
|
||||
headers: map[string]string{
|
||||
"Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`,
|
||||
},
|
||||
body: bytes.Join(
|
||||
[][]byte{
|
||||
externalMetricFamilyAsProtoCompactText,
|
||||
expectedMetricFamilyMergedWithExternalAsProtoCompactText,
|
||||
},
|
||||
[]byte{},
|
||||
),
|
||||
},
|
||||
collector: metricVec,
|
||||
externalMF: []*dto.MetricFamily{
|
||||
externalMetricFamily,
|
||||
externalMetricFamilyWithSameName,
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, scenario := range scenarios {
|
||||
registry := prometheus.NewPedanticRegistry()
|
||||
gatherer := prometheus.Gatherer(registry)
|
||||
if scenario.externalMF != nil {
|
||||
gatherer = prometheus.Gatherers{
|
||||
registry,
|
||||
prometheus.GathererFunc(func() ([]*dto.MetricFamily, error) {
|
||||
return scenario.externalMF, nil
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
if scenario.collector != nil {
|
||||
registry.Register(scenario.collector)
|
||||
}
|
||||
writer := httptest.NewRecorder()
|
||||
handler := prometheus.InstrumentHandler("prometheus", promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}))
|
||||
request, _ := http.NewRequest("GET", "/", nil)
|
||||
for key, value := range scenario.headers {
|
||||
request.Header.Add(key, value)
|
||||
}
|
||||
handler(writer, request)
|
||||
|
||||
for key, value := range scenario.out.headers {
|
||||
if writer.HeaderMap.Get(key) != value {
|
||||
t.Errorf(
|
||||
"%d. expected %q for header %q, got %q",
|
||||
i, value, key, writer.Header().Get(key),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !bytes.Equal(scenario.out.body, writer.Body.Bytes()) {
|
||||
t.Errorf(
|
||||
"%d. expected body:\n%s\ngot body:\n%s\n",
|
||||
i, scenario.out.body, writer.Body.Bytes(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
testHandler(t)
|
||||
}
|
||||
|
||||
func BenchmarkHandler(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
testHandler(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterWithOrGet(t *testing.T) {
|
||||
// Replace the default registerer just to be sure. This is bad, but this
|
||||
// whole test will go away once RegisterOrGet is removed.
|
||||
oldRegisterer := prometheus.DefaultRegisterer
|
||||
defer func() {
|
||||
prometheus.DefaultRegisterer = oldRegisterer
|
||||
}()
|
||||
prometheus.DefaultRegisterer = prometheus.NewRegistry()
|
||||
original := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "test",
|
||||
Help: "help",
|
||||
},
|
||||
[]string{"foo", "bar"},
|
||||
)
|
||||
equalButNotSame := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "test",
|
||||
Help: "help",
|
||||
},
|
||||
[]string{"foo", "bar"},
|
||||
)
|
||||
if err := prometheus.Register(original); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := prometheus.Register(equalButNotSame); err == nil {
|
||||
t.Fatal("expected error when registringe equal collector")
|
||||
}
|
||||
existing, err := prometheus.RegisterOrGet(equalButNotSame)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if existing != original {
|
||||
t.Error("expected original collector but got something else")
|
||||
}
|
||||
if existing == equalButNotSame {
|
||||
t.Error("expected original callector but got new one")
|
||||
}
|
||||
}
|
86
gateway/vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
86
gateway/vendor/github.com/prometheus/client_golang/prometheus/summary.go
generated
vendored
@ -54,9 +54,6 @@ type Summary interface {
|
||||
}
|
||||
|
||||
// DefObjectives are the default Summary quantile values.
|
||||
//
|
||||
// Deprecated: DefObjectives will not be used as the default objectives in
|
||||
// v0.10 of the library. The default Summary will have no quantiles then.
|
||||
var (
|
||||
DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
|
||||
|
||||
@ -116,15 +113,9 @@ type SummaryOpts struct {
|
||||
ConstLabels Labels
|
||||
|
||||
// Objectives defines the quantile rank estimates with their respective
|
||||
// absolute error. If Objectives[q] = e, then the value reported for q
|
||||
// will be the φ-quantile value for some φ between q-e and q+e. The
|
||||
// default value is DefObjectives. It is used if Objectives is left at
|
||||
// its zero value (i.e. nil). To create a Summary without Objectives,
|
||||
// set it to an empty map (i.e. map[float64]float64{}).
|
||||
//
|
||||
// Deprecated: Note that the current value of DefObjectives is
|
||||
// deprecated. It will be replaced by an empty map in v0.10 of the
|
||||
// library. Please explicitly set Objectives to the desired value.
|
||||
// absolute error. If Objectives[q] = e, then the value reported
|
||||
// for q will be the φ-quantile value for some φ between q-e and q+e.
|
||||
// The default value is DefObjectives.
|
||||
Objectives map[float64]float64
|
||||
|
||||
// MaxAge defines the duration for which an observation stays relevant
|
||||
@ -192,7 +183,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Objectives == nil {
|
||||
if len(opts.Objectives) == 0 {
|
||||
opts.Objectives = DefObjectives
|
||||
}
|
||||
|
||||
@ -399,11 +390,12 @@ func (s quantSort) Less(i, j int) bool {
|
||||
// (e.g. HTTP request latencies, partitioned by status code and method). Create
|
||||
// instances with NewSummaryVec.
|
||||
type SummaryVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
|
||||
// partitioned by the given label names.
|
||||
// partitioned by the given label names. At least one label name must be
|
||||
// provided.
|
||||
func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||
desc := NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
@ -412,60 +404,30 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &SummaryVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
return newSummary(desc, opts, lvs...)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Summary for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Summary is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Summary to only
|
||||
// create the new Summary but leave it at its starting value, a Summary without
|
||||
// any observations.
|
||||
//
|
||||
// Keeping the Summary for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Summary from the SummaryVec. In that case, the
|
||||
// Summary will still exist, but it will not be exported anymore, even if a
|
||||
// Summary with the same label values is created later. See also the CounterVec
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (m *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
metric, err := m.metricVec.getMetricWithLabelValues(lvs...)
|
||||
// GetMetricWithLabelValues replaces the method of the same name in
|
||||
// MetricVec. The difference is that this method returns a Summary and not a
|
||||
// Metric so that no type conversion is required.
|
||||
func (m *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Summary, error) {
|
||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
return metric.(Summary), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Summary for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new Summary is created. Implications of
|
||||
// creating a Summary without using it and keeping the Summary for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc.
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (m *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
metric, err := m.metricVec.getMetricWith(labels)
|
||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
||||
// difference is that this method returns a Summary and not a Metric so that no
|
||||
// type conversion is required.
|
||||
func (m *SummaryVec) GetMetricWith(labels Labels) (Summary, error) {
|
||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
return metric.(Summary), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@ -474,15 +436,15 @@ func (m *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
// GetMetricWithLabelValues would have returned an error. By not returning an
|
||||
// error, WithLabelValues allows shortcuts like
|
||||
// myVec.WithLabelValues("404", "GET").Observe(42.21)
|
||||
func (m *SummaryVec) WithLabelValues(lvs ...string) Observer {
|
||||
return m.metricVec.withLabelValues(lvs...).(Observer)
|
||||
func (m *SummaryVec) WithLabelValues(lvs ...string) Summary {
|
||||
return m.MetricVec.WithLabelValues(lvs...).(Summary)
|
||||
}
|
||||
|
||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||
// returned an error. By not returning an error, With allows shortcuts like
|
||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
|
||||
func (m *SummaryVec) With(labels Labels) Observer {
|
||||
return m.metricVec.with(labels).(Observer)
|
||||
func (m *SummaryVec) With(labels Labels) Summary {
|
||||
return m.MetricVec.With(labels).(Summary)
|
||||
}
|
||||
|
||||
type constSummary struct {
|
||||
|
347
gateway/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
generated
vendored
Normal file
347
gateway/vendor/github.com/prometheus/client_golang/prometheus/summary_test.go
generated
vendored
Normal file
@ -0,0 +1,347 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
func benchmarkSummaryObserve(w int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(w)
|
||||
|
||||
g := new(sync.WaitGroup)
|
||||
g.Add(1)
|
||||
|
||||
s := NewSummary(SummaryOpts{})
|
||||
|
||||
for i := 0; i < w; i++ {
|
||||
go func() {
|
||||
g.Wait()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.Observe(float64(i))
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
g.Done()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkSummaryObserve1(b *testing.B) {
|
||||
benchmarkSummaryObserve(1, b)
|
||||
}
|
||||
|
||||
func BenchmarkSummaryObserve2(b *testing.B) {
|
||||
benchmarkSummaryObserve(2, b)
|
||||
}
|
||||
|
||||
func BenchmarkSummaryObserve4(b *testing.B) {
|
||||
benchmarkSummaryObserve(4, b)
|
||||
}
|
||||
|
||||
func BenchmarkSummaryObserve8(b *testing.B) {
|
||||
benchmarkSummaryObserve(8, b)
|
||||
}
|
||||
|
||||
func benchmarkSummaryWrite(w int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(w)
|
||||
|
||||
g := new(sync.WaitGroup)
|
||||
g.Add(1)
|
||||
|
||||
s := NewSummary(SummaryOpts{})
|
||||
|
||||
for i := 0; i < 1000000; i++ {
|
||||
s.Observe(float64(i))
|
||||
}
|
||||
|
||||
for j := 0; j < w; j++ {
|
||||
outs := make([]dto.Metric, b.N)
|
||||
|
||||
go func(o []dto.Metric) {
|
||||
g.Wait()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.Write(&o[i])
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}(outs)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
g.Done()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkSummaryWrite1(b *testing.B) {
|
||||
benchmarkSummaryWrite(1, b)
|
||||
}
|
||||
|
||||
func BenchmarkSummaryWrite2(b *testing.B) {
|
||||
benchmarkSummaryWrite(2, b)
|
||||
}
|
||||
|
||||
func BenchmarkSummaryWrite4(b *testing.B) {
|
||||
benchmarkSummaryWrite(4, b)
|
||||
}
|
||||
|
||||
func BenchmarkSummaryWrite8(b *testing.B) {
|
||||
benchmarkSummaryWrite(8, b)
|
||||
}
|
||||
|
||||
func TestSummaryConcurrency(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short mode.")
|
||||
}
|
||||
|
||||
rand.Seed(42)
|
||||
|
||||
it := func(n uint32) bool {
|
||||
mutations := int(n%1e4 + 1e4)
|
||||
concLevel := int(n%5 + 1)
|
||||
total := mutations * concLevel
|
||||
|
||||
var start, end sync.WaitGroup
|
||||
start.Add(1)
|
||||
end.Add(concLevel)
|
||||
|
||||
sum := NewSummary(SummaryOpts{
|
||||
Name: "test_summary",
|
||||
Help: "helpless",
|
||||
})
|
||||
|
||||
allVars := make([]float64, total)
|
||||
var sampleSum float64
|
||||
for i := 0; i < concLevel; i++ {
|
||||
vals := make([]float64, mutations)
|
||||
for j := 0; j < mutations; j++ {
|
||||
v := rand.NormFloat64()
|
||||
vals[j] = v
|
||||
allVars[i*mutations+j] = v
|
||||
sampleSum += v
|
||||
}
|
||||
|
||||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for _, v := range vals {
|
||||
sum.Observe(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
}
|
||||
sort.Float64s(allVars)
|
||||
start.Done()
|
||||
end.Wait()
|
||||
|
||||
m := &dto.Metric{}
|
||||
sum.Write(m)
|
||||
if got, want := int(*m.Summary.SampleCount), total; got != want {
|
||||
t.Errorf("got sample count %d, want %d", got, want)
|
||||
}
|
||||
if got, want := *m.Summary.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 {
|
||||
t.Errorf("got sample sum %f, want %f", got, want)
|
||||
}
|
||||
|
||||
objectives := make([]float64, 0, len(DefObjectives))
|
||||
for qu := range DefObjectives {
|
||||
objectives = append(objectives, qu)
|
||||
}
|
||||
sort.Float64s(objectives)
|
||||
|
||||
for i, wantQ := range objectives {
|
||||
ε := DefObjectives[wantQ]
|
||||
gotQ := *m.Summary.Quantile[i].Quantile
|
||||
gotV := *m.Summary.Quantile[i].Value
|
||||
min, max := getBounds(allVars, wantQ, ε)
|
||||
if gotQ != wantQ {
|
||||
t.Errorf("got quantile %f, want %f", gotQ, wantQ)
|
||||
}
|
||||
if gotV < min || gotV > max {
|
||||
t.Errorf("got %f for quantile %f, want [%f,%f]", gotV, gotQ, min, max)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(it, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryVecConcurrency(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short mode.")
|
||||
}
|
||||
|
||||
rand.Seed(42)
|
||||
|
||||
objectives := make([]float64, 0, len(DefObjectives))
|
||||
for qu := range DefObjectives {
|
||||
|
||||
objectives = append(objectives, qu)
|
||||
}
|
||||
sort.Float64s(objectives)
|
||||
|
||||
it := func(n uint32) bool {
|
||||
mutations := int(n%1e4 + 1e4)
|
||||
concLevel := int(n%7 + 1)
|
||||
vecLength := int(n%3 + 1)
|
||||
|
||||
var start, end sync.WaitGroup
|
||||
start.Add(1)
|
||||
end.Add(concLevel)
|
||||
|
||||
sum := NewSummaryVec(
|
||||
SummaryOpts{
|
||||
Name: "test_summary",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"label"},
|
||||
)
|
||||
|
||||
allVars := make([][]float64, vecLength)
|
||||
sampleSums := make([]float64, vecLength)
|
||||
for i := 0; i < concLevel; i++ {
|
||||
vals := make([]float64, mutations)
|
||||
picks := make([]int, mutations)
|
||||
for j := 0; j < mutations; j++ {
|
||||
v := rand.NormFloat64()
|
||||
vals[j] = v
|
||||
pick := rand.Intn(vecLength)
|
||||
picks[j] = pick
|
||||
allVars[pick] = append(allVars[pick], v)
|
||||
sampleSums[pick] += v
|
||||
}
|
||||
|
||||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for i, v := range vals {
|
||||
sum.WithLabelValues(string('A' + picks[i])).Observe(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
}
|
||||
for _, vars := range allVars {
|
||||
sort.Float64s(vars)
|
||||
}
|
||||
start.Done()
|
||||
end.Wait()
|
||||
|
||||
for i := 0; i < vecLength; i++ {
|
||||
m := &dto.Metric{}
|
||||
s := sum.WithLabelValues(string('A' + i))
|
||||
s.Write(m)
|
||||
if got, want := int(*m.Summary.SampleCount), len(allVars[i]); got != want {
|
||||
t.Errorf("got sample count %d for label %c, want %d", got, 'A'+i, want)
|
||||
}
|
||||
if got, want := *m.Summary.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 {
|
||||
t.Errorf("got sample sum %f for label %c, want %f", got, 'A'+i, want)
|
||||
}
|
||||
for j, wantQ := range objectives {
|
||||
ε := DefObjectives[wantQ]
|
||||
gotQ := *m.Summary.Quantile[j].Quantile
|
||||
gotV := *m.Summary.Quantile[j].Value
|
||||
min, max := getBounds(allVars[i], wantQ, ε)
|
||||
if gotQ != wantQ {
|
||||
t.Errorf("got quantile %f for label %c, want %f", gotQ, 'A'+i, wantQ)
|
||||
}
|
||||
if gotV < min || gotV > max {
|
||||
t.Errorf("got %f for quantile %f for label %c, want [%f,%f]", gotV, gotQ, 'A'+i, min, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if err := quick.Check(it, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryDecay(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping test in short mode.")
|
||||
// More because it depends on timing than because it is particularly long...
|
||||
}
|
||||
|
||||
sum := NewSummary(SummaryOpts{
|
||||
Name: "test_summary",
|
||||
Help: "helpless",
|
||||
MaxAge: 100 * time.Millisecond,
|
||||
Objectives: map[float64]float64{0.1: 0.001},
|
||||
AgeBuckets: 10,
|
||||
})
|
||||
|
||||
m := &dto.Metric{}
|
||||
i := 0
|
||||
tick := time.NewTicker(time.Millisecond)
|
||||
for _ = range tick.C {
|
||||
i++
|
||||
sum.Observe(float64(i))
|
||||
if i%10 == 0 {
|
||||
sum.Write(m)
|
||||
if got, want := *m.Summary.Quantile[0].Value, math.Max(float64(i)/10, float64(i-90)); math.Abs(got-want) > 20 {
|
||||
t.Errorf("%d. got %f, want %f", i, got, want)
|
||||
}
|
||||
m.Reset()
|
||||
}
|
||||
if i >= 1000 {
|
||||
break
|
||||
}
|
||||
}
|
||||
tick.Stop()
|
||||
// Wait for MaxAge without observations and make sure quantiles are NaN.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
sum.Write(m)
|
||||
if got := *m.Summary.Quantile[0].Value; !math.IsNaN(got) {
|
||||
t.Errorf("got %f, want NaN after expiration", got)
|
||||
}
|
||||
}
|
||||
|
||||
func getBounds(vars []float64, q, ε float64) (min, max float64) {
|
||||
// TODO(beorn7): This currently tolerates an error of up to 2*ε. The
|
||||
// error must be at most ε, but for some reason, it's sometimes slightly
|
||||
// higher. That's a bug.
|
||||
n := float64(len(vars))
|
||||
lower := int((q - 2*ε) * n)
|
||||
upper := int(math.Ceil((q + 2*ε) * n))
|
||||
min = vars[0]
|
||||
if lower > 1 {
|
||||
min = vars[lower-1]
|
||||
}
|
||||
max = vars[len(vars)-1]
|
||||
if upper < len(vars) {
|
||||
max = vars[upper-1]
|
||||
}
|
||||
return
|
||||
}
|
51
gateway/vendor/github.com/prometheus/client_golang/prometheus/timer.go
generated
vendored
51
gateway/vendor/github.com/prometheus/client_golang/prometheus/timer.go
generated
vendored
@ -1,51 +0,0 @@
|
||||
// Copyright 2016 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import "time"
|
||||
|
||||
// Timer is a helper type to time functions. Use NewTimer to create new
|
||||
// instances.
|
||||
type Timer struct {
|
||||
begin time.Time
|
||||
observer Observer
|
||||
}
|
||||
|
||||
// NewTimer creates a new Timer. The provided Observer is used to observe a
|
||||
// duration in seconds. Timer is usually used to time a function call in the
|
||||
// following way:
|
||||
// func TimeMe() {
|
||||
// timer := NewTimer(myHistogram)
|
||||
// defer timer.ObserveDuration()
|
||||
// // Do actual work.
|
||||
// }
|
||||
func NewTimer(o Observer) *Timer {
|
||||
return &Timer{
|
||||
begin: time.Now(),
|
||||
observer: o,
|
||||
}
|
||||
}
|
||||
|
||||
// ObserveDuration records the duration passed since the Timer was created with
|
||||
// NewTimer. It calls the Observe method of the Observer provided during
|
||||
// construction with the duration in seconds as an argument. ObserveDuration is
|
||||
// usually called with a defer statement.
|
||||
//
|
||||
// Note that this method is only guaranteed to never observe negative durations
|
||||
// if used with Go1.9+.
|
||||
func (t *Timer) ObserveDuration() {
|
||||
if t.observer != nil {
|
||||
t.observer.Observe(time.Since(t.begin).Seconds())
|
||||
}
|
||||
}
|
64
gateway/vendor/github.com/prometheus/client_golang/prometheus/untyped.go
generated
vendored
64
gateway/vendor/github.com/prometheus/client_golang/prometheus/untyped.go
generated
vendored
@ -20,11 +20,6 @@ package prometheus
|
||||
// no type information is implied.
|
||||
//
|
||||
// To create Untyped instances, use NewUntyped.
|
||||
//
|
||||
// Deprecated: The Untyped type is deprecated because it doesn't make sense in
|
||||
// direct instrumentation. If you need to mirror an external metric of unknown
|
||||
// type (usually while writing exporters), Use MustNewConstMetric to create an
|
||||
// untyped metric instance on the fly.
|
||||
type Untyped interface {
|
||||
Metric
|
||||
Collector
|
||||
@ -60,14 +55,13 @@ func NewUntyped(opts UntypedOpts) Untyped {
|
||||
// share the same Desc, but have different values for their variable
|
||||
// labels. This is used if you want to count the same thing partitioned by
|
||||
// various dimensions. Create instances with NewUntypedVec.
|
||||
//
|
||||
// Deprecated: UntypedVec is deprecated for the same reasons as Untyped.
|
||||
type UntypedVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewUntypedVec creates a new UntypedVec based on the provided UntypedOpts and
|
||||
// partitioned by the given label names.
|
||||
// partitioned by the given label names. At least one label name must be
|
||||
// provided.
|
||||
func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec {
|
||||
desc := NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
@ -76,58 +70,28 @@ func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec {
|
||||
opts.ConstLabels,
|
||||
)
|
||||
return &UntypedVec{
|
||||
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
MetricVec: newMetricVec(desc, func(lvs ...string) Metric {
|
||||
return newValue(desc, UntypedValue, 0, lvs...)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Untyped for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Untyped is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Untyped to only
|
||||
// create the new Untyped but leave it at its starting value 0. See also the
|
||||
// SummaryVec example.
|
||||
//
|
||||
// Keeping the Untyped for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Untyped from the UntypedVec. In that case, the
|
||||
// Untyped will still exist, but it will not be exported anymore, even if a
|
||||
// Untyped with the same label values is created later. See also the CounterVec
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
// GetMetricWithLabelValues replaces the method of the same name in
|
||||
// MetricVec. The difference is that this method returns an Untyped and not a
|
||||
// Metric so that no type conversion is required.
|
||||
func (m *UntypedVec) GetMetricWithLabelValues(lvs ...string) (Untyped, error) {
|
||||
metric, err := m.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Untyped), err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetMetricWith returns the Untyped for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new Untyped is created. Implications of
|
||||
// creating a Untyped without using it and keeping the Untyped for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc.
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
// GetMetricWith replaces the method of the same name in MetricVec. The
|
||||
// difference is that this method returns an Untyped and not a Metric so that no
|
||||
// type conversion is required.
|
||||
func (m *UntypedVec) GetMetricWith(labels Labels) (Untyped, error) {
|
||||
metric, err := m.metricVec.getMetricWith(labels)
|
||||
metric, err := m.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Untyped), err
|
||||
}
|
||||
@ -139,14 +103,14 @@ func (m *UntypedVec) GetMetricWith(labels Labels) (Untyped, error) {
|
||||
// error, WithLabelValues allows shortcuts like
|
||||
// myVec.WithLabelValues("404", "GET").Add(42)
|
||||
func (m *UntypedVec) WithLabelValues(lvs ...string) Untyped {
|
||||
return m.metricVec.withLabelValues(lvs...).(Untyped)
|
||||
return m.MetricVec.WithLabelValues(lvs...).(Untyped)
|
||||
}
|
||||
|
||||
// With works as GetMetricWith, but panics where GetMetricWithLabels would have
|
||||
// returned an error. By not returning an error, With allows shortcuts like
|
||||
// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
|
||||
func (m *UntypedVec) With(labels Labels) Untyped {
|
||||
return m.metricVec.with(labels).(Untyped)
|
||||
return m.MetricVec.With(labels).(Untyped)
|
||||
}
|
||||
|
||||
// UntypedFunc is an Untyped whose value is determined at collect time by
|
||||
|
7
gateway/vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
7
gateway/vendor/github.com/prometheus/client_golang/prometheus/value.go
generated
vendored
@ -19,7 +19,6 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
@ -44,7 +43,7 @@ var errInconsistentCardinality = errors.New("inconsistent label cardinality")
|
||||
// ValueType. This is a low-level building block used by the library to back the
|
||||
// implementations of Counter, Gauge, and Untyped.
|
||||
type value struct {
|
||||
// valBits contains the bits of the represented float64 value. It has
|
||||
// valBits containst the bits of the represented float64 value. It has
|
||||
// to go first in the struct to guarantee alignment for atomic
|
||||
// operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||
valBits uint64
|
||||
@ -81,10 +80,6 @@ func (v *value) Set(val float64) {
|
||||
atomic.StoreUint64(&v.valBits, math.Float64bits(val))
|
||||
}
|
||||
|
||||
func (v *value) SetToCurrentTime() {
|
||||
v.Set(float64(time.Now().UnixNano()) / 1e9)
|
||||
}
|
||||
|
||||
func (v *value) Inc() {
|
||||
v.Add(1)
|
||||
}
|
||||
|
130
gateway/vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
130
gateway/vendor/github.com/prometheus/client_golang/prometheus/vec.go
generated
vendored
@ -20,12 +20,12 @@ import (
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
// metricVec is a Collector to bundle metrics of the same name that differ in
|
||||
// their label values. metricVec is not used directly (and therefore
|
||||
// unexported). It is used as a building block for implementations of vectors of
|
||||
// a given metric type, like GaugeVec, CounterVec, SummaryVec, HistogramVec, and
|
||||
// UntypedVec.
|
||||
type metricVec struct {
|
||||
// MetricVec is a Collector to bundle metrics of the same name that
|
||||
// differ in their label values. MetricVec is usually not used directly but as a
|
||||
// building block for implementations of vectors of a given metric
|
||||
// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
|
||||
// provided in this package.
|
||||
type MetricVec struct {
|
||||
mtx sync.RWMutex // Protects the children.
|
||||
children map[uint64][]metricWithLabelValues
|
||||
desc *Desc
|
||||
@ -37,8 +37,8 @@ type metricVec struct {
|
||||
|
||||
// newMetricVec returns an initialized MetricVec. The concrete value is
|
||||
// returned for embedding into another struct.
|
||||
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
||||
return &metricVec{
|
||||
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
||||
return &MetricVec{
|
||||
children: map[uint64][]metricWithLabelValues{},
|
||||
desc: desc,
|
||||
newMetric: newMetric,
|
||||
@ -56,12 +56,12 @@ type metricWithLabelValues struct {
|
||||
|
||||
// Describe implements Collector. The length of the returned slice
|
||||
// is always one.
|
||||
func (m *metricVec) Describe(ch chan<- *Desc) {
|
||||
func (m *MetricVec) Describe(ch chan<- *Desc) {
|
||||
ch <- m.desc
|
||||
}
|
||||
|
||||
// Collect implements Collector.
|
||||
func (m *metricVec) Collect(ch chan<- Metric) {
|
||||
func (m *MetricVec) Collect(ch chan<- Metric) {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
|
||||
@ -72,7 +72,31 @@ func (m *metricVec) Collect(ch chan<- Metric) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
// GetMetricWithLabelValues returns the Metric for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Metric is created.
|
||||
//
|
||||
// It is possible to call this method without using the returned Metric to only
|
||||
// create the new Metric but leave it at its start value (e.g. a Summary or
|
||||
// Histogram without any observations). See also the SummaryVec example.
|
||||
//
|
||||
// Keeping the Metric for later use is possible (and should be considered if
|
||||
// performance is critical), but keep in mind that Reset, DeleteLabelValues and
|
||||
// Delete can be used to delete the Metric from the MetricVec. In that case, the
|
||||
// Metric will still exist, but it will not be exported anymore, even if a
|
||||
// Metric with the same label values is created later. See also the CounterVec
|
||||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -81,7 +105,19 @@ func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
return m.getOrCreateMetricWithLabelValues(h, lvs), nil
|
||||
}
|
||||
|
||||
func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
||||
// GetMetricWith returns the Metric for the given Labels map (the label names
|
||||
// must match those of the VariableLabels in Desc). If that label map is
|
||||
// accessed for the first time, a new Metric is created. Implications of
|
||||
// creating a Metric without using it and keeping the Metric for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc.
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -90,16 +126,22 @@ func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
||||
return m.getOrCreateMetricWithLabels(h, labels), nil
|
||||
}
|
||||
|
||||
func (m *metricVec) withLabelValues(lvs ...string) Metric {
|
||||
metric, err := m.getMetricWithLabelValues(lvs...)
|
||||
// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
|
||||
// occurs. The method allows neat syntax like:
|
||||
// httpReqs.WithLabelValues("404", "POST").Inc()
|
||||
func (m *MetricVec) WithLabelValues(lvs ...string) Metric {
|
||||
metric, err := m.GetMetricWithLabelValues(lvs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return metric
|
||||
}
|
||||
|
||||
func (m *metricVec) with(labels Labels) Metric {
|
||||
metric, err := m.getMetricWith(labels)
|
||||
// With works as GetMetricWith, but panics if an error occurs. The method allows
|
||||
// neat syntax like:
|
||||
// httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
|
||||
func (m *MetricVec) With(labels Labels) Metric {
|
||||
metric, err := m.GetMetricWith(labels)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -111,8 +153,8 @@ func (m *metricVec) with(labels Labels) Metric {
|
||||
// returns true if a metric was deleted.
|
||||
//
|
||||
// It is not an error if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc. However, such inconsistent label count can
|
||||
// never match an actual metric, so the method will always return false in that
|
||||
// number of VariableLabels in Desc. However, such inconsistent label count can
|
||||
// never match an actual Metric, so the method will always return false in that
|
||||
// case.
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
@ -121,7 +163,7 @@ func (m *metricVec) with(labels Labels) Metric {
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the CounterVec example.
|
||||
func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
@ -136,13 +178,13 @@ func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
||||
// passed in as labels. It returns true if a metric was deleted.
|
||||
//
|
||||
// It is not an error if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc. However, such inconsistent Labels
|
||||
// can never match an actual metric, so the method will always return false in
|
||||
// that case.
|
||||
// with those of the VariableLabels in the Desc of the MetricVec. However, such
|
||||
// inconsistent Labels can never match an actual Metric, so the method will
|
||||
// always return false in that case.
|
||||
//
|
||||
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
||||
// there for pros and cons of the two methods.
|
||||
func (m *metricVec) Delete(labels Labels) bool {
|
||||
func (m *MetricVec) Delete(labels Labels) bool {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
@ -157,7 +199,7 @@ func (m *metricVec) Delete(labels Labels) bool {
|
||||
// deleteByHashWithLabelValues removes the metric from the hash bucket h. If
|
||||
// there are multiple matches in the bucket, use lvs to select a metric and
|
||||
// remove only that metric.
|
||||
func (m *metricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
|
||||
func (m *MetricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
|
||||
metrics, ok := m.children[h]
|
||||
if !ok {
|
||||
return false
|
||||
@ -179,7 +221,7 @@ func (m *metricVec) deleteByHashWithLabelValues(h uint64, lvs []string) bool {
|
||||
// deleteByHashWithLabels removes the metric from the hash bucket h. If there
|
||||
// are multiple matches in the bucket, use lvs to select a metric and remove
|
||||
// only that metric.
|
||||
func (m *metricVec) deleteByHashWithLabels(h uint64, labels Labels) bool {
|
||||
func (m *MetricVec) deleteByHashWithLabels(h uint64, labels Labels) bool {
|
||||
metrics, ok := m.children[h]
|
||||
if !ok {
|
||||
return false
|
||||
@ -198,7 +240,7 @@ func (m *metricVec) deleteByHashWithLabels(h uint64, labels Labels) bool {
|
||||
}
|
||||
|
||||
// Reset deletes all metrics in this vector.
|
||||
func (m *metricVec) Reset() {
|
||||
func (m *MetricVec) Reset() {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
@ -207,7 +249,7 @@ func (m *metricVec) Reset() {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
if len(vals) != len(m.desc.variableLabels) {
|
||||
return 0, errInconsistentCardinality
|
||||
}
|
||||
@ -219,7 +261,7 @@ func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
if len(labels) != len(m.desc.variableLabels) {
|
||||
return 0, errInconsistentCardinality
|
||||
}
|
||||
@ -239,9 +281,9 @@ func (m *metricVec) hashLabels(labels Labels) (uint64, error) {
|
||||
// or creates it and returns the new one.
|
||||
//
|
||||
// This function holds the mutex.
|
||||
func (m *metricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric {
|
||||
func (m *MetricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string) Metric {
|
||||
m.mtx.RLock()
|
||||
metric, ok := m.getMetricWithHashAndLabelValues(hash, lvs)
|
||||
metric, ok := m.getMetricWithLabelValues(hash, lvs)
|
||||
m.mtx.RUnlock()
|
||||
if ok {
|
||||
return metric
|
||||
@ -249,7 +291,7 @@ func (m *metricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string)
|
||||
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
metric, ok = m.getMetricWithHashAndLabelValues(hash, lvs)
|
||||
metric, ok = m.getMetricWithLabelValues(hash, lvs)
|
||||
if !ok {
|
||||
// Copy to avoid allocation in case wo don't go down this code path.
|
||||
copiedLVs := make([]string, len(lvs))
|
||||
@ -264,9 +306,9 @@ func (m *metricVec) getOrCreateMetricWithLabelValues(hash uint64, lvs []string)
|
||||
// or creates it and returns the new one.
|
||||
//
|
||||
// This function holds the mutex.
|
||||
func (m *metricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric {
|
||||
func (m *MetricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metric {
|
||||
m.mtx.RLock()
|
||||
metric, ok := m.getMetricWithHashAndLabels(hash, labels)
|
||||
metric, ok := m.getMetricWithLabels(hash, labels)
|
||||
m.mtx.RUnlock()
|
||||
if ok {
|
||||
return metric
|
||||
@ -274,7 +316,7 @@ func (m *metricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metr
|
||||
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
metric, ok = m.getMetricWithHashAndLabels(hash, labels)
|
||||
metric, ok = m.getMetricWithLabels(hash, labels)
|
||||
if !ok {
|
||||
lvs := m.extractLabelValues(labels)
|
||||
metric = m.newMetric(lvs...)
|
||||
@ -283,9 +325,9 @@ func (m *metricVec) getOrCreateMetricWithLabels(hash uint64, labels Labels) Metr
|
||||
return metric
|
||||
}
|
||||
|
||||
// getMetricWithHashAndLabelValues gets a metric while handling possible
|
||||
// collisions in the hash space. Must be called while holding the read mutex.
|
||||
func (m *metricVec) getMetricWithHashAndLabelValues(h uint64, lvs []string) (Metric, bool) {
|
||||
// getMetricWithLabelValues gets a metric while handling possible collisions in
|
||||
// the hash space. Must be called while holding read mutex.
|
||||
func (m *MetricVec) getMetricWithLabelValues(h uint64, lvs []string) (Metric, bool) {
|
||||
metrics, ok := m.children[h]
|
||||
if ok {
|
||||
if i := m.findMetricWithLabelValues(metrics, lvs); i < len(metrics) {
|
||||
@ -295,9 +337,9 @@ func (m *metricVec) getMetricWithHashAndLabelValues(h uint64, lvs []string) (Met
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// getMetricWithHashAndLabels gets a metric while handling possible collisions in
|
||||
// getMetricWithLabels gets a metric while handling possible collisions in
|
||||
// the hash space. Must be called while holding read mutex.
|
||||
func (m *metricVec) getMetricWithHashAndLabels(h uint64, labels Labels) (Metric, bool) {
|
||||
func (m *MetricVec) getMetricWithLabels(h uint64, labels Labels) (Metric, bool) {
|
||||
metrics, ok := m.children[h]
|
||||
if ok {
|
||||
if i := m.findMetricWithLabels(metrics, labels); i < len(metrics) {
|
||||
@ -309,7 +351,7 @@ func (m *metricVec) getMetricWithHashAndLabels(h uint64, labels Labels) (Metric,
|
||||
|
||||
// findMetricWithLabelValues returns the index of the matching metric or
|
||||
// len(metrics) if not found.
|
||||
func (m *metricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int {
|
||||
func (m *MetricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, lvs []string) int {
|
||||
for i, metric := range metrics {
|
||||
if m.matchLabelValues(metric.values, lvs) {
|
||||
return i
|
||||
@ -320,7 +362,7 @@ func (m *metricVec) findMetricWithLabelValues(metrics []metricWithLabelValues, l
|
||||
|
||||
// findMetricWithLabels returns the index of the matching metric or len(metrics)
|
||||
// if not found.
|
||||
func (m *metricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int {
|
||||
func (m *MetricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels Labels) int {
|
||||
for i, metric := range metrics {
|
||||
if m.matchLabels(metric.values, labels) {
|
||||
return i
|
||||
@ -329,7 +371,7 @@ func (m *metricVec) findMetricWithLabels(metrics []metricWithLabelValues, labels
|
||||
return len(metrics)
|
||||
}
|
||||
|
||||
func (m *metricVec) matchLabelValues(values []string, lvs []string) bool {
|
||||
func (m *MetricVec) matchLabelValues(values []string, lvs []string) bool {
|
||||
if len(values) != len(lvs) {
|
||||
return false
|
||||
}
|
||||
@ -341,7 +383,7 @@ func (m *metricVec) matchLabelValues(values []string, lvs []string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *metricVec) matchLabels(values []string, labels Labels) bool {
|
||||
func (m *MetricVec) matchLabels(values []string, labels Labels) bool {
|
||||
if len(labels) != len(values) {
|
||||
return false
|
||||
}
|
||||
@ -353,7 +395,7 @@ func (m *metricVec) matchLabels(values []string, labels Labels) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *metricVec) extractLabelValues(labels Labels) []string {
|
||||
func (m *MetricVec) extractLabelValues(labels Labels) []string {
|
||||
labelValues := make([]string, len(labels))
|
||||
for i, k := range m.desc.variableLabels {
|
||||
labelValues[i] = labels[k]
|
||||
|
312
gateway/vendor/github.com/prometheus/client_golang/prometheus/vec_test.go
generated
vendored
Normal file
312
gateway/vendor/github.com/prometheus/client_golang/prometheus/vec_test.go
generated
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"l1", "l2"},
|
||||
)
|
||||
testDelete(t, vec)
|
||||
}
|
||||
|
||||
func TestDeleteWithCollisions(t *testing.T) {
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"l1", "l2"},
|
||||
)
|
||||
vec.hashAdd = func(h uint64, s string) uint64 { return 1 }
|
||||
vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 }
|
||||
testDelete(t, vec)
|
||||
}
|
||||
|
||||
func testDelete(t *testing.T, vec *UntypedVec) {
|
||||
if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
|
||||
if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), true; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
|
||||
if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), true; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
|
||||
if got, want := vec.Delete(Labels{"l2": "v1", "l1": "v2"}), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := vec.Delete(Labels{"l1": "v1"}), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteLabelValues(t *testing.T) {
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"l1", "l2"},
|
||||
)
|
||||
testDeleteLabelValues(t, vec)
|
||||
}
|
||||
|
||||
func TestDeleteLabelValuesWithCollisions(t *testing.T) {
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"l1", "l2"},
|
||||
)
|
||||
vec.hashAdd = func(h uint64, s string) uint64 { return 1 }
|
||||
vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 }
|
||||
testDeleteLabelValues(t, vec)
|
||||
}
|
||||
|
||||
func testDeleteLabelValues(t *testing.T, vec *UntypedVec) {
|
||||
if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
|
||||
vec.With(Labels{"l1": "v1", "l2": "v3"}).(Untyped).Set(42) // Add junk data for collision.
|
||||
if got, want := vec.DeleteLabelValues("v1", "v2"), true; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := vec.DeleteLabelValues("v1", "v3"), true; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
|
||||
// Delete out of order.
|
||||
if got, want := vec.DeleteLabelValues("v2", "v1"), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := vec.DeleteLabelValues("v1"), false; got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetricVec(t *testing.T) {
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"l1", "l2"},
|
||||
)
|
||||
testMetricVec(t, vec)
|
||||
}
|
||||
|
||||
func TestMetricVecWithCollisions(t *testing.T) {
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"l1", "l2"},
|
||||
)
|
||||
vec.hashAdd = func(h uint64, s string) uint64 { return 1 }
|
||||
vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 }
|
||||
testMetricVec(t, vec)
|
||||
}
|
||||
|
||||
func testMetricVec(t *testing.T, vec *UntypedVec) {
|
||||
vec.Reset() // Actually test Reset now!
|
||||
|
||||
var pair [2]string
|
||||
// Keep track of metrics.
|
||||
expected := map[[2]string]int{}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
pair[0], pair[1] = fmt.Sprint(i%4), fmt.Sprint(i%5) // Varying combinations multiples.
|
||||
expected[pair]++
|
||||
vec.WithLabelValues(pair[0], pair[1]).Inc()
|
||||
|
||||
expected[[2]string{"v1", "v2"}]++
|
||||
vec.WithLabelValues("v1", "v2").(Untyped).Inc()
|
||||
}
|
||||
|
||||
var total int
|
||||
for _, metrics := range vec.children {
|
||||
for _, metric := range metrics {
|
||||
total++
|
||||
copy(pair[:], metric.values)
|
||||
|
||||
var metricOut dto.Metric
|
||||
if err := metric.metric.Write(&metricOut); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := *metricOut.Untyped.Value
|
||||
|
||||
var actualPair [2]string
|
||||
for i, label := range metricOut.Label {
|
||||
actualPair[i] = *label.Value
|
||||
}
|
||||
|
||||
// Test output pair against metric.values to ensure we've selected
|
||||
// the right one. We check this to ensure the below check means
|
||||
// anything at all.
|
||||
if actualPair != pair {
|
||||
t.Fatalf("unexpected pair association in metric map: %v != %v", actualPair, pair)
|
||||
}
|
||||
|
||||
if actual != float64(expected[pair]) {
|
||||
t.Fatalf("incorrect counter value for %v: %v != %v", pair, actual, expected[pair])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if total != len(expected) {
|
||||
t.Fatalf("unexpected number of metrics: %v != %v", total, len(expected))
|
||||
}
|
||||
|
||||
vec.Reset()
|
||||
|
||||
if len(vec.children) > 0 {
|
||||
t.Fatalf("reset failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCounterVecEndToEndWithCollision(t *testing.T) {
|
||||
vec := NewCounterVec(
|
||||
CounterOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
[]string{"labelname"},
|
||||
)
|
||||
vec.WithLabelValues("77kepQFQ8Kl").Inc()
|
||||
vec.WithLabelValues("!0IC=VloaY").Add(2)
|
||||
|
||||
m := &dto.Metric{}
|
||||
if err := vec.WithLabelValues("77kepQFQ8Kl").Write(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := m.GetLabel()[0].GetValue(), "77kepQFQ8Kl"; got != want {
|
||||
t.Errorf("got label value %q, want %q", got, want)
|
||||
}
|
||||
if got, want := m.GetCounter().GetValue(), 1.; got != want {
|
||||
t.Errorf("got value %f, want %f", got, want)
|
||||
}
|
||||
m.Reset()
|
||||
if err := vec.WithLabelValues("!0IC=VloaY").Write(m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := m.GetLabel()[0].GetValue(), "!0IC=VloaY"; got != want {
|
||||
t.Errorf("got label value %q, want %q", got, want)
|
||||
}
|
||||
if got, want := m.GetCounter().GetValue(), 2.; got != want {
|
||||
t.Errorf("got value %f, want %f", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMetricVecWithLabelValuesBasic(b *testing.B) {
|
||||
benchmarkMetricVecWithLabelValues(b, map[string][]string{
|
||||
"l1": []string{"onevalue"},
|
||||
"l2": []string{"twovalue"},
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkMetricVecWithLabelValues2Keys10ValueCardinality(b *testing.B) {
|
||||
benchmarkMetricVecWithLabelValuesCardinality(b, 2, 10)
|
||||
}
|
||||
|
||||
func BenchmarkMetricVecWithLabelValues4Keys10ValueCardinality(b *testing.B) {
|
||||
benchmarkMetricVecWithLabelValuesCardinality(b, 4, 10)
|
||||
}
|
||||
|
||||
func BenchmarkMetricVecWithLabelValues2Keys100ValueCardinality(b *testing.B) {
|
||||
benchmarkMetricVecWithLabelValuesCardinality(b, 2, 100)
|
||||
}
|
||||
|
||||
func BenchmarkMetricVecWithLabelValues10Keys100ValueCardinality(b *testing.B) {
|
||||
benchmarkMetricVecWithLabelValuesCardinality(b, 10, 100)
|
||||
}
|
||||
|
||||
func BenchmarkMetricVecWithLabelValues10Keys1000ValueCardinality(b *testing.B) {
|
||||
benchmarkMetricVecWithLabelValuesCardinality(b, 10, 1000)
|
||||
}
|
||||
|
||||
func benchmarkMetricVecWithLabelValuesCardinality(b *testing.B, nkeys, nvalues int) {
|
||||
labels := map[string][]string{}
|
||||
|
||||
for i := 0; i < nkeys; i++ {
|
||||
var (
|
||||
k = fmt.Sprintf("key-%v", i)
|
||||
vs = make([]string, 0, nvalues)
|
||||
)
|
||||
for j := 0; j < nvalues; j++ {
|
||||
vs = append(vs, fmt.Sprintf("value-%v", j))
|
||||
}
|
||||
labels[k] = vs
|
||||
}
|
||||
|
||||
benchmarkMetricVecWithLabelValues(b, labels)
|
||||
}
|
||||
|
||||
func benchmarkMetricVecWithLabelValues(b *testing.B, labels map[string][]string) {
|
||||
var keys []string
|
||||
for k := range labels { // Map order dependent, who cares though.
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
values := make([]string, len(labels)) // Value cache for permutations.
|
||||
vec := NewUntypedVec(
|
||||
UntypedOpts{
|
||||
Name: "test",
|
||||
Help: "helpless",
|
||||
},
|
||||
keys,
|
||||
)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Varies input across provide map entries based on key size.
|
||||
for j, k := range keys {
|
||||
candidates := labels[k]
|
||||
values[j] = candidates[i%len(candidates)]
|
||||
}
|
||||
|
||||
vec.WithLabelValues(values...)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user