forked from mirror/client_golang
Merge branch 'master' into beorn7/histogram
This commit is contained in:
commit
d6983369d2
|
@ -1,15 +1,13 @@
|
|||
---
|
||||
version: 2.1
|
||||
|
||||
orbs:
|
||||
go: circleci/go@0.2.0
|
||||
|
||||
prometheus: prometheus/prometheus@0.11.0
|
||||
jobs:
|
||||
test:
|
||||
parameters:
|
||||
go_version:
|
||||
type: string
|
||||
run_style:
|
||||
run_style_and_unused:
|
||||
type: boolean
|
||||
default: false
|
||||
run_lint:
|
||||
|
@ -19,56 +17,52 @@ jobs:
|
|||
type: boolean
|
||||
default: true
|
||||
docker:
|
||||
- image: circleci/golang:<< parameters.go_version >>
|
||||
- image: circleci/golang:<< parameters.go_version >>
|
||||
working_directory: /go/src/github.com/prometheus/client_golang
|
||||
steps:
|
||||
- checkout
|
||||
- when:
|
||||
condition: << parameters.use_gomod_cache >>
|
||||
steps:
|
||||
- go/load-cache:
|
||||
key: v1-go<< parameters.go_version >>
|
||||
- run: make check_license unused test
|
||||
- when:
|
||||
condition: << parameters.run_lint >>
|
||||
steps:
|
||||
- run: make lint
|
||||
- when:
|
||||
condition: << parameters.run_style >>
|
||||
steps:
|
||||
- run: make style
|
||||
- when:
|
||||
condition: << parameters.use_gomod_cache >>
|
||||
steps:
|
||||
- go/save-cache:
|
||||
key: v1-go<< parameters.go_version >>
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
|
||||
- checkout
|
||||
- when:
|
||||
condition: << parameters.use_gomod_cache >>
|
||||
steps:
|
||||
- go/load-cache:
|
||||
key: v1-go<< parameters.go_version >>
|
||||
- run: make check_license test
|
||||
- when:
|
||||
condition: << parameters.run_lint >>
|
||||
steps:
|
||||
- run: make lint
|
||||
- when:
|
||||
condition: << parameters.run_style_and_unused >>
|
||||
steps:
|
||||
- run: make style unused
|
||||
- when:
|
||||
condition: << parameters.use_gomod_cache >>
|
||||
steps:
|
||||
- go/save-cache:
|
||||
key: v1-go<< parameters.go_version >>
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
workflows:
|
||||
version: 2
|
||||
client_golang:
|
||||
jobs:
|
||||
# Refer to README.md for the currently supported versions.
|
||||
- test:
|
||||
name: go-1-9
|
||||
go_version: "1.9"
|
||||
use_gomod_cache: false
|
||||
- test:
|
||||
name: go-1-10
|
||||
go_version: "1.10"
|
||||
use_gomod_cache: false
|
||||
- test:
|
||||
name: go-1-11
|
||||
go_version: "1.11"
|
||||
run_lint: true
|
||||
- test:
|
||||
name: go-1-12
|
||||
go_version: "1.12"
|
||||
run_lint: true
|
||||
- test:
|
||||
name: go-1-13
|
||||
go_version: "1.13"
|
||||
run_lint: true
|
||||
# Style is only checked against the latest supported Go version.
|
||||
run_style: true
|
||||
# Refer to README.md for the currently supported versions.
|
||||
- test:
|
||||
name: go-1-13
|
||||
go_version: "1.13"
|
||||
run_lint: true
|
||||
- test:
|
||||
name: go-1-14
|
||||
go_version: "1.14"
|
||||
run_lint: true
|
||||
- test:
|
||||
name: go-1-15
|
||||
go_version: "1.15"
|
||||
run_lint: true
|
||||
- test:
|
||||
name: go-1-16
|
||||
go_version: "1.16"
|
||||
run_lint: true
|
||||
# Style and unused/missing packages are only checked against
|
||||
# the latest supported Go version.
|
||||
run_style_and_unused: true
|
||||
|
|
14
.travis.yml
14
.travis.yml
|
@ -1,14 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.9.x # See README.md for current minimum version.
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
|
||||
script:
|
||||
- make check_license unused test-short
|
||||
- if [[ ! $TRAVIS_GO_VERSION =~ ^1\.(9|10)\. ]]; then make lint; fi
|
||||
# Style is only checked against the latest supported Go version.
|
||||
- if [[ $TRAVIS_GO_VERSION =~ ^1\.(13)\. ]]; then make style; fi
|
43
CHANGELOG.md
43
CHANGELOG.md
|
@ -1,3 +1,46 @@
|
|||
## 1.10.0 / 2021-03-18
|
||||
|
||||
* [CHANGE] Minimum required Go version is now 1.13.
|
||||
* [CHANGE] API client: Add matchers to `LabelNames` and `LabesValues`. #828
|
||||
* [FEATURE] API client: Add buildinfo call. #841
|
||||
* [BUGFIX] Fix build on riscv64. #833
|
||||
|
||||
## 1.9.0 / 2020-12-17
|
||||
|
||||
* [FEATURE] `NewPidFileFn` helper to create process collectors for processes whose PID is read from a file. #804
|
||||
* [BUGFIX] promhttp: Prevent endless loop in `InstrumentHandler...` middlewares with invalid metric or label names. #823
|
||||
|
||||
## 1.8.0 / 2020-10-15
|
||||
|
||||
* [CHANGE] API client: Use `time.Time` rather than `string` for timestamps in `RuntimeinfoResult`. #777
|
||||
* [FEATURE] Export `MetricVec` to facilitate implementation of vectors of custom `Metric` types. #803
|
||||
* [FEATURE API client: Support `/status/tsdb` endpoint. #773
|
||||
* [ENHANCEMENT] API client: Enable GET fallback on status code 501. #802
|
||||
* [ENHANCEMENT] Remove `Metric` references after reslicing to free up more memory. #784
|
||||
|
||||
## 1.7.1 / 2020-06-23
|
||||
|
||||
* [BUGFIX] API client: Actually propagate start/end parameters of `LabelNames` and `LabelValues`. #771
|
||||
|
||||
## 1.7.0 / 2020-06-17
|
||||
|
||||
* [CHANGE] API client: Add start/end parameters to `LabelNames` and `LabelValues`. #767
|
||||
* [FEATURE] testutil: Add `GatherAndCount` and enable filtering in `CollectAndCount` #753
|
||||
* [FEATURE] API client: Add support for `status` and `runtimeinfo` endpoints. #755
|
||||
* [ENHANCEMENT] Wrapping `nil` with a `WrapRegistererWith...` function creates a no-op `Registerer`. #764
|
||||
* [ENHANCEMENT] promlint: Allow Kelvin as a base unit for cases like color temperature. #761
|
||||
* [BUGFIX] push: Properly handle empty job and label values. #752
|
||||
|
||||
## 1.6.0 / 2020-04-28
|
||||
|
||||
* [FEATURE] testutil: Add lint checks for metrics, including a sub-package `promlint` to expose the linter engine for external usage. #739 #743
|
||||
* [ENHANCEMENT] API client: Improve error messages. #731
|
||||
* [BUGFIX] process collector: Fix `process_resident_memory_bytes` on 32bit MS Windows. #734
|
||||
|
||||
## 1.5.1 / 2020-03-14
|
||||
|
||||
* [BUGFIX] promhttp: Remove another superfluous `WriteHeader` call. #726
|
||||
|
||||
## 1.5.0 / 2020-03-03
|
||||
|
||||
* [FEATURE] promauto: Add a factory to allow automatic registration with a local registry. #713
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
## Prometheus Community Code of Conduct
|
||||
|
||||
Prometheus follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
7
Makefile
7
Makefile
|
@ -13,13 +13,6 @@
|
|||
|
||||
include Makefile.common
|
||||
|
||||
# http.CloseNotifier is deprecated but we don't want to remove support
|
||||
# from client_golang to not break anybody still using it.
|
||||
STATICCHECK_IGNORE = \
|
||||
github.com/prometheus/client_golang/prometheus/promhttp/delegator*.go:SA1019 \
|
||||
github.com/prometheus/client_golang/prometheus/promhttp/instrument_server_test.go:SA1019 \
|
||||
github.com/prometheus/client_golang/prometheus/http.go:SA1019
|
||||
|
||||
.PHONY: test
|
||||
test: deps common-test
|
||||
|
||||
|
|
|
@ -78,12 +78,12 @@ ifneq ($(shell which gotestsum),)
|
|||
endif
|
||||
endif
|
||||
|
||||
PROMU_VERSION ?= 0.5.0
|
||||
PROMU_VERSION ?= 0.11.1
|
||||
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
|
||||
|
||||
GOLANGCI_LINT :=
|
||||
GOLANGCI_LINT_OPTS ?=
|
||||
GOLANGCI_LINT_VERSION ?= v1.18.0
|
||||
GOLANGCI_LINT_VERSION ?= v1.36.0
|
||||
# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
|
||||
# windows isn't included here because of the path separator being different.
|
||||
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
|
||||
|
@ -150,6 +150,17 @@ else
|
|||
$(GO) get $(GOOPTS) -t ./...
|
||||
endif
|
||||
|
||||
.PHONY: update-go-deps
|
||||
update-go-deps:
|
||||
@echo ">> updating Go dependencies"
|
||||
@for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
|
||||
$(GO) get $$m; \
|
||||
done
|
||||
GO111MODULE=$(GO111MODULE) $(GO) mod tidy
|
||||
ifneq (,$(wildcard vendor))
|
||||
GO111MODULE=$(GO111MODULE) $(GO) mod vendor
|
||||
endif
|
||||
|
||||
.PHONY: common-test-short
|
||||
common-test-short: $(GOTEST_DIR)
|
||||
@echo ">> running short tests"
|
||||
|
@ -234,10 +245,12 @@ common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
|
|||
$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
|
||||
docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)"
|
||||
|
||||
DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION)))
|
||||
.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
|
||||
common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
|
||||
$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
|
||||
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
|
||||
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"
|
||||
|
||||
.PHONY: common-docker-manifest
|
||||
common-docker-manifest:
|
||||
|
|
|
@ -9,7 +9,7 @@ This is the [Go](http://golang.org) client library for
|
|||
instrumenting application code, and one for creating clients that talk to the
|
||||
Prometheus HTTP API.
|
||||
|
||||
__This library requires Go1.9 or later.__ The minimum required patch releases for older Go versions are Go1.9.7 and Go1.10.3.
|
||||
__This library requires Go1.13 or later.__
|
||||
|
||||
## Important note about releases and stability
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Reporting a security issue
|
||||
|
||||
The Prometheus security policy, including how to report vulnerabilities, can be
|
||||
found here:
|
||||
|
||||
https://prometheus.io/docs/operating/security/
|
|
@ -117,8 +117,6 @@ func marshalPointJSONIsEmpty(ptr unsafe.Pointer) bool {
|
|||
}
|
||||
|
||||
const (
|
||||
statusAPIError = 422
|
||||
|
||||
apiPrefix = "/api/v1"
|
||||
|
||||
epAlerts = apiPrefix + "/alerts"
|
||||
|
@ -137,6 +135,9 @@ const (
|
|||
epCleanTombstones = apiPrefix + "/admin/tsdb/clean_tombstones"
|
||||
epConfig = apiPrefix + "/status/config"
|
||||
epFlags = apiPrefix + "/status/flags"
|
||||
epBuildinfo = apiPrefix + "/status/buildinfo"
|
||||
epRuntimeinfo = apiPrefix + "/status/runtimeinfo"
|
||||
epTSDB = apiPrefix + "/status/tsdb"
|
||||
)
|
||||
|
||||
// AlertState models the state of an alert.
|
||||
|
@ -230,14 +231,18 @@ type API interface {
|
|||
DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error
|
||||
// Flags returns the flag values that Prometheus was launched with.
|
||||
Flags(ctx context.Context) (FlagsResult, error)
|
||||
// LabelNames returns all the unique label names present in the block in sorted order.
|
||||
LabelNames(ctx context.Context) ([]string, Warnings, error)
|
||||
// LabelValues performs a query for the values of the given label.
|
||||
LabelValues(ctx context.Context, label string) (model.LabelValues, Warnings, error)
|
||||
// LabelNames returns the unique label names present in the block in sorted order by given time range and matchers.
|
||||
LabelNames(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, Warnings, error)
|
||||
// LabelValues performs a query for the values of the given label, time range and matchers.
|
||||
LabelValues(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (model.LabelValues, Warnings, error)
|
||||
// Query performs a query for the given time.
|
||||
Query(ctx context.Context, query string, ts time.Time) (model.Value, Warnings, error)
|
||||
// QueryRange performs a query for the given range.
|
||||
QueryRange(ctx context.Context, query string, r Range) (model.Value, Warnings, error)
|
||||
// Buildinfo returns various build information properties about the Prometheus server
|
||||
Buildinfo(ctx context.Context) (BuildinfoResult, error)
|
||||
// Runtimeinfo returns the various runtime information properties about the Prometheus server.
|
||||
Runtimeinfo(ctx context.Context) (RuntimeinfoResult, error)
|
||||
// Series finds series by label matchers.
|
||||
Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, Warnings, error)
|
||||
// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
|
||||
|
@ -251,6 +256,8 @@ type API interface {
|
|||
TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, error)
|
||||
// Metadata returns metadata about metrics currently scraped by the metric name.
|
||||
Metadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error)
|
||||
// TSDB returns the cardinality statistics.
|
||||
TSDB(ctx context.Context) (TSDBResult, error)
|
||||
}
|
||||
|
||||
// AlertsResult contains the result from querying the alerts endpoint.
|
||||
|
@ -277,6 +284,32 @@ type ConfigResult struct {
|
|||
// FlagsResult contains the result from querying the flag endpoint.
|
||||
type FlagsResult map[string]string
|
||||
|
||||
// BuildinfoResult contains the results from querying the buildinfo endpoint.
|
||||
type BuildinfoResult struct {
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
Branch string `json:"branch"`
|
||||
BuildUser string `json:"buildUser"`
|
||||
BuildDate string `json:"buildDate"`
|
||||
GoVersion string `json:"goVersion"`
|
||||
}
|
||||
|
||||
// RuntimeinfoResult contains the result from querying the runtimeinfo endpoint.
|
||||
type RuntimeinfoResult struct {
|
||||
StartTime time.Time `json:"startTime"`
|
||||
CWD string `json:"CWD"`
|
||||
ReloadConfigSuccess bool `json:"reloadConfigSuccess"`
|
||||
LastConfigTime time.Time `json:"lastConfigTime"`
|
||||
ChunkCount int `json:"chunkCount"`
|
||||
TimeSeriesCount int `json:"timeSeriesCount"`
|
||||
CorruptionCount int `json:"corruptionCount"`
|
||||
GoroutineCount int `json:"goroutineCount"`
|
||||
GOMAXPROCS int `json:"GOMAXPROCS"`
|
||||
GOGC string `json:"GOGC"`
|
||||
GODEBUG string `json:"GODEBUG"`
|
||||
StorageRetention string `json:"storageRetention"`
|
||||
}
|
||||
|
||||
// SnapshotResult contains the result from querying the snapshot endpoint.
|
||||
type SnapshotResult struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -385,6 +418,20 @@ type queryResult struct {
|
|||
v model.Value
|
||||
}
|
||||
|
||||
// TSDBResult contains the result from querying the tsdb endpoint.
|
||||
type TSDBResult struct {
|
||||
SeriesCountByMetricName []Stat `json:"seriesCountByMetricName"`
|
||||
LabelValueCountByLabelName []Stat `json:"labelValueCountByLabelName"`
|
||||
MemoryInBytesByLabelName []Stat `json:"memoryInBytesByLabelName"`
|
||||
SeriesCountByLabelValuePair []Stat `json:"seriesCountByLabelValuePair"`
|
||||
}
|
||||
|
||||
// Stat models information about statistic value.
|
||||
type Stat struct {
|
||||
Name string `json:"name"`
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
|
||||
func (rg *RuleGroup) UnmarshalJSON(b []byte) error {
|
||||
v := struct {
|
||||
Name string `json:"name"`
|
||||
|
@ -640,8 +687,51 @@ func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) {
|
|||
return res, json.Unmarshal(body, &res)
|
||||
}
|
||||
|
||||
func (h *httpAPI) LabelNames(ctx context.Context) ([]string, Warnings, error) {
|
||||
func (h *httpAPI) Buildinfo(ctx context.Context) (BuildinfoResult, error) {
|
||||
u := h.client.URL(epBuildinfo, nil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return BuildinfoResult{}, err
|
||||
}
|
||||
|
||||
_, body, _, err := h.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return BuildinfoResult{}, err
|
||||
}
|
||||
|
||||
var res BuildinfoResult
|
||||
return res, json.Unmarshal(body, &res)
|
||||
}
|
||||
|
||||
func (h *httpAPI) Runtimeinfo(ctx context.Context) (RuntimeinfoResult, error) {
|
||||
u := h.client.URL(epRuntimeinfo, nil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return RuntimeinfoResult{}, err
|
||||
}
|
||||
|
||||
_, body, _, err := h.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return RuntimeinfoResult{}, err
|
||||
}
|
||||
|
||||
var res RuntimeinfoResult
|
||||
return res, json.Unmarshal(body, &res)
|
||||
}
|
||||
|
||||
func (h *httpAPI) LabelNames(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]string, Warnings, error) {
|
||||
u := h.client.URL(epLabels, nil)
|
||||
q := u.Query()
|
||||
q.Set("start", formatTime(startTime))
|
||||
q.Set("end", formatTime(endTime))
|
||||
for _, m := range matches {
|
||||
q.Add("match[]", m)
|
||||
}
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -654,8 +744,17 @@ func (h *httpAPI) LabelNames(ctx context.Context) ([]string, Warnings, error) {
|
|||
return labelNames, w, json.Unmarshal(body, &labelNames)
|
||||
}
|
||||
|
||||
func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, Warnings, error) {
|
||||
func (h *httpAPI) LabelValues(ctx context.Context, label string, matches []string, startTime time.Time, endTime time.Time) (model.LabelValues, Warnings, error) {
|
||||
u := h.client.URL(epLabelValues, map[string]string{"name": label})
|
||||
q := u.Query()
|
||||
q.Set("start", formatTime(startTime))
|
||||
q.Set("end", formatTime(endTime))
|
||||
for _, m := range matches {
|
||||
q.Add("match[]", m)
|
||||
}
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -835,6 +934,24 @@ func (h *httpAPI) Metadata(ctx context.Context, metric string, limit string) (ma
|
|||
return res, json.Unmarshal(body, &res)
|
||||
}
|
||||
|
||||
func (h *httpAPI) TSDB(ctx context.Context) (TSDBResult, error) {
|
||||
u := h.client.URL(epTSDB, nil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return TSDBResult{}, err
|
||||
}
|
||||
|
||||
_, body, _, err := h.client.Do(ctx, req)
|
||||
if err != nil {
|
||||
return TSDBResult{}, err
|
||||
}
|
||||
|
||||
var res TSDBResult
|
||||
return res, json.Unmarshal(body, &res)
|
||||
|
||||
}
|
||||
|
||||
// Warnings is an array of non critical errors
|
||||
type Warnings []string
|
||||
|
||||
|
@ -860,7 +977,7 @@ type apiResponse struct {
|
|||
|
||||
func apiError(code int) bool {
|
||||
// These are the codes that Prometheus sends when it returns an error.
|
||||
return code == statusAPIError || code == http.StatusBadRequest
|
||||
return code == http.StatusUnprocessableEntity || code == http.StatusBadRequest
|
||||
}
|
||||
|
||||
func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
|
||||
|
@ -905,14 +1022,14 @@ func (h *apiClientImpl) Do(ctx context.Context, req *http.Request) (*http.Respon
|
|||
}
|
||||
}
|
||||
|
||||
if apiError(code) != (result.Status == "error") {
|
||||
if apiError(code) && result.Status == "success" {
|
||||
err = &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
}
|
||||
}
|
||||
|
||||
if apiError(code) && result.Status == "error" {
|
||||
if result.Status == "error" {
|
||||
err = &Error{
|
||||
Type: result.ErrorType,
|
||||
Msg: result.Error,
|
||||
|
@ -923,7 +1040,8 @@ func (h *apiClientImpl) Do(ctx context.Context, req *http.Request) (*http.Respon
|
|||
|
||||
}
|
||||
|
||||
// DoGetFallback will attempt to do the request as-is, and on a 405 it will fallback to a GET request.
|
||||
// DoGetFallback will attempt to do the request as-is, and on a 405 or 501 it
|
||||
// will fallback to a GET request.
|
||||
func (h *apiClientImpl) DoGetFallback(ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, Warnings, error) {
|
||||
req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(args.Encode()))
|
||||
if err != nil {
|
||||
|
@ -932,7 +1050,7 @@ func (h *apiClientImpl) DoGetFallback(ctx context.Context, u *url.URL, args url.
|
|||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
resp, body, warnings, err := h.Do(ctx, req)
|
||||
if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed {
|
||||
if resp != nil && (resp.StatusCode == http.StatusMethodNotAllowed || resp.StatusCode == http.StatusNotImplemented) {
|
||||
u.RawQuery = args.Encode()
|
||||
req, err = http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -84,7 +84,7 @@ func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Respon
|
|||
if test.inStatusCode != 0 {
|
||||
resp.StatusCode = test.inStatusCode
|
||||
} else if test.inErr != nil {
|
||||
resp.StatusCode = statusAPIError
|
||||
resp.StatusCode = http.StatusUnprocessableEntity
|
||||
} else {
|
||||
resp.StatusCode = http.StatusOK
|
||||
}
|
||||
|
@ -144,15 +144,29 @@ func TestAPIs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
doLabelNames := func(label string) func() (interface{}, Warnings, error) {
|
||||
doBuildinfo := func() func() (interface{}, Warnings, error) {
|
||||
return func() (interface{}, Warnings, error) {
|
||||
return promAPI.LabelNames(context.Background())
|
||||
v, err := promAPI.Buildinfo(context.Background())
|
||||
return v, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
doLabelValues := func(label string) func() (interface{}, Warnings, error) {
|
||||
doRuntimeinfo := func() func() (interface{}, Warnings, error) {
|
||||
return func() (interface{}, Warnings, error) {
|
||||
return promAPI.LabelValues(context.Background(), label)
|
||||
v, err := promAPI.Runtimeinfo(context.Background())
|
||||
return v, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
doLabelNames := func(matches []string) func() (interface{}, Warnings, error) {
|
||||
return func() (interface{}, Warnings, error) {
|
||||
return promAPI.LabelNames(context.Background(), matches, time.Now().Add(-100*time.Hour), time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
doLabelValues := func(matches []string, label string) func() (interface{}, Warnings, error) {
|
||||
return func() (interface{}, Warnings, error) {
|
||||
return promAPI.LabelValues(context.Background(), label, matches, time.Now().Add(-100*time.Hour), time.Now())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,6 +223,13 @@ func TestAPIs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
doTSDB := func() func() (interface{}, Warnings, error) {
|
||||
return func() (interface{}, Warnings, error) {
|
||||
v, err := promAPI.TSDB(context.Background())
|
||||
return v, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
queryTests := []apiTest{
|
||||
{
|
||||
do: doQuery("2", testTime),
|
||||
|
@ -345,14 +366,14 @@ func TestAPIs(t *testing.T) {
|
|||
},
|
||||
|
||||
{
|
||||
do: doLabelNames("mylabel"),
|
||||
do: doLabelNames(nil),
|
||||
inRes: []string{"val1", "val2"},
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/labels",
|
||||
res: []string{"val1", "val2"},
|
||||
},
|
||||
{
|
||||
do: doLabelNames("mylabel"),
|
||||
do: doLabelNames(nil),
|
||||
inRes: []string{"val1", "val2"},
|
||||
inWarnings: []string{"a"},
|
||||
reqMethod: "GET",
|
||||
|
@ -362,14 +383,14 @@ func TestAPIs(t *testing.T) {
|
|||
},
|
||||
|
||||
{
|
||||
do: doLabelNames("mylabel"),
|
||||
do: doLabelNames(nil),
|
||||
inErr: fmt.Errorf("some error"),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/labels",
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
{
|
||||
do: doLabelNames("mylabel"),
|
||||
do: doLabelNames(nil),
|
||||
inErr: fmt.Errorf("some error"),
|
||||
inWarnings: []string{"a"},
|
||||
reqMethod: "GET",
|
||||
|
@ -377,16 +398,24 @@ func TestAPIs(t *testing.T) {
|
|||
err: fmt.Errorf("some error"),
|
||||
warnings: []string{"a"},
|
||||
},
|
||||
{
|
||||
do: doLabelNames([]string{"up"}),
|
||||
inRes: []string{"val1", "val2"},
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/labels",
|
||||
reqParam: url.Values{"match[]": {"up"}},
|
||||
res: []string{"val1", "val2"},
|
||||
},
|
||||
|
||||
{
|
||||
do: doLabelValues("mylabel"),
|
||||
do: doLabelValues(nil, "mylabel"),
|
||||
inRes: []string{"val1", "val2"},
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/label/mylabel/values",
|
||||
res: model.LabelValues{"val1", "val2"},
|
||||
},
|
||||
{
|
||||
do: doLabelValues("mylabel"),
|
||||
do: doLabelValues(nil, "mylabel"),
|
||||
inRes: []string{"val1", "val2"},
|
||||
inWarnings: []string{"a"},
|
||||
reqMethod: "GET",
|
||||
|
@ -396,14 +425,14 @@ func TestAPIs(t *testing.T) {
|
|||
},
|
||||
|
||||
{
|
||||
do: doLabelValues("mylabel"),
|
||||
do: doLabelValues(nil, "mylabel"),
|
||||
inErr: fmt.Errorf("some error"),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/label/mylabel/values",
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
{
|
||||
do: doLabelValues("mylabel"),
|
||||
do: doLabelValues(nil, "mylabel"),
|
||||
inErr: fmt.Errorf("some error"),
|
||||
inWarnings: []string{"a"},
|
||||
reqMethod: "GET",
|
||||
|
@ -411,6 +440,14 @@ func TestAPIs(t *testing.T) {
|
|||
err: fmt.Errorf("some error"),
|
||||
warnings: []string{"a"},
|
||||
},
|
||||
{
|
||||
do: doLabelValues([]string{"up"}, "mylabel"),
|
||||
inRes: []string{"val1", "val2"},
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/label/mylabel/values",
|
||||
reqParam: url.Values{"match[]": {"up"}},
|
||||
res: model.LabelValues{"val1", "val2"},
|
||||
},
|
||||
|
||||
{
|
||||
do: doSeries("up", testTime.Add(-time.Minute), testTime),
|
||||
|
@ -605,6 +642,78 @@ func TestAPIs(t *testing.T) {
|
|||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
|
||||
{
|
||||
do: doBuildinfo(),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/status/buildinfo",
|
||||
inErr: fmt.Errorf("some error"),
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
|
||||
{
|
||||
do: doBuildinfo(),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/status/buildinfo",
|
||||
inRes: map[string]interface{}{
|
||||
"version": "2.23.0",
|
||||
"revision": "26d89b4b0776fe4cd5a3656dfa520f119a375273",
|
||||
"branch": "HEAD",
|
||||
"buildUser": "root@37609b3a0a21",
|
||||
"buildDate": "20201126-10:56:17",
|
||||
"goVersion": "go1.15.5",
|
||||
},
|
||||
res: BuildinfoResult{
|
||||
Version: "2.23.0",
|
||||
Revision: "26d89b4b0776fe4cd5a3656dfa520f119a375273",
|
||||
Branch: "HEAD",
|
||||
BuildUser: "root@37609b3a0a21",
|
||||
BuildDate: "20201126-10:56:17",
|
||||
GoVersion: "go1.15.5",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
do: doRuntimeinfo(),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/status/runtimeinfo",
|
||||
inErr: fmt.Errorf("some error"),
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
|
||||
{
|
||||
do: doRuntimeinfo(),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/status/runtimeinfo",
|
||||
inRes: map[string]interface{}{
|
||||
"startTime": "2020-05-18T15:52:53.4503113Z",
|
||||
"CWD": "/prometheus",
|
||||
"reloadConfigSuccess": true,
|
||||
"lastConfigTime": "2020-05-18T15:52:56Z",
|
||||
"chunkCount": 72692,
|
||||
"timeSeriesCount": 18476,
|
||||
"corruptionCount": 0,
|
||||
"goroutineCount": 217,
|
||||
"GOMAXPROCS": 2,
|
||||
"GOGC": "100",
|
||||
"GODEBUG": "allocfreetrace",
|
||||
"storageRetention": "1d",
|
||||
},
|
||||
res: RuntimeinfoResult{
|
||||
StartTime: time.Date(2020, 5, 18, 15, 52, 53, 450311300, time.UTC),
|
||||
CWD: "/prometheus",
|
||||
ReloadConfigSuccess: true,
|
||||
LastConfigTime: time.Date(2020, 5, 18, 15, 52, 56, 0, time.UTC),
|
||||
ChunkCount: 72692,
|
||||
TimeSeriesCount: 18476,
|
||||
CorruptionCount: 0,
|
||||
GoroutineCount: 217,
|
||||
GOMAXPROCS: 2,
|
||||
GOGC: "100",
|
||||
GODEBUG: "allocfreetrace",
|
||||
StorageRetention: "1d",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
do: doAlertManagers(),
|
||||
reqMethod: "GET",
|
||||
|
@ -904,6 +1013,72 @@ func TestAPIs(t *testing.T) {
|
|||
},
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
|
||||
{
|
||||
do: doTSDB(),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/status/tsdb",
|
||||
inErr: fmt.Errorf("some error"),
|
||||
err: fmt.Errorf("some error"),
|
||||
},
|
||||
|
||||
{
|
||||
do: doTSDB(),
|
||||
reqMethod: "GET",
|
||||
reqPath: "/api/v1/status/tsdb",
|
||||
inRes: map[string]interface{}{
|
||||
"seriesCountByMetricName": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "kubelet_http_requests_duration_seconds_bucket",
|
||||
"value": 1000,
|
||||
},
|
||||
},
|
||||
"labelValueCountByLabelName": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "__name__",
|
||||
"value": 200,
|
||||
},
|
||||
},
|
||||
"memoryInBytesByLabelName": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "id",
|
||||
"value": 4096,
|
||||
},
|
||||
},
|
||||
"seriesCountByLabelValuePair": []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "job=kubelet",
|
||||
"value": 30000,
|
||||
},
|
||||
},
|
||||
},
|
||||
res: TSDBResult{
|
||||
SeriesCountByMetricName: []Stat{
|
||||
{
|
||||
Name: "kubelet_http_requests_duration_seconds_bucket",
|
||||
Value: 1000,
|
||||
},
|
||||
},
|
||||
LabelValueCountByLabelName: []Stat{
|
||||
{
|
||||
Name: "__name__",
|
||||
Value: 200,
|
||||
},
|
||||
},
|
||||
MemoryInBytesByLabelName: []Stat{
|
||||
{
|
||||
Name: "id",
|
||||
Value: 4096,
|
||||
},
|
||||
},
|
||||
SeriesCountByLabelValuePair: []Stat{
|
||||
{
|
||||
Name: "job=kubelet",
|
||||
Value: 30000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var tests []apiTest
|
||||
|
@ -996,7 +1171,7 @@ func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response,
|
|||
func TestAPIClientDo(t *testing.T) {
|
||||
tests := []apiClientTest{
|
||||
{
|
||||
code: statusAPIError,
|
||||
code: http.StatusUnprocessableEntity,
|
||||
response: &apiResponse{
|
||||
Status: "error",
|
||||
Data: json.RawMessage(`null`),
|
||||
|
@ -1010,7 +1185,7 @@ func TestAPIClientDo(t *testing.T) {
|
|||
expectedBody: `null`,
|
||||
},
|
||||
{
|
||||
code: statusAPIError,
|
||||
code: http.StatusUnprocessableEntity,
|
||||
response: &apiResponse{
|
||||
Status: "error",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
|
@ -1055,7 +1230,7 @@ func TestAPIClientDo(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
code: statusAPIError,
|
||||
code: http.StatusUnprocessableEntity,
|
||||
response: "bad json",
|
||||
expectedErr: &Error{
|
||||
Type: ErrBadResponse,
|
||||
|
@ -1063,7 +1238,7 @@ func TestAPIClientDo(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
code: statusAPIError,
|
||||
code: http.StatusUnprocessableEntity,
|
||||
response: &apiResponse{
|
||||
Status: "success",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
|
@ -1074,7 +1249,7 @@ func TestAPIClientDo(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
code: statusAPIError,
|
||||
code: http.StatusUnprocessableEntity,
|
||||
response: &apiResponse{
|
||||
Status: "success",
|
||||
Data: json.RawMessage(`"test"`),
|
||||
|
@ -1095,8 +1270,8 @@ func TestAPIClientDo(t *testing.T) {
|
|||
Error: "timed out",
|
||||
},
|
||||
expectedErr: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
Type: ErrTimeout,
|
||||
Msg: "timed out",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1109,8 +1284,8 @@ func TestAPIClientDo(t *testing.T) {
|
|||
Warnings: []string{"a"},
|
||||
},
|
||||
expectedErr: &Error{
|
||||
Type: ErrBadResponse,
|
||||
Msg: "inconsistent body for response code",
|
||||
Type: ErrTimeout,
|
||||
Msg: "timed out",
|
||||
},
|
||||
expectedWarnings: []string{"a"},
|
||||
},
|
||||
|
@ -1325,12 +1500,19 @@ func TestDoGetFallback(t *testing.T) {
|
|||
body, _ := json.Marshal(apiResp)
|
||||
|
||||
if req.Method == http.MethodPost {
|
||||
if req.URL.Path == "/blockPost" {
|
||||
if req.URL.Path == "/blockPost405" {
|
||||
http.Error(w, string(body), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if req.Method == http.MethodPost {
|
||||
if req.URL.Path == "/blockPost501" {
|
||||
http.Error(w, string(body), http.StatusNotImplemented)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Write(body)
|
||||
}))
|
||||
// Close the server when test finishes.
|
||||
|
@ -1361,8 +1543,24 @@ func TestDoGetFallback(t *testing.T) {
|
|||
t.Fatalf("Mismatch in values")
|
||||
}
|
||||
|
||||
// Do a fallbcak to a get.
|
||||
u.Path = "/blockPost"
|
||||
// Do a fallback to a get on 405.
|
||||
u.Path = "/blockPost405"
|
||||
_, b, _, err = api.DoGetFallback(context.TODO(), u, v)
|
||||
if err != nil {
|
||||
t.Fatalf("Error doing local request: %v", err)
|
||||
}
|
||||
if err := json.Unmarshal(b, resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.Method != http.MethodGet {
|
||||
t.Fatalf("Mismatch method")
|
||||
}
|
||||
if resp.Values != v.Encode() {
|
||||
t.Fatalf("Mismatch in values")
|
||||
}
|
||||
|
||||
// Do a fallback to a get on 501.
|
||||
u.Path = "/blockPost501"
|
||||
_, b, _, err = api.DoGetFallback(context.TODO(), u, v)
|
||||
if err != nil {
|
||||
t.Fatalf("Error doing local request: %v", err)
|
||||
|
|
|
@ -18,11 +18,13 @@ package v1_test
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/api"
|
||||
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
"github.com/prometheus/common/config"
|
||||
)
|
||||
|
||||
func ExampleAPI_query() {
|
||||
|
@ -76,6 +78,118 @@ func ExampleAPI_queryRange() {
|
|||
fmt.Printf("Result:\n%v\n", result)
|
||||
}
|
||||
|
||||
type userAgentRoundTripper struct {
|
||||
name string
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
// RoundTrip implements the http.RoundTripper interface.
|
||||
func (u userAgentRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
if r.UserAgent() == "" {
|
||||
// The specification of http.RoundTripper says that it shouldn't mutate
|
||||
// the request so make a copy of req.Header since this is all that is
|
||||
// modified.
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
r2.Header.Set("User-Agent", u.name)
|
||||
r = r2
|
||||
}
|
||||
return u.rt.RoundTrip(r)
|
||||
}
|
||||
|
||||
func ExampleAPI_queryRangeWithUserAgent() {
|
||||
client, err := api.NewClient(api.Config{
|
||||
Address: "http://demo.robustperception.io:9090",
|
||||
RoundTripper: userAgentRoundTripper{name: "Client-Golang", rt: api.DefaultRoundTripper},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating client: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
v1api := v1.NewAPI(client)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
r := v1.Range{
|
||||
Start: time.Now().Add(-time.Hour),
|
||||
End: time.Now(),
|
||||
Step: time.Minute,
|
||||
}
|
||||
result, warnings, err := v1api.QueryRange(ctx, "rate(prometheus_tsdb_head_samples_appended_total[5m])", r)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying Prometheus: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
fmt.Printf("Warnings: %v\n", warnings)
|
||||
}
|
||||
fmt.Printf("Result:\n%v\n", result)
|
||||
}
|
||||
|
||||
func ExampleAPI_queryRangeWithBasicAuth() {
|
||||
client, err := api.NewClient(api.Config{
|
||||
Address: "http://demo.robustperception.io:9090",
|
||||
// We can use amazing github.com/prometheus/common/config helper!
|
||||
RoundTripper: config.NewBasicAuthRoundTripper("me", "defintely_me", "", api.DefaultRoundTripper),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating client: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
v1api := v1.NewAPI(client)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
r := v1.Range{
|
||||
Start: time.Now().Add(-time.Hour),
|
||||
End: time.Now(),
|
||||
Step: time.Minute,
|
||||
}
|
||||
result, warnings, err := v1api.QueryRange(ctx, "rate(prometheus_tsdb_head_samples_appended_total[5m])", r)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying Prometheus: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
fmt.Printf("Warnings: %v\n", warnings)
|
||||
}
|
||||
fmt.Printf("Result:\n%v\n", result)
|
||||
}
|
||||
|
||||
func ExampleAPI_queryRangeWithAuthBearerToken() {
|
||||
client, err := api.NewClient(api.Config{
|
||||
Address: "http://demo.robustperception.io:9090",
|
||||
// We can use amazing github.com/prometheus/common/config helper!
|
||||
RoundTripper: config.NewAuthorizationCredentialsRoundTripper("Bearer", "secret_token", api.DefaultRoundTripper),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating client: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
v1api := v1.NewAPI(client)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
r := v1.Range{
|
||||
Start: time.Now().Add(-time.Hour),
|
||||
End: time.Now(),
|
||||
Step: time.Minute,
|
||||
}
|
||||
result, warnings, err := v1api.QueryRange(ctx, "rate(prometheus_tsdb_head_samples_appended_total[5m])", r)
|
||||
if err != nil {
|
||||
fmt.Printf("Error querying Prometheus: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(warnings) > 0 {
|
||||
fmt.Printf("Warnings: %v\n", warnings)
|
||||
}
|
||||
fmt.Printf("Result:\n%v\n", result)
|
||||
}
|
||||
|
||||
func ExampleAPI_series() {
|
||||
client, err := api.NewClient(api.Config{
|
||||
Address: "http://demo.robustperception.io:9090",
|
||||
|
|
17
go.mod
17
go.mod
|
@ -3,17 +3,12 @@ module github.com/prometheus/client_golang
|
|||
require (
|
||||
github.com/beorn7/perks v1.0.1
|
||||
github.com/cespare/xxhash/v2 v2.1.1
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/prometheus/client_model v0.2.1-0.20200406191659-4b803f3550a4
|
||||
github.com/prometheus/common v0.9.1
|
||||
github.com/prometheus/procfs v0.0.8
|
||||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.5 // indirect
|
||||
github.com/prometheus/common v0.18.0
|
||||
github.com/prometheus/procfs v0.6.0
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2
|
||||
)
|
||||
|
||||
go 1.11
|
||||
go 1.13
|
||||
|
|
368
go.sum
368
go.sum
|
@ -1,128 +1,416 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
|
||||
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.1-0.20200406191659-4b803f3550a4 h1:7Ws+6l4/5eJPHAxe0Axwo4XJwSAA4i0ipEjuoLXWFyo=
|
||||
github.com/prometheus/client_model v0.2.1-0.20200406191659-4b803f3550a4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y=
|
||||
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5 h1:mzjBh+S5frKOsOBobWIMAbXavqjmgO17k/2puhcFR94=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
|
||||
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2019 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.12
|
||||
|
||||
package prometheus
|
||||
|
||||
import "runtime/debug"
|
||||
|
||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go 1.12+.
|
||||
func readBuildInfo() (path, version, sum string) {
|
||||
path, version, sum = "unknown", "unknown", "unknown"
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
path = bi.Main.Path
|
||||
version = bi.Main.Version
|
||||
sum = bi.Main.Sum
|
||||
}
|
||||
return
|
||||
}
|
|
@ -163,7 +163,7 @@ func (c *counter) updateExemplar(v float64, l Labels) {
|
|||
// (e.g. number of HTTP requests, partitioned by response code and
|
||||
// method). Create instances with NewCounterVec.
|
||||
type CounterVec struct {
|
||||
*metricVec
|
||||
*MetricVec
|
||||
}
|
||||
|
||||
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
|
||||
|
@ -176,11 +176,11 @@ 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 {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||
}
|
||||
result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs), now: time.Now}
|
||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||
result.init(result) // Init self-collection.
|
||||
return result
|
||||
}),
|
||||
|
@ -188,7 +188,7 @@ 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
|
||||
// values (same order as the variable labels 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
|
||||
|
@ -202,7 +202,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
|||
// Counter with the same label values is created later.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
|
@ -211,7 +211,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
|
|||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Counter), err
|
||||
}
|
||||
|
@ -219,19 +219,19 @@ func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
|
|||
}
|
||||
|
||||
// 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
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Counter is created. Implications of
|
||||
// creating a Counter without using it and keeping the Counter for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Counter), err
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ func (v *CounterVec) With(labels Labels) Counter {
|
|||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &CounterVec{vec}, err
|
||||
}
|
||||
|
@ -309,6 +309,8 @@ type CounterFunc interface {
|
|||
// provided function must be concurrency-safe. The function should also honor
|
||||
// the contract for a Counter (values only go up, not down), but compliance will
|
||||
// not be checked.
|
||||
//
|
||||
// Check out the ExampleGaugeFunc examples for the similar GaugeFunc.
|
||||
func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc {
|
||||
return newValueFunc(NewDesc(
|
||||
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
|
@ -50,7 +51,7 @@ type Desc struct {
|
|||
// constLabelPairs contains precalculated DTO label pairs based on
|
||||
// the constant labels.
|
||||
constLabelPairs []*dto.LabelPair
|
||||
// VariableLabels contains names of labels for which the metric
|
||||
// variableLabels contains names of labels for which the metric
|
||||
// maintains variable values.
|
||||
variableLabels []string
|
||||
// id is a hash of the values of the ConstLabels and fqName. This
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright 2020 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 (
|
||||
"fmt"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// Info implements an info pseudo-metric, which is modeled as a Gauge that
|
||||
// always has a value of 1. In practice, you would just use a Gauge directly,
|
||||
// but for this example, we pretend it would be useful to have a “native”
|
||||
// implementation.
|
||||
type Info struct {
|
||||
desc *prometheus.Desc
|
||||
labelPairs []*dto.LabelPair
|
||||
}
|
||||
|
||||
func (i Info) Desc() *prometheus.Desc {
|
||||
return i.desc
|
||||
}
|
||||
|
||||
func (i Info) Write(out *dto.Metric) error {
|
||||
out.Label = i.labelPairs
|
||||
out.Gauge = &dto.Gauge{Value: proto.Float64(1)}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InfoVec is the vector version for Info. As an info metric never changes, we
|
||||
// wouldn't really need to wrap GetMetricWithLabelValues and GetMetricWith
|
||||
// because Info has no additional methods compared to the vanilla Metric that
|
||||
// the unwrapped MetricVec methods return. However, to demonstrate all there is
|
||||
// to do to fully implement a vector for a custom Metric implementation, we do
|
||||
// it in this example anyway.
|
||||
type InfoVec struct {
|
||||
*prometheus.MetricVec
|
||||
}
|
||||
|
||||
func NewInfoVec(name, help string, labelNames []string) *InfoVec {
|
||||
desc := prometheus.NewDesc(name, help, labelNames, nil)
|
||||
return &InfoVec{
|
||||
MetricVec: prometheus.NewMetricVec(desc, func(lvs ...string) prometheus.Metric {
|
||||
if len(lvs) != len(labelNames) {
|
||||
panic("inconsistent label cardinality")
|
||||
}
|
||||
return Info{desc: desc, labelPairs: prometheus.MakeLabelPairs(desc, lvs)}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *InfoVec) GetMetricWithLabelValues(lvs ...string) (Info, error) {
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
return metric.(Info), err
|
||||
}
|
||||
|
||||
func (v *InfoVec) GetMetricWith(labels prometheus.Labels) (Info, error) {
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
return metric.(Info), err
|
||||
}
|
||||
|
||||
func (v *InfoVec) WithLabelValues(lvs ...string) Info {
|
||||
i, err := v.GetMetricWithLabelValues(lvs...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (v *InfoVec) With(labels prometheus.Labels) Info {
|
||||
i, err := v.GetMetricWith(labels)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (v *InfoVec) CurryWith(labels prometheus.Labels) (*InfoVec, error) {
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &InfoVec{vec}, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (v *InfoVec) MustCurryWith(labels prometheus.Labels) *InfoVec {
|
||||
vec, err := v.CurryWith(labels)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vec
|
||||
}
|
||||
|
||||
func ExampleMetricVec() {
|
||||
|
||||
infoVec := NewInfoVec(
|
||||
"library_version_info",
|
||||
"Versions of the libraries used in this binary.",
|
||||
[]string{"library", "version"},
|
||||
)
|
||||
|
||||
infoVec.WithLabelValues("prometheus/client_golang", "1.7.1")
|
||||
infoVec.WithLabelValues("k8s.io/client-go", "0.18.8")
|
||||
|
||||
// Just for demonstration, let's check the state of the InfoVec by
|
||||
// registering it with a custom registry and then let it collect the
|
||||
// metrics.
|
||||
reg := prometheus.NewRegistry()
|
||||
reg.MustRegister(infoVec)
|
||||
|
||||
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: "library_version_info"
|
||||
// help: "Versions of the libraries used in this binary."
|
||||
// type: GAUGE
|
||||
// metric: <
|
||||
// label: <
|
||||
// name: "library"
|
||||
// value: "k8s.io/client-go"
|
||||
// >
|
||||
// label: <
|
||||
// name: "version"
|
||||
// value: "0.18.8"
|
||||
// >
|
||||
// gauge: <
|
||||
// value: 1
|
||||
// >
|
||||
// >
|
||||
// metric: <
|
||||
// label: <
|
||||
// name: "library"
|
||||
// value: "prometheus/client_golang"
|
||||
// >
|
||||
// label: <
|
||||
// name: "version"
|
||||
// value: "1.7.1"
|
||||
// >
|
||||
// gauge: <
|
||||
// value: 1
|
||||
// >
|
||||
// >
|
||||
}
|
|
@ -22,6 +22,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
|
@ -72,7 +73,7 @@ func ExampleGaugeVec() {
|
|||
opsQueued.With(prometheus.Labels{"type": "delete", "user": "alice"}).Inc()
|
||||
}
|
||||
|
||||
func ExampleGaugeFunc() {
|
||||
func ExampleGaugeFunc_simple() {
|
||||
if err := prometheus.Register(prometheus.NewGaugeFunc(
|
||||
prometheus.GaugeOpts{
|
||||
Subsystem: "runtime",
|
||||
|
@ -90,6 +91,44 @@ func ExampleGaugeFunc() {
|
|||
// GaugeFunc 'goroutines_count' registered.
|
||||
}
|
||||
|
||||
func ExampleGaugeFunc_constLabels() {
|
||||
// primaryDB and secondaryDB represent two example *sql.DB connections we want to instrument.
|
||||
var primaryDB, secondaryDB interface {
|
||||
Stats() struct{ OpenConnections int }
|
||||
}
|
||||
|
||||
if err := prometheus.Register(prometheus.NewGaugeFunc(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "mysql",
|
||||
Name: "connections_open",
|
||||
Help: "Number of mysql connections open.",
|
||||
ConstLabels: prometheus.Labels{"destination": "primary"},
|
||||
},
|
||||
func() float64 { return float64(primaryDB.Stats().OpenConnections) },
|
||||
)); err == nil {
|
||||
fmt.Println(`GaugeFunc 'connections_open' for primary DB connection registered with labels {destination="primary"}`)
|
||||
}
|
||||
|
||||
if err := prometheus.Register(prometheus.NewGaugeFunc(
|
||||
prometheus.GaugeOpts{
|
||||
Namespace: "mysql",
|
||||
Name: "connections_open",
|
||||
Help: "Number of mysql connections open.",
|
||||
ConstLabels: prometheus.Labels{"destination": "secondary"},
|
||||
},
|
||||
func() float64 { return float64(secondaryDB.Stats().OpenConnections) },
|
||||
)); err == nil {
|
||||
fmt.Println(`GaugeFunc 'connections_open' for secondary DB connection registered with labels {destination="secondary"}`)
|
||||
}
|
||||
|
||||
// Note that we can register more than once GaugeFunc with same metric name
|
||||
// as long as their const labels are consistent.
|
||||
|
||||
// Output:
|
||||
// GaugeFunc 'connections_open' for primary DB connection registered with labels {destination="primary"}
|
||||
// GaugeFunc 'connections_open' for secondary DB connection registered with labels {destination="secondary"}
|
||||
}
|
||||
|
||||
func ExampleCounterVec() {
|
||||
httpReqs := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
|
|
|
@ -132,7 +132,7 @@ func (g *gauge) Write(out *dto.Metric) error {
|
|||
// (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
|
||||
|
@ -145,11 +145,11 @@ 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 {
|
||||
if len(lvs) != len(desc.variableLabels) {
|
||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
|
||||
}
|
||||
result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
|
||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
||||
result.init(result) // Init self-collection.
|
||||
return result
|
||||
}),
|
||||
|
@ -157,7 +157,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
|||
}
|
||||
|
||||
// GetMetricWithLabelValues returns the Gauge for the given slice of label
|
||||
// values (same order as the VariableLabels in Desc). If that combination of
|
||||
// values (same order as the variable labels 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
|
||||
|
@ -172,7 +172,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
|||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
|
@ -180,7 +180,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
|
|||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Gauge), err
|
||||
}
|
||||
|
@ -188,19 +188,19 @@ func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
|
|||
}
|
||||
|
||||
// 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
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Gauge is created. Implications of
|
||||
// creating a Gauge without using it and keeping the Gauge for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Gauge), err
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ func (v *GaugeVec) With(labels Labels) Gauge {
|
|||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &GaugeVec{vec}, err
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ func TestGaugeVecConcurrency(t *testing.T) {
|
|||
start.Wait()
|
||||
for i, v := range vals {
|
||||
sStreams[pick[i]] <- v
|
||||
gge.WithLabelValues(string('A' + pick[i])).Add(v)
|
||||
gge.WithLabelValues(string('A' + rune(pick[i]))).Add(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
|
@ -147,7 +147,7 @@ func TestGaugeVecConcurrency(t *testing.T) {
|
|||
start.Done()
|
||||
|
||||
for i := range sStreams {
|
||||
if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*gauge).valBits); math.Abs(expected-got) > 0.000001 {
|
||||
if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+rune(i))).(*gauge).valBits); math.Abs(expected-got) > 0.000001 {
|
||||
t.Fatalf("expected approx. %f, got %f", expected, got)
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -58,9 +58,10 @@ type goCollector struct {
|
|||
// collector will use the memstats from a previous collection if
|
||||
// runtime.ReadMemStats takes more than 1s. However, if there are no previously
|
||||
// collected memstats, or their collection is more than 5m ago, the collection
|
||||
// will block until runtime.ReadMemStats succeeds. (The problem might be solved
|
||||
// in Go1.13, see https://github.com/golang/go/issues/19812 for the related Go
|
||||
// issue.)
|
||||
// will block until runtime.ReadMemStats succeeds.
|
||||
//
|
||||
// NOTE: The problem is solved in Go 1.15, see
|
||||
// https://github.com/golang/go/issues/19812 for the related Go issue.
|
||||
func NewGoCollector() Collector {
|
||||
return &goCollector{
|
||||
goroutinesDesc: NewDesc(
|
||||
|
@ -383,7 +384,12 @@ type memStatsMetrics []struct {
|
|||
// https://github.com/povilasv/prommod for an example of a collector for the
|
||||
// module dependencies.
|
||||
func NewBuildInfoCollector() Collector {
|
||||
path, version, sum := readBuildInfo()
|
||||
path, version, sum := "unknown", "unknown", "unknown"
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
path = bi.Main.Path
|
||||
version = bi.Main.Version
|
||||
sum = bi.Main.Sum
|
||||
}
|
||||
c := &selfCollector{MustNewConstMetric(
|
||||
NewDesc(
|
||||
"go_build_info",
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
@ -215,7 +216,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
|
|||
upperBounds: opts.Buckets,
|
||||
sparseResolution: uint32(opts.SparseBucketsResolution),
|
||||
sparseThreshold: opts.SparseBucketsZeroThreshold,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
counts: [2]*histogramCounts{{}, {}},
|
||||
now: time.Now,
|
||||
}
|
||||
|
@ -565,7 +566,7 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
|
|||
// (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
|
||||
|
@ -578,14 +579,14 @@ 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
|
||||
// values (same order as the variable labels 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
|
||||
|
@ -600,7 +601,7 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
|||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
|
@ -609,7 +610,7 @@ func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
|
|||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
|
@ -617,19 +618,19 @@ func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error)
|
|||
}
|
||||
|
||||
// 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
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Histogram is created. Implications of
|
||||
// creating a Histogram without using it and keeping the Histogram for later use
|
||||
// are the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
|
@ -673,7 +674,7 @@ func (v *HistogramVec) With(labels Labels) Observer {
|
|||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *HistogramVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &HistogramVec{vec}, err
|
||||
}
|
||||
|
@ -758,12 +759,12 @@ func NewConstHistogram(
|
|||
count: count,
|
||||
sum: sum,
|
||||
buckets: buckets,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MustNewConstHistogram is a version of NewConstHistogram that panics where
|
||||
// NewConstMetric would have returned an error.
|
||||
// NewConstHistogram would have returned an error.
|
||||
func MustNewConstHistogram(
|
||||
desc *Desc,
|
||||
count uint64,
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"testing/quick"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
|
@ -278,7 +279,7 @@ func TestHistogramVecConcurrency(t *testing.T) {
|
|||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for i, v := range vals {
|
||||
his.WithLabelValues(string('A' + picks[i])).Observe(v)
|
||||
his.WithLabelValues(string('A' + rune(picks[i]))).Observe(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
|
@ -291,7 +292,7 @@ func TestHistogramVecConcurrency(t *testing.T) {
|
|||
|
||||
for i := 0; i < vecLength; i++ {
|
||||
m := &dto.Metric{}
|
||||
s := his.WithLabelValues(string('A' + i))
|
||||
s := his.WithLabelValues(string('A' + rune(i)))
|
||||
s.(Histogram).Write(m)
|
||||
|
||||
if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
|
@ -57,7 +58,7 @@ type Metric interface {
|
|||
}
|
||||
|
||||
// Opts bundles the options for creating most Metric types. Each metric
|
||||
// implementation XXX has its own XXXOpts type, but in most cases, it is just be
|
||||
// implementation XXX has its own XXXOpts type, but in most cases, it is just
|
||||
// an alias of this type (which might change when the requirement arises.)
|
||||
//
|
||||
// It is mandatory to set Name to a non-empty string. All other fields are
|
||||
|
@ -88,7 +89,7 @@ type Opts struct {
|
|||
// better covered by target labels set by the scraping Prometheus
|
||||
// server, or by one specific metric (e.g. a build_info or a
|
||||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,11 @@ package prometheus
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type processCollector struct {
|
||||
|
@ -149,3 +153,20 @@ func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error)
|
|||
}
|
||||
ch <- NewInvalidMetric(desc, err)
|
||||
}
|
||||
|
||||
// NewPidFileFn returns a function that retrieves a pid from the specified file.
|
||||
// It is meant to be used for the PidFn field in ProcessCollectorOpts.
|
||||
func NewPidFileFn(pidFilePath string) func() (int, error) {
|
||||
return func() (int, error) {
|
||||
content, err := ioutil.ReadFile(pidFilePath)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("can't read pid file %q: %+v", pidFilePath, err)
|
||||
}
|
||||
pid, err := strconv.Atoi(strings.TrimSpace(string(content)))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("can't parse pid file %q: %+v", pidFilePath, err)
|
||||
}
|
||||
|
||||
return pid, nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ package prometheus
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
@ -101,3 +104,64 @@ func TestProcessCollector(t *testing.T) {
|
|||
t.Errorf("%d metrics collected, want 1", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPidFileFn(t *testing.T) {
|
||||
folderPath, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Error("failed to get current path")
|
||||
}
|
||||
mockPidFilePath := filepath.Join(folderPath, "mockPidFile")
|
||||
defer os.Remove(mockPidFilePath)
|
||||
|
||||
testCases := []struct {
|
||||
mockPidFile func()
|
||||
expectedErrPrefix string
|
||||
expectedPid int
|
||||
desc string
|
||||
}{
|
||||
{
|
||||
mockPidFile: func() {
|
||||
os.Remove(mockPidFilePath)
|
||||
},
|
||||
expectedErrPrefix: "can't read pid file",
|
||||
expectedPid: 0,
|
||||
desc: "no existed pid file",
|
||||
},
|
||||
{
|
||||
mockPidFile: func() {
|
||||
os.Remove(mockPidFilePath)
|
||||
f, _ := os.Create(mockPidFilePath)
|
||||
f.Write([]byte("abc"))
|
||||
f.Close()
|
||||
},
|
||||
expectedErrPrefix: "can't parse pid file",
|
||||
expectedPid: 0,
|
||||
desc: "existed pid file, error pid number",
|
||||
},
|
||||
{
|
||||
mockPidFile: func() {
|
||||
os.Remove(mockPidFilePath)
|
||||
f, _ := os.Create(mockPidFilePath)
|
||||
f.Write([]byte("123"))
|
||||
f.Close()
|
||||
},
|
||||
expectedErrPrefix: "",
|
||||
expectedPid: 123,
|
||||
desc: "existed pid file, correct pid number",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
fn := NewPidFileFn(mockPidFilePath)
|
||||
if fn == nil {
|
||||
t.Error("Should not get nil PidFileFn")
|
||||
}
|
||||
|
||||
tc.mockPidFile()
|
||||
|
||||
if pid, err := fn(); pid != tc.expectedPid || (err != nil && !strings.HasPrefix(err.Error(), tc.expectedErrPrefix)) {
|
||||
fmt.Println(err.Error())
|
||||
t.Error(tc.desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,18 +33,22 @@ var (
|
|||
)
|
||||
|
||||
type processMemoryCounters struct {
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-_process_memory_counters_ex
|
||||
// System interface description
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-process_memory_counters_ex
|
||||
|
||||
// Refer to the Golang internal implementation
|
||||
// https://golang.org/src/internal/syscall/windows/psapi_windows.go
|
||||
_ uint32
|
||||
PageFaultCount uint32
|
||||
PeakWorkingSetSize uint64
|
||||
WorkingSetSize uint64
|
||||
QuotaPeakPagedPoolUsage uint64
|
||||
QuotaPagedPoolUsage uint64
|
||||
QuotaPeakNonPagedPoolUsage uint64
|
||||
QuotaNonPagedPoolUsage uint64
|
||||
PagefileUsage uint64
|
||||
PeakPagefileUsage uint64
|
||||
PrivateUsage uint64
|
||||
PeakWorkingSetSize uintptr
|
||||
WorkingSetSize uintptr
|
||||
QuotaPeakPagedPoolUsage uintptr
|
||||
QuotaPagedPoolUsage uintptr
|
||||
QuotaPeakNonPagedPoolUsage uintptr
|
||||
QuotaNonPagedPoolUsage uintptr
|
||||
PagefileUsage uintptr
|
||||
PeakPagefileUsage uintptr
|
||||
PrivateUsage uintptr
|
||||
}
|
||||
|
||||
func getProcessMemoryInfo(handle windows.Handle) (processMemoryCounters, error) {
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
// requestCount = promauto.With(reg).NewCounterVec(
|
||||
// prometheus.CounterOpts{
|
||||
// Name: "http_requests_total",
|
||||
// Help: "Total number of HTTP requests by status code end method.",
|
||||
// Help: "Total number of HTTP requests by status code and method.",
|
||||
// },
|
||||
// []string{"code", "method"},
|
||||
// )
|
||||
|
@ -117,7 +117,7 @@
|
|||
// requestCount = factory.NewCounterVec(
|
||||
// prometheus.CounterOpts{
|
||||
// Name: "http_requests_total",
|
||||
// Help: "Total number of HTTP requests by status code end method.",
|
||||
// Help: "Total number of HTTP requests by status code and method.",
|
||||
// },
|
||||
// []string{"code", "method"},
|
||||
// )
|
||||
|
@ -127,29 +127,30 @@
|
|||
// separate package?
|
||||
//
|
||||
// The main problem is that registration may fail, e.g. if a metric inconsistent
|
||||
// with the newly to be registered one is already registered. Therefore, the
|
||||
// Register method in the prometheus.Registerer interface returns an error, and
|
||||
// the same is the case for the top-level prometheus.Register function that
|
||||
// registers with the global registry. The prometheus package also provides
|
||||
// MustRegister versions for both. They panic if the registration fails, and
|
||||
// they clearly call this out by using the Must… idiom. Panicking is a bit
|
||||
// problematic here because it doesn't just happen on input provided by the
|
||||
// caller that is invalid on its own. Things are a bit more subtle here: Metric
|
||||
// creation and registration tend to be spread widely over the codebase. It can
|
||||
// easily happen that an incompatible metric is added to an unrelated part of
|
||||
// the code, and suddenly code that used to work perfectly fine starts to panic
|
||||
// (provided that the registration of the newly added metric happens before the
|
||||
// registration of the previously existing metric). This may come as an even
|
||||
// bigger surprise with the global registry, where simply importing another
|
||||
// package can trigger a panic (if the newly imported package registers metrics
|
||||
// in its init function). At least, in the prometheus package, creation of
|
||||
// metrics and other collectors is separate from registration. You first create
|
||||
// the metric, and then you decide explicitly if you want to register it with a
|
||||
// local or the global registry, and if you want to handle the error or risk a
|
||||
// panic. With the constructors in the promauto package, registration is
|
||||
// automatic, and if it fails, it will always panic. Furthermore, the
|
||||
// constructors will often be called in the var section of a file, which means
|
||||
// that panicking will happen as a side effect of merely importing a package.
|
||||
// with or equal to the newly to be registered one is already registered.
|
||||
// Therefore, the Register method in the prometheus.Registerer interface returns
|
||||
// an error, and the same is the case for the top-level prometheus.Register
|
||||
// function that registers with the global registry. The prometheus package also
|
||||
// provides MustRegister versions for both. They panic if the registration
|
||||
// fails, and they clearly call this out by using the Must… idiom. Panicking is
|
||||
// problematic in this case because it doesn't just happen on input provided by
|
||||
// the caller that is invalid on its own. Things are a bit more subtle here:
|
||||
// Metric creation and registration tend to be spread widely over the
|
||||
// codebase. It can easily happen that an incompatible metric is added to an
|
||||
// unrelated part of the code, and suddenly code that used to work perfectly
|
||||
// fine starts to panic (provided that the registration of the newly added
|
||||
// metric happens before the registration of the previously existing
|
||||
// metric). This may come as an even bigger surprise with the global registry,
|
||||
// where simply importing another package can trigger a panic (if the newly
|
||||
// imported package registers metrics in its init function). At least, in the
|
||||
// prometheus package, creation of metrics and other collectors is separate from
|
||||
// registration. You first create the metric, and then you decide explicitly if
|
||||
// you want to register it with a local or the global registry, and if you want
|
||||
// to handle the error or risk a panic. With the constructors in the promauto
|
||||
// package, registration is automatic, and if it fails, it will always
|
||||
// panic. Furthermore, the constructors will often be called in the var section
|
||||
// of a file, which means that panicking will happen as a side effect of merely
|
||||
// importing a package.
|
||||
//
|
||||
// A separate package allows conservative users to entirely ignore it. And
|
||||
// whoever wants to use it, will do so explicitly, with an opportunity to read
|
||||
|
@ -252,7 +253,8 @@ type Factory struct {
|
|||
}
|
||||
|
||||
// With creates a Factory using the provided Registerer for registration of the
|
||||
// created Collectors.
|
||||
// created Collectors. If the provided Registerer is nil, the returned Factory
|
||||
// creates Collectors that are not registered with any Registerer.
|
||||
func With(r prometheus.Registerer) Factory { return Factory{r} }
|
||||
|
||||
// NewCounter works like the function of the same name in the prometheus package
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019 The Prometheus Authors
|
||||
// Copyright 2020 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
@ -11,12 +11,15 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !go1.12
|
||||
package promauto
|
||||
|
||||
package prometheus
|
||||
import (
|
||||
"testing"
|
||||
|
||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go versions before
|
||||
// 1.12. Remove this whole file once the minimum supported Go version is 1.12.
|
||||
func readBuildInfo() (path, version, sum string) {
|
||||
return "unknown", "unknown", "unknown"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func TestNil(t *testing.T) {
|
||||
// A nil registerer should be treated as a no-op by promauto.
|
||||
With(nil).NewCounter(prometheus.CounterOpts{Name: "test"}).Inc()
|
||||
}
|
|
@ -53,12 +53,16 @@ func (r *responseWriterDelegator) Written() int64 {
|
|||
}
|
||||
|
||||
func (r *responseWriterDelegator) WriteHeader(code int) {
|
||||
if r.observeWriteHeader != nil && !r.wroteHeader {
|
||||
// Only call observeWriteHeader for the 1st time. It's a bug if
|
||||
// WriteHeader is called more than once, but we want to protect
|
||||
// against it here. Note that we still delegate the WriteHeader
|
||||
// to the original ResponseWriter to not mask the bug from it.
|
||||
r.observeWriteHeader(code)
|
||||
}
|
||||
r.status = code
|
||||
r.wroteHeader = true
|
||||
r.ResponseWriter.WriteHeader(code)
|
||||
if r.observeWriteHeader != nil {
|
||||
r.observeWriteHeader(code)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *responseWriterDelegator) Write(b []byte) (int, error) {
|
||||
|
@ -79,8 +83,7 @@ type readerFromDelegator struct{ *responseWriterDelegator }
|
|||
type pusherDelegator struct{ *responseWriterDelegator }
|
||||
|
||||
func (d closeNotifierDelegator) CloseNotify() <-chan bool {
|
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
//nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users.
|
||||
return d.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
func (d flusherDelegator) Flush() {
|
||||
|
@ -344,8 +347,7 @@ func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) deleg
|
|||
}
|
||||
|
||||
id := 0
|
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
//nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users.
|
||||
if _, ok := w.(http.CloseNotifier); ok {
|
||||
id += closeNotifier
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
|||
inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight)
|
||||
}
|
||||
if opts.Registry != nil {
|
||||
// Initialize all possibilites that can occur below.
|
||||
// Initialize all possibilities that can occur below.
|
||||
errCnt.WithLabelValues("gathering")
|
||||
errCnt.WithLabelValues("encoding")
|
||||
if err := opts.Registry.Register(errCnt); err != nil {
|
||||
|
@ -167,15 +167,12 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
|||
|
||||
enc := expfmt.NewEncoder(w, contentType)
|
||||
|
||||
var lastErr error
|
||||
|
||||
// handleError handles the error according to opts.ErrorHandling
|
||||
// and returns true if we have to abort after the handling.
|
||||
handleError := func(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
lastErr = err
|
||||
if opts.ErrorLog != nil {
|
||||
opts.ErrorLog.Println("error encoding and sending metric family:", err)
|
||||
}
|
||||
|
@ -184,7 +181,10 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
|||
case PanicOnError:
|
||||
panic(err)
|
||||
case HTTPErrorOnError:
|
||||
httpError(rsp, err)
|
||||
// We cannot really send an HTTP error at this
|
||||
// point because we most likely have written
|
||||
// something to rsp already. But at least we can
|
||||
// stop sending.
|
||||
return true
|
||||
}
|
||||
// Do nothing in all other cases, including ContinueOnError.
|
||||
|
@ -202,10 +202,6 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
if lastErr != nil {
|
||||
httpError(rsp, lastErr)
|
||||
}
|
||||
})
|
||||
|
||||
if opts.Timeout <= 0 {
|
||||
|
@ -276,7 +272,12 @@ type HandlerErrorHandling int
|
|||
// errors are encountered.
|
||||
const (
|
||||
// Serve an HTTP status code 500 upon the first error
|
||||
// encountered. Report the error message in the body.
|
||||
// encountered. Report the error message in the body. Note that HTTP
|
||||
// errors cannot be served anymore once the beginning of a regular
|
||||
// payload has been sent. Thus, in the (unlikely) case that encoding the
|
||||
// payload into the negotiated wire format fails, serving the response
|
||||
// will simply be aborted. Set an ErrorLog in HandlerOpts to detect
|
||||
// those errors.
|
||||
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
|
||||
|
@ -302,8 +303,12 @@ type Logger 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 specifies an optional Logger for errors collecting and
|
||||
// serving metrics. If nil, errors are not logged at all. Note that the
|
||||
// type of a reported error is often prometheus.MultiError, which
|
||||
// formats into a multi-line error string. If you want to avoid the
|
||||
// latter, create a Logger implementation that detects a
|
||||
// prometheus.MultiError and formats the contained errors into one line.
|
||||
ErrorLog Logger
|
||||
// ErrorHandling defines how errors are handled. Note that errors are
|
||||
// logged regardless of the configured ErrorHandling provided ErrorLog
|
||||
|
@ -365,11 +370,9 @@ func gzipAccepted(header http.Header) bool {
|
|||
}
|
||||
|
||||
// httpError removes any content-encoding header and then calls http.Error with
|
||||
// the provided error and http.StatusInternalServerErrer. Error contents is
|
||||
// supposed to be uncompressed plain text. However, same as with a plain
|
||||
// http.Error, any header settings will be void if the header has already been
|
||||
// sent. The error message will still be written to the writer, but it will
|
||||
// probably be of limited use.
|
||||
// the provided error and http.StatusInternalServerError. Error contents is
|
||||
// supposed to be uncompressed plain text. Same as with a plain http.Error, this
|
||||
// must not be called if the header or any payload has already been sent.
|
||||
func httpError(rsp http.ResponseWriter, err error) {
|
||||
rsp.Header().Del(contentEncodingHeader)
|
||||
http.Error(
|
||||
|
|
|
@ -15,10 +15,10 @@ package promhttp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -186,8 +186,9 @@ func TestClientMiddlewareAPIWithRequestContextTimeout(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatal("did not get timeout error")
|
||||
}
|
||||
if want, got := fmt.Sprintf("Get %s: context deadline exceeded", backend.URL), err.Error(); want != got {
|
||||
t.Fatalf("want error %q, got %q", want, got)
|
||||
expectedMsg := "context deadline exceeded"
|
||||
if !strings.Contains(err.Error(), expectedMsg) {
|
||||
t.Fatalf("unexpected error: %q, expect error: %q", err.Error(), expectedMsg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,14 +43,14 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl
|
|||
|
||||
// InstrumentHandlerDuration is a middleware that wraps the provided
|
||||
// http.Handler to observe the request duration with the provided ObserverVec.
|
||||
// The ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||
// called with the request duration in seconds. Partitioning happens by HTTP
|
||||
// status code and/or HTTP method if the respective instance label names are
|
||||
// present in the ObserverVec. For unpartitioned observations, use an
|
||||
// ObserverVec with zero labels. Note that partitioning of Histograms is
|
||||
// expensive and should be used judiciously.
|
||||
// The ObserverVec must have valid metric and label names and must have zero,
|
||||
// one, or two non-const non-curried labels. For those, the only allowed label
|
||||
// names are "code" and "method". The function panics otherwise. The Observe
|
||||
// method of the Observer in the ObserverVec is called with the request duration
|
||||
// in seconds. Partitioning happens by HTTP status code and/or HTTP method if
|
||||
// the respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
|
@ -79,12 +79,13 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) ht
|
|||
}
|
||||
|
||||
// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler
|
||||
// to observe the request result with the provided CounterVec. The CounterVec
|
||||
// must have zero, one, or two non-const non-curried labels. For those, the only
|
||||
// allowed label names are "code" and "method". The function panics
|
||||
// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or
|
||||
// HTTP method if the respective instance label names are present in the
|
||||
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||
// to observe the request result with the provided CounterVec. The CounterVec
|
||||
// must have valid metric and label names and must have zero, one, or two
|
||||
// non-const non-curried labels. For those, the only allowed label names are
|
||||
// "code" and "method". The function panics otherwise. Partitioning of the
|
||||
// CounterVec happens by HTTP status code and/or HTTP method if the respective
|
||||
// instance label names are present in the CounterVec. For unpartitioned
|
||||
// counting, use a CounterVec with zero labels.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
|
@ -110,14 +111,15 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler)
|
|||
|
||||
// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
|
||||
// http.Handler to observe with the provided ObserverVec the request duration
|
||||
// until the response headers are written. The ObserverVec must have zero, one,
|
||||
// or two non-const non-curried labels. For those, the only allowed label names
|
||||
// are "code" and "method". The function panics otherwise. The Observe method of
|
||||
// the Observer in the ObserverVec is called with the request duration in
|
||||
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
// until the response headers are written. The ObserverVec must have valid
|
||||
// metric and label names and must have zero, one, or two non-const non-curried
|
||||
// labels. For those, the only allowed label names are "code" and "method". The
|
||||
// function panics otherwise. The Observe method of the Observer in the
|
||||
// ObserverVec is called with the request duration in seconds. Partitioning
|
||||
// happens by HTTP status code and/or HTTP method if the respective instance
|
||||
// label names are present in the ObserverVec. For unpartitioned observations,
|
||||
// use an ObserverVec with zero labels. Note that partitioning of Histograms is
|
||||
// expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler panics before calling WriteHeader, no value is
|
||||
// reported.
|
||||
|
@ -139,15 +141,15 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha
|
|||
}
|
||||
|
||||
// InstrumentHandlerRequestSize is a middleware that wraps the provided
|
||||
// http.Handler to observe the request size with the provided ObserverVec. The
|
||||
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||
// called with the request size in bytes. Partitioning happens by HTTP status
|
||||
// code and/or HTTP method if the respective instance label names are present in
|
||||
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||
// judiciously.
|
||||
// http.Handler to observe the request size with the provided ObserverVec. The
|
||||
// ObserverVec must have valid metric and label names and must have zero, one,
|
||||
// or two non-const non-curried labels. For those, the only allowed label names
|
||||
// are "code" and "method". The function panics otherwise. The Observe method of
|
||||
// the Observer in the ObserverVec is called with the request size in
|
||||
// bytes. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
|
@ -174,15 +176,15 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler)
|
|||
}
|
||||
|
||||
// InstrumentHandlerResponseSize is a middleware that wraps the provided
|
||||
// http.Handler to observe the response size with the provided ObserverVec. The
|
||||
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||
// those, the only allowed label names are "code" and "method". The function
|
||||
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||
// called with the response size in bytes. Partitioning happens by HTTP status
|
||||
// code and/or HTTP method if the respective instance label names are present in
|
||||
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||
// judiciously.
|
||||
// http.Handler to observe the response size with the provided ObserverVec. The
|
||||
// ObserverVec must have valid metric and label names and must have zero, one,
|
||||
// or two non-const non-curried labels. For those, the only allowed label names
|
||||
// are "code" and "method". The function panics otherwise. The Observe method of
|
||||
// the Observer in the ObserverVec is called with the response size in
|
||||
// bytes. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||
// respective instance label names are present in the ObserverVec. For
|
||||
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||
// partitioning of Histograms is expensive and should be used judiciously.
|
||||
//
|
||||
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||
//
|
||||
|
@ -198,6 +200,11 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler
|
|||
})
|
||||
}
|
||||
|
||||
// checkLabels returns whether the provided Collector has a non-const,
|
||||
// non-curried label named "code" and/or "method". It panics if the provided
|
||||
// Collector does not have a Desc or has more than one Desc or its Desc is
|
||||
// invalid. It also panics if the Collector has any non-const, non-curried
|
||||
// labels that are not named "code" or "method".
|
||||
func checkLabels(c prometheus.Collector) (code bool, method bool) {
|
||||
// TODO(beorn7): Remove this hacky way to check for instance labels
|
||||
// once Descriptors can have their dimensionality queried.
|
||||
|
@ -225,6 +232,10 @@ func checkLabels(c prometheus.Collector) (code bool, method bool) {
|
|||
|
||||
close(descc)
|
||||
|
||||
// Make sure the Collector has a valid Desc by registering it with a
|
||||
// temporary registry.
|
||||
prometheus.NewRegistry().MustRegister(c)
|
||||
|
||||
// Create a ConstMetric with the Desc. Since we don't know how many
|
||||
// variable labels there are, try for as long as it needs.
|
||||
for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
func TestLabelCheck(t *testing.T) {
|
||||
scenarios := map[string]struct {
|
||||
metricName string // Defaults to "c".
|
||||
varLabels []string
|
||||
constLabels []string
|
||||
curriedLabels []string
|
||||
|
@ -48,7 +49,7 @@ func TestLabelCheck(t *testing.T) {
|
|||
curriedLabels: []string{},
|
||||
ok: true,
|
||||
},
|
||||
"cade and method as var labels": {
|
||||
"code and method as var labels": {
|
||||
varLabels: []string{"method", "code"},
|
||||
constLabels: []string{},
|
||||
curriedLabels: []string{},
|
||||
|
@ -60,6 +61,12 @@ func TestLabelCheck(t *testing.T) {
|
|||
curriedLabels: []string{"dings", "bums"},
|
||||
ok: true,
|
||||
},
|
||||
"all labels used with an invalid const label name": {
|
||||
varLabels: []string{"code", "method"},
|
||||
constLabels: []string{"in-valid", "bar"},
|
||||
curriedLabels: []string{"dings", "bums"},
|
||||
ok: false,
|
||||
},
|
||||
"unsupported var label": {
|
||||
varLabels: []string{"foo"},
|
||||
constLabels: []string{},
|
||||
|
@ -96,17 +103,35 @@ func TestLabelCheck(t *testing.T) {
|
|||
curriedLabels: []string{"method"},
|
||||
ok: false,
|
||||
},
|
||||
"invalid name and otherwise empty": {
|
||||
metricName: "in-valid",
|
||||
varLabels: []string{},
|
||||
constLabels: []string{},
|
||||
curriedLabels: []string{},
|
||||
ok: false,
|
||||
},
|
||||
"invalid name with all the otherwise valid labels": {
|
||||
metricName: "in-valid",
|
||||
varLabels: []string{"code", "method"},
|
||||
constLabels: []string{"foo", "bar"},
|
||||
curriedLabels: []string{"dings", "bums"},
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, sc := range scenarios {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
metricName := sc.metricName
|
||||
if metricName == "" {
|
||||
metricName = "c"
|
||||
}
|
||||
constLabels := prometheus.Labels{}
|
||||
for _, l := range sc.constLabels {
|
||||
constLabels[l] = "dummy"
|
||||
}
|
||||
c := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "c",
|
||||
Name: metricName,
|
||||
Help: "c help",
|
||||
ConstLabels: constLabels,
|
||||
},
|
||||
|
@ -114,7 +139,7 @@ func TestLabelCheck(t *testing.T) {
|
|||
)
|
||||
o := prometheus.ObserverVec(prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "c",
|
||||
Name: metricName,
|
||||
Help: "c help",
|
||||
ConstLabels: constLabels,
|
||||
},
|
||||
|
@ -294,8 +319,7 @@ func (t *testFlusher) Flush() { t.flushCalled = true }
|
|||
func TestInterfaceUpgrade(t *testing.T) {
|
||||
w := &testResponseWriter{}
|
||||
d := newDelegator(w, nil)
|
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
//nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users.
|
||||
d.(http.CloseNotifier).CloseNotify()
|
||||
if !w.closeNotifyCalled {
|
||||
t.Error("CloseNotify not called")
|
||||
|
@ -314,8 +338,7 @@ func TestInterfaceUpgrade(t *testing.T) {
|
|||
|
||||
f := &testFlusher{}
|
||||
d = newDelegator(f, nil)
|
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
//nolint:staticcheck // Ignore SA1019. http.CloseNotifier is deprecated but we keep it here to not break existing users.
|
||||
if _, ok := d.(http.CloseNotifier); ok {
|
||||
t.Error("delegator unexpectedly implements http.CloseNotifier")
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ package push
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -56,6 +57,8 @@ const (
|
|||
base64Suffix = "@base64"
|
||||
)
|
||||
|
||||
var errJobEmpty = errors.New("job name is empty")
|
||||
|
||||
// HTTPDoer is an interface for the one method of http.Client that is used by Pusher
|
||||
type HTTPDoer interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
|
@ -80,14 +83,17 @@ type Pusher struct {
|
|||
}
|
||||
|
||||
// New creates a new Pusher to push to the provided URL with the provided job
|
||||
// name. You can use just host:port or ip:port as url, in which case “http://”
|
||||
// is added automatically. Alternatively, include the schema in the
|
||||
// URL. However, do not include the “/metrics/jobs/…” part.
|
||||
// name (which must not be empty). You can use just host:port or ip:port as url,
|
||||
// in which case “http://” is added automatically. Alternatively, include the
|
||||
// schema in the URL. However, do not include the “/metrics/jobs/…” part.
|
||||
func New(url, job string) *Pusher {
|
||||
var (
|
||||
reg = prometheus.NewRegistry()
|
||||
err error
|
||||
)
|
||||
if job == "" {
|
||||
err = errJobEmpty
|
||||
}
|
||||
if !strings.Contains(url, "://") {
|
||||
url = "http://" + url
|
||||
}
|
||||
|
@ -267,7 +273,7 @@ func (p *Pusher) push(method string) error {
|
|||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// Pushgateway 0.10+ responds with StatusOK, earlier versions with StatusAccepted.
|
||||
// Depending on version and configuration of the PGW, StatusOK or StatusAccepted may be returned.
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
|
||||
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, p.fullURL(), body)
|
||||
|
@ -278,9 +284,11 @@ func (p *Pusher) push(method string) error {
|
|||
// fullURL assembles the URL used to push/delete metrics and returns it as a
|
||||
// string. The job name and any grouping label values containing a '/' will
|
||||
// trigger a base64 encoding of the affected component and proper suffixing of
|
||||
// the preceding component. If the component does not contain a '/' but other
|
||||
// special character, the usual url.QueryEscape is used for compatibility with
|
||||
// older versions of the Pushgateway and for better readability.
|
||||
// the preceding component. Similarly, an empty grouping label value will be
|
||||
// encoded as base64 just with a single `=` padding character (to avoid an empty
|
||||
// path component). If the component does not contain a '/' but other special
|
||||
// characters, the usual url.QueryEscape is used for compatibility with older
|
||||
// versions of the Pushgateway and for better readability.
|
||||
func (p *Pusher) fullURL() string {
|
||||
urlComponents := []string{}
|
||||
if encodedJob, base64 := encodeComponent(p.job); base64 {
|
||||
|
@ -299,9 +307,12 @@ func (p *Pusher) fullURL() string {
|
|||
}
|
||||
|
||||
// encodeComponent encodes the provided string with base64.RawURLEncoding in
|
||||
// case it contains '/'. If not, it uses url.QueryEscape instead. It returns
|
||||
// true in the former case.
|
||||
// case it contains '/' and as "=" in case it is empty. If neither is the case,
|
||||
// it uses url.QueryEscape instead. It returns true in the former two cases.
|
||||
func encodeComponent(s string) (string, bool) {
|
||||
if s == "" {
|
||||
return "=", true
|
||||
}
|
||||
if strings.Contains(s, "/") {
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(s)), true
|
||||
}
|
||||
|
|
|
@ -176,6 +176,36 @@ func TestPush(t *testing.T) {
|
|||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// Empty label value triggers special base64 encoding.
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Grouping("empty", "").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != http.MethodPut {
|
||||
t.Errorf("got method %q for Push, want %q", lastMethod, http.MethodPut)
|
||||
}
|
||||
if !bytes.Equal(lastBody, wantBody) {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
if lastPath != "/metrics/job/testjob/empty@base64/=" {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// Empty job name results in error.
|
||||
if err := New(pgwErr.URL, "").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err == nil {
|
||||
t.Error("push with empty job succeded")
|
||||
} else {
|
||||
if got, want := err, errJobEmpty; got != want {
|
||||
t.Errorf("got error %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Push some Collectors with a broken PGW.
|
||||
if err := New(pgwErr.URL, "testjob").
|
||||
Collector(metric1).
|
||||
|
@ -251,5 +281,4 @@ func TestPush(t *testing.T) {
|
|||
if lastPath != "/metrics/job/testjob/a/x/b/y" && lastPath != "/metrics/job/testjob/b/y/a/x" {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"unicode/utf8"
|
||||
|
||||
"github.com/cespare/xxhash/v2"
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
|
@ -214,6 +215,8 @@ func (err AlreadyRegisteredError) Error() string {
|
|||
// by a Gatherer to report multiple errors during MetricFamily gathering.
|
||||
type MultiError []error
|
||||
|
||||
// Error formats the contained errors as a bullet point list, preceded by the
|
||||
// total number of errors. Note that this results in a multi-line string.
|
||||
func (errs MultiError) Error() string {
|
||||
if len(errs) == 0 {
|
||||
return ""
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/beorn7/perks/quantile"
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
@ -109,7 +110,7 @@ type SummaryOpts struct {
|
|||
// better covered by target labels set by the scraping Prometheus
|
||||
// server, or by one specific metric (e.g. a build_info or a
|
||||
// machine_role metric). See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels,-not-static-scraped-labels
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
ConstLabels Labels
|
||||
|
||||
// Objectives defines the quantile rank estimates with their respective
|
||||
|
@ -207,7 +208,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
|||
// Use the lock-free implementation of a Summary without objectives.
|
||||
s := &noObjectivesSummary{
|
||||
desc: desc,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
counts: [2]*summaryCounts{{}, {}},
|
||||
}
|
||||
s.init(s) // Init self-collection.
|
||||
|
@ -220,7 +221,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
|||
objectives: opts.Objectives,
|
||||
sortedObjectives: make([]float64, 0, len(opts.Objectives)),
|
||||
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
|
||||
hotBuf: make([]float64, 0, opts.BufCap),
|
||||
coldBuf: make([]float64, 0, opts.BufCap),
|
||||
|
@ -512,7 +513,7 @@ 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
|
||||
|
@ -534,14 +535,14 @@ 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
|
||||
// values (same order as the variable labels 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
|
||||
|
@ -556,7 +557,7 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
|||
// example.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of VariableLabels in Desc (minus any curried labels).
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
|
@ -565,7 +566,7 @@ func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
|
|||
// with a performance overhead (for creating and processing the Labels map).
|
||||
// See also the GaugeVec example.
|
||||
func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
|
||||
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
|
@ -573,19 +574,19 @@ func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) {
|
|||
}
|
||||
|
||||
// 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
|
||||
// must match those of the variable labels in Desc). If that label map is
|
||||
// accessed for the first time, a new Summary is created. Implications of
|
||||
// creating a Summary without using it and keeping the Summary for later use are
|
||||
// the same as for GetMetricWithLabelValues.
|
||||
//
|
||||
// An error is returned if the number and names of the Labels are inconsistent
|
||||
// with those of the VariableLabels in Desc (minus any curried labels).
|
||||
// with those of the variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) {
|
||||
metric, err := v.metricVec.getMetricWith(labels)
|
||||
metric, err := v.MetricVec.GetMetricWith(labels)
|
||||
if metric != nil {
|
||||
return metric.(Observer), err
|
||||
}
|
||||
|
@ -629,7 +630,7 @@ func (v *SummaryVec) With(labels Labels) Observer {
|
|||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
func (v *SummaryVec) CurryWith(labels Labels) (ObserverVec, error) {
|
||||
vec, err := v.curryWith(labels)
|
||||
vec, err := v.MetricVec.CurryWith(labels)
|
||||
if vec != nil {
|
||||
return &SummaryVec{vec}, err
|
||||
}
|
||||
|
@ -715,7 +716,7 @@ func NewConstSummary(
|
|||
count: count,
|
||||
sum: sum,
|
||||
quantiles: quantiles,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -321,7 +321,7 @@ func TestSummaryVecConcurrency(t *testing.T) {
|
|||
go func(vals []float64) {
|
||||
start.Wait()
|
||||
for i, v := range vals {
|
||||
sum.WithLabelValues(string('A' + picks[i])).Observe(v)
|
||||
sum.WithLabelValues(string('A' + rune(picks[i]))).Observe(v)
|
||||
}
|
||||
end.Done()
|
||||
}(vals)
|
||||
|
@ -334,7 +334,7 @@ func TestSummaryVecConcurrency(t *testing.T) {
|
|||
|
||||
for i := 0; i < vecLength; i++ {
|
||||
m := &dto.Metric{}
|
||||
s := sum.WithLabelValues(string('A' + i))
|
||||
s := sum.WithLabelValues(string('A' + rune(i)))
|
||||
s.(Summary).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)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2020 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 testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/testutil/promlint"
|
||||
)
|
||||
|
||||
// CollectAndLint registers the provided Collector with a newly created pedantic
|
||||
// Registry. It then calls GatherAndLint with that Registry and with the
|
||||
// provided metricNames.
|
||||
func CollectAndLint(c prometheus.Collector, metricNames ...string) ([]promlint.Problem, error) {
|
||||
reg := prometheus.NewPedanticRegistry()
|
||||
if err := reg.Register(c); err != nil {
|
||||
return nil, fmt.Errorf("registering collector failed: %s", err)
|
||||
}
|
||||
return GatherAndLint(reg, metricNames...)
|
||||
}
|
||||
|
||||
// GatherAndLint gathers all metrics from the provided Gatherer and checks them
|
||||
// with the linter in the promlint package. If any metricNames are provided,
|
||||
// only metrics with those names are checked.
|
||||
func GatherAndLint(g prometheus.Gatherer, metricNames ...string) ([]promlint.Problem, error) {
|
||||
got, err := g.Gather()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("gathering metrics failed: %s", err)
|
||||
}
|
||||
if metricNames != nil {
|
||||
got = filterMetrics(got, metricNames)
|
||||
}
|
||||
return promlint.NewWithMetricFamilies(got).Lint()
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2020 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 testutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func TestCollectAndLintGood(t *testing.T) {
|
||||
cnt := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "some_total",
|
||||
Help: "A value that represents a counter.",
|
||||
ConstLabels: prometheus.Labels{
|
||||
"label1": "value1",
|
||||
},
|
||||
},
|
||||
[]string{"foo"},
|
||||
)
|
||||
cnt.WithLabelValues("bar")
|
||||
cnt.WithLabelValues("baz")
|
||||
|
||||
problems, err := CollectAndLint(cnt)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err)
|
||||
}
|
||||
if len(problems) > 0 {
|
||||
t.Error("Unexpected lint problems:", problems)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectAndLintBad(t *testing.T) {
|
||||
cnt := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "someThing_ms",
|
||||
Help: "A value that represents a counter.",
|
||||
ConstLabels: prometheus.Labels{
|
||||
"label1": "value1",
|
||||
},
|
||||
},
|
||||
[]string{"fooBar"},
|
||||
)
|
||||
cnt.WithLabelValues("bar")
|
||||
cnt.WithLabelValues("baz")
|
||||
|
||||
problems, err := CollectAndLint(cnt)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error:", err)
|
||||
}
|
||||
if len(problems) < 5 {
|
||||
// The exact nature of the lint problems found is tested within
|
||||
// the promlint package itself. Here we only want to make sure
|
||||
// that the collector successfully hits the linter and that at
|
||||
// least the five problems that the linter could recognize at
|
||||
// the time of writing this test are flagged.
|
||||
t.Error("Not enough lint problems found.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,386 @@
|
|||
// Copyright 2020 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 promlint provides a linter for Prometheus metrics.
|
||||
package promlint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
// A Linter is a Prometheus metrics linter. It identifies issues with metric
|
||||
// names, types, and metadata, and reports them to the caller.
|
||||
type Linter struct {
|
||||
// The linter will read metrics in the Prometheus text format from r and
|
||||
// then lint it, _and_ it will lint the metrics provided directly as
|
||||
// MetricFamily proto messages in mfs. Note, however, that the current
|
||||
// constructor functions New and NewWithMetricFamilies only ever set one
|
||||
// of them.
|
||||
r io.Reader
|
||||
mfs []*dto.MetricFamily
|
||||
}
|
||||
|
||||
// A Problem is an issue detected by a Linter.
|
||||
type Problem struct {
|
||||
// The name of the metric indicated by this Problem.
|
||||
Metric string
|
||||
|
||||
// A description of the issue for this Problem.
|
||||
Text string
|
||||
}
|
||||
|
||||
// newProblem is helper function to create a Problem.
|
||||
func newProblem(mf *dto.MetricFamily, text string) Problem {
|
||||
return Problem{
|
||||
Metric: mf.GetName(),
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Linter that reads an input stream of Prometheus metrics in
|
||||
// the Prometheus text exposition format.
|
||||
func New(r io.Reader) *Linter {
|
||||
return &Linter{
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
// NewWithMetricFamilies creates a new Linter that reads from a slice of
|
||||
// MetricFamily protobuf messages.
|
||||
func NewWithMetricFamilies(mfs []*dto.MetricFamily) *Linter {
|
||||
return &Linter{
|
||||
mfs: mfs,
|
||||
}
|
||||
}
|
||||
|
||||
// Lint performs a linting pass, returning a slice of Problems indicating any
|
||||
// issues found in the metrics stream. The slice is sorted by metric name
|
||||
// and issue description.
|
||||
func (l *Linter) Lint() ([]Problem, error) {
|
||||
var problems []Problem
|
||||
|
||||
if l.r != nil {
|
||||
d := expfmt.NewDecoder(l.r, expfmt.FmtText)
|
||||
|
||||
mf := &dto.MetricFamily{}
|
||||
for {
|
||||
if err := d.Decode(mf); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
problems = append(problems, lint(mf)...)
|
||||
}
|
||||
}
|
||||
for _, mf := range l.mfs {
|
||||
problems = append(problems, lint(mf)...)
|
||||
}
|
||||
|
||||
// Ensure deterministic output.
|
||||
sort.SliceStable(problems, func(i, j int) bool {
|
||||
if problems[i].Metric == problems[j].Metric {
|
||||
return problems[i].Text < problems[j].Text
|
||||
}
|
||||
return problems[i].Metric < problems[j].Metric
|
||||
})
|
||||
|
||||
return problems, nil
|
||||
}
|
||||
|
||||
// lint is the entry point for linting a single metric.
|
||||
func lint(mf *dto.MetricFamily) []Problem {
|
||||
fns := []func(mf *dto.MetricFamily) []Problem{
|
||||
lintHelp,
|
||||
lintMetricUnits,
|
||||
lintCounter,
|
||||
lintHistogramSummaryReserved,
|
||||
lintMetricTypeInName,
|
||||
lintReservedChars,
|
||||
lintCamelCase,
|
||||
lintUnitAbbreviations,
|
||||
}
|
||||
|
||||
var problems []Problem
|
||||
for _, fn := range fns {
|
||||
problems = append(problems, fn(mf)...)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): lint rules for specific metrics types.
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintHelp detects issues related to the help text for a metric.
|
||||
func lintHelp(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
|
||||
// Expect all metrics to have help text available.
|
||||
if mf.Help == nil {
|
||||
problems = append(problems, newProblem(mf, "no help text"))
|
||||
}
|
||||
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintMetricUnits detects issues with metric unit names.
|
||||
func lintMetricUnits(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
|
||||
unit, base, ok := metricUnits(*mf.Name)
|
||||
if !ok {
|
||||
// No known units detected.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unit is already a base unit.
|
||||
if unit == base {
|
||||
return nil
|
||||
}
|
||||
|
||||
problems = append(problems, newProblem(mf, fmt.Sprintf("use base unit %q instead of %q", base, unit)))
|
||||
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintCounter detects issues specific to counters, as well as patterns that should
|
||||
// only be used with counters.
|
||||
func lintCounter(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
|
||||
isCounter := mf.GetType() == dto.MetricType_COUNTER
|
||||
isUntyped := mf.GetType() == dto.MetricType_UNTYPED
|
||||
hasTotalSuffix := strings.HasSuffix(mf.GetName(), "_total")
|
||||
|
||||
switch {
|
||||
case isCounter && !hasTotalSuffix:
|
||||
problems = append(problems, newProblem(mf, `counter metrics should have "_total" suffix`))
|
||||
case !isUntyped && !isCounter && hasTotalSuffix:
|
||||
problems = append(problems, newProblem(mf, `non-counter metrics should not have "_total" suffix`))
|
||||
}
|
||||
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintHistogramSummaryReserved detects when other types of metrics use names or labels
|
||||
// reserved for use by histograms and/or summaries.
|
||||
func lintHistogramSummaryReserved(mf *dto.MetricFamily) []Problem {
|
||||
// These rules do not apply to untyped metrics.
|
||||
t := mf.GetType()
|
||||
if t == dto.MetricType_UNTYPED {
|
||||
return nil
|
||||
}
|
||||
|
||||
var problems []Problem
|
||||
|
||||
isHistogram := t == dto.MetricType_HISTOGRAM
|
||||
isSummary := t == dto.MetricType_SUMMARY
|
||||
|
||||
n := mf.GetName()
|
||||
|
||||
if !isHistogram && strings.HasSuffix(n, "_bucket") {
|
||||
problems = append(problems, newProblem(mf, `non-histogram metrics should not have "_bucket" suffix`))
|
||||
}
|
||||
if !isHistogram && !isSummary && strings.HasSuffix(n, "_count") {
|
||||
problems = append(problems, newProblem(mf, `non-histogram and non-summary metrics should not have "_count" suffix`))
|
||||
}
|
||||
if !isHistogram && !isSummary && strings.HasSuffix(n, "_sum") {
|
||||
problems = append(problems, newProblem(mf, `non-histogram and non-summary metrics should not have "_sum" suffix`))
|
||||
}
|
||||
|
||||
for _, m := range mf.GetMetric() {
|
||||
for _, l := range m.GetLabel() {
|
||||
ln := l.GetName()
|
||||
|
||||
if !isHistogram && ln == "le" {
|
||||
problems = append(problems, newProblem(mf, `non-histogram metrics should not have "le" label`))
|
||||
}
|
||||
if !isSummary && ln == "quantile" {
|
||||
problems = append(problems, newProblem(mf, `non-summary metrics should not have "quantile" label`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintMetricTypeInName detects when metric types are included in the metric name.
|
||||
func lintMetricTypeInName(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
n := strings.ToLower(mf.GetName())
|
||||
|
||||
for i, t := range dto.MetricType_name {
|
||||
if i == int32(dto.MetricType_UNTYPED) {
|
||||
continue
|
||||
}
|
||||
|
||||
typename := strings.ToLower(t)
|
||||
if strings.Contains(n, "_"+typename+"_") || strings.HasSuffix(n, "_"+typename) {
|
||||
problems = append(problems, newProblem(mf, fmt.Sprintf(`metric name should not include type '%s'`, typename)))
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintReservedChars detects colons in metric names.
|
||||
func lintReservedChars(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
if strings.Contains(mf.GetName(), ":") {
|
||||
problems = append(problems, newProblem(mf, "metric names should not contain ':'"))
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
var camelCase = regexp.MustCompile(`[a-z][A-Z]`)
|
||||
|
||||
// lintCamelCase detects metric names and label names written in camelCase.
|
||||
func lintCamelCase(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
if camelCase.FindString(mf.GetName()) != "" {
|
||||
problems = append(problems, newProblem(mf, "metric names should be written in 'snake_case' not 'camelCase'"))
|
||||
}
|
||||
|
||||
for _, m := range mf.GetMetric() {
|
||||
for _, l := range m.GetLabel() {
|
||||
if camelCase.FindString(l.GetName()) != "" {
|
||||
problems = append(problems, newProblem(mf, "label names should be written in 'snake_case' not 'camelCase'"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
// lintUnitAbbreviations detects abbreviated units in the metric name.
|
||||
func lintUnitAbbreviations(mf *dto.MetricFamily) []Problem {
|
||||
var problems []Problem
|
||||
n := strings.ToLower(mf.GetName())
|
||||
for _, s := range unitAbbreviations {
|
||||
if strings.Contains(n, "_"+s+"_") || strings.HasSuffix(n, "_"+s) {
|
||||
problems = append(problems, newProblem(mf, "metric names should not contain abbreviated units"))
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
// metricUnits attempts to detect known unit types used as part of a metric name,
|
||||
// e.g. "foo_bytes_total" or "bar_baz_milligrams".
|
||||
func metricUnits(m string) (unit string, base string, ok bool) {
|
||||
ss := strings.Split(m, "_")
|
||||
|
||||
for unit, base := range units {
|
||||
// Also check for "no prefix".
|
||||
for _, p := range append(unitPrefixes, "") {
|
||||
for _, s := range ss {
|
||||
// Attempt to explicitly match a known unit with a known prefix,
|
||||
// as some words may look like "units" when matching suffix.
|
||||
//
|
||||
// As an example, "thermometers" should not match "meters", but
|
||||
// "kilometers" should.
|
||||
if s == p+unit {
|
||||
return p + unit, base, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
// Units and their possible prefixes recognized by this library. More can be
|
||||
// added over time as needed.
|
||||
var (
|
||||
// map a unit to the appropriate base unit.
|
||||
units = map[string]string{
|
||||
// Base units.
|
||||
"amperes": "amperes",
|
||||
"bytes": "bytes",
|
||||
"celsius": "celsius", // Also allow Celsius because it is common in typical Prometheus use cases.
|
||||
"grams": "grams",
|
||||
"joules": "joules",
|
||||
"kelvin": "kelvin", // SI base unit, used in special cases (e.g. color temperature, scientific measurements).
|
||||
"meters": "meters", // Both American and international spelling permitted.
|
||||
"metres": "metres",
|
||||
"seconds": "seconds",
|
||||
"volts": "volts",
|
||||
|
||||
// Non base units.
|
||||
// Time.
|
||||
"minutes": "seconds",
|
||||
"hours": "seconds",
|
||||
"days": "seconds",
|
||||
"weeks": "seconds",
|
||||
// Temperature.
|
||||
"kelvins": "kelvin",
|
||||
"fahrenheit": "celsius",
|
||||
"rankine": "celsius",
|
||||
// Length.
|
||||
"inches": "meters",
|
||||
"yards": "meters",
|
||||
"miles": "meters",
|
||||
// Bytes.
|
||||
"bits": "bytes",
|
||||
// Energy.
|
||||
"calories": "joules",
|
||||
// Mass.
|
||||
"pounds": "grams",
|
||||
"ounces": "grams",
|
||||
}
|
||||
|
||||
unitPrefixes = []string{
|
||||
"pico",
|
||||
"nano",
|
||||
"micro",
|
||||
"milli",
|
||||
"centi",
|
||||
"deci",
|
||||
"deca",
|
||||
"hecto",
|
||||
"kilo",
|
||||
"kibi",
|
||||
"mega",
|
||||
"mibi",
|
||||
"giga",
|
||||
"gibi",
|
||||
"tera",
|
||||
"tebi",
|
||||
"peta",
|
||||
"pebi",
|
||||
}
|
||||
|
||||
// Common abbreviations that we'd like to discourage.
|
||||
unitAbbreviations = []string{
|
||||
"s",
|
||||
"ms",
|
||||
"us",
|
||||
"ns",
|
||||
"sec",
|
||||
"b",
|
||||
"kb",
|
||||
"mb",
|
||||
"gb",
|
||||
"tb",
|
||||
"pb",
|
||||
"m",
|
||||
"h",
|
||||
"d",
|
||||
}
|
||||
)
|
|
@ -0,0 +1,784 @@
|
|||
// Copyright 2020 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 promlint_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/testutil/promlint"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
in string
|
||||
problems []promlint.Problem
|
||||
}
|
||||
|
||||
func TestLintNoHelpText(t *testing.T) {
|
||||
const msg = "no help text"
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
name: "no help",
|
||||
in: `
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines 24
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "go_goroutines",
|
||||
Text: msg,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "empty help",
|
||||
in: `
|
||||
# HELP go_goroutines
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines 24
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "go_goroutines",
|
||||
Text: msg,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "no help and empty help",
|
||||
in: `
|
||||
# HELP go_goroutines
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines 24
|
||||
# TYPE go_threads gauge
|
||||
go_threads 10
|
||||
`,
|
||||
problems: []promlint.Problem{
|
||||
{
|
||||
Metric: "go_goroutines",
|
||||
Text: msg,
|
||||
},
|
||||
{
|
||||
Metric: "go_threads",
|
||||
Text: msg,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OK",
|
||||
in: `
|
||||
# HELP go_goroutines Number of goroutines that currently exist.
|
||||
# TYPE go_goroutines gauge
|
||||
go_goroutines 24
|
||||
`,
|
||||
},
|
||||
}
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func TestLintMetricUnits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
problems []promlint.Problem
|
||||
}{
|
||||
// good cases.
|
||||
{
|
||||
name: "amperes",
|
||||
in: `
|
||||
# HELP x_amperes Test metric.
|
||||
# TYPE x_amperes untyped
|
||||
x_amperes 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "bytes",
|
||||
in: `
|
||||
# HELP x_bytes Test metric.
|
||||
# TYPE x_bytes untyped
|
||||
x_bytes 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "grams",
|
||||
in: `
|
||||
# HELP x_grams Test metric.
|
||||
# TYPE x_grams untyped
|
||||
x_grams 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "celsius",
|
||||
in: `
|
||||
# HELP x_celsius Test metric.
|
||||
# TYPE x_celsius untyped
|
||||
x_celsius 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "meters",
|
||||
in: `
|
||||
# HELP x_meters Test metric.
|
||||
# TYPE x_meters untyped
|
||||
x_meters 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "metres",
|
||||
in: `
|
||||
# HELP x_metres Test metric.
|
||||
# TYPE x_metres untyped
|
||||
x_metres 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "moles",
|
||||
in: `
|
||||
# HELP x_moles Test metric.
|
||||
# TYPE x_moles untyped
|
||||
x_moles 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "seconds",
|
||||
in: `
|
||||
# HELP x_seconds Test metric.
|
||||
# TYPE x_seconds untyped
|
||||
x_seconds 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "joules",
|
||||
in: `
|
||||
# HELP x_joules Test metric.
|
||||
# TYPE x_joules untyped
|
||||
x_joules 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "kelvin",
|
||||
in: `
|
||||
# HELP x_kelvin Test metric.
|
||||
# TYPE x_kelvin untyped
|
||||
x_kelvin 10
|
||||
`,
|
||||
},
|
||||
// bad cases.
|
||||
{
|
||||
name: "milliamperes",
|
||||
in: `
|
||||
# HELP x_milliamperes Test metric.
|
||||
# TYPE x_milliamperes untyped
|
||||
x_milliamperes 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_milliamperes",
|
||||
Text: `use base unit "amperes" instead of "milliamperes"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "gigabytes",
|
||||
in: `
|
||||
# HELP x_gigabytes Test metric.
|
||||
# TYPE x_gigabytes untyped
|
||||
x_gigabytes 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_gigabytes",
|
||||
Text: `use base unit "bytes" instead of "gigabytes"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "kilograms",
|
||||
in: `
|
||||
# HELP x_kilograms Test metric.
|
||||
# TYPE x_kilograms untyped
|
||||
x_kilograms 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_kilograms",
|
||||
Text: `use base unit "grams" instead of "kilograms"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "nanocelsius",
|
||||
in: `
|
||||
# HELP x_nanocelsius Test metric.
|
||||
# TYPE x_nanocelsius untyped
|
||||
x_nanocelsius 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_nanocelsius",
|
||||
Text: `use base unit "celsius" instead of "nanocelsius"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "kilometers",
|
||||
in: `
|
||||
# HELP x_kilometers Test metric.
|
||||
# TYPE x_kilometers untyped
|
||||
x_kilometers 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_kilometers",
|
||||
Text: `use base unit "meters" instead of "kilometers"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "picometers",
|
||||
in: `
|
||||
# HELP x_picometers Test metric.
|
||||
# TYPE x_picometers untyped
|
||||
x_picometers 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_picometers",
|
||||
Text: `use base unit "meters" instead of "picometers"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "microseconds",
|
||||
in: `
|
||||
# HELP x_microseconds Test metric.
|
||||
# TYPE x_microseconds untyped
|
||||
x_microseconds 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_microseconds",
|
||||
Text: `use base unit "seconds" instead of "microseconds"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "minutes",
|
||||
in: `
|
||||
# HELP x_minutes Test metric.
|
||||
# TYPE x_minutes untyped
|
||||
x_minutes 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_minutes",
|
||||
Text: `use base unit "seconds" instead of "minutes"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "hours",
|
||||
in: `
|
||||
# HELP x_hours Test metric.
|
||||
# TYPE x_hours untyped
|
||||
x_hours 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_hours",
|
||||
Text: `use base unit "seconds" instead of "hours"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "days",
|
||||
in: `
|
||||
# HELP x_days Test metric.
|
||||
# TYPE x_days untyped
|
||||
x_days 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_days",
|
||||
Text: `use base unit "seconds" instead of "days"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "kelvins",
|
||||
in: `
|
||||
# HELP x_kelvins Test metric.
|
||||
# TYPE x_kelvins untyped
|
||||
x_kelvins 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_kelvins",
|
||||
Text: `use base unit "kelvin" instead of "kelvins"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "fahrenheit",
|
||||
in: `
|
||||
# HELP thermometers_fahrenheit Test metric.
|
||||
# TYPE thermometers_fahrenheit untyped
|
||||
thermometers_fahrenheit 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "thermometers_fahrenheit",
|
||||
Text: `use base unit "celsius" instead of "fahrenheit"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "rankine",
|
||||
in: `
|
||||
# HELP thermometers_rankine Test metric.
|
||||
# TYPE thermometers_rankine untyped
|
||||
thermometers_rankine 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "thermometers_rankine",
|
||||
Text: `use base unit "celsius" instead of "rankine"`,
|
||||
}},
|
||||
}, {
|
||||
name: "inches",
|
||||
in: `
|
||||
# HELP x_inches Test metric.
|
||||
# TYPE x_inches untyped
|
||||
x_inches 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_inches",
|
||||
Text: `use base unit "meters" instead of "inches"`,
|
||||
}},
|
||||
}, {
|
||||
name: "yards",
|
||||
in: `
|
||||
# HELP x_yards Test metric.
|
||||
# TYPE x_yards untyped
|
||||
x_yards 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_yards",
|
||||
Text: `use base unit "meters" instead of "yards"`,
|
||||
}},
|
||||
}, {
|
||||
name: "miles",
|
||||
in: `
|
||||
# HELP x_miles Test metric.
|
||||
# TYPE x_miles untyped
|
||||
x_miles 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_miles",
|
||||
Text: `use base unit "meters" instead of "miles"`,
|
||||
}},
|
||||
}, {
|
||||
name: "bits",
|
||||
in: `
|
||||
# HELP x_bits Test metric.
|
||||
# TYPE x_bits untyped
|
||||
x_bits 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bits",
|
||||
Text: `use base unit "bytes" instead of "bits"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "calories",
|
||||
in: `
|
||||
# HELP x_calories Test metric.
|
||||
# TYPE x_calories untyped
|
||||
x_calories 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_calories",
|
||||
Text: `use base unit "joules" instead of "calories"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "pounds",
|
||||
in: `
|
||||
# HELP x_pounds Test metric.
|
||||
# TYPE x_pounds untyped
|
||||
x_pounds 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_pounds",
|
||||
Text: `use base unit "grams" instead of "pounds"`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "ounces",
|
||||
in: `
|
||||
# HELP x_ounces Test metric.
|
||||
# TYPE x_ounces untyped
|
||||
x_ounces 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_ounces",
|
||||
Text: `use base unit "grams" instead of "ounces"`,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
l := promlint.New(strings.NewReader(tt.in))
|
||||
|
||||
problems, err := l.Lint()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if want, got := tt.problems, problems; !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("unexpected problems:\n- want: %v\n- got: %v",
|
||||
want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLintCounter(t *testing.T) {
|
||||
tests := []test{
|
||||
{
|
||||
name: "counter without _total suffix",
|
||||
in: `
|
||||
# HELP x_bytes Test metric.
|
||||
# TYPE x_bytes counter
|
||||
x_bytes 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes",
|
||||
Text: `counter metrics should have "_total" suffix`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "gauge with _total suffix",
|
||||
in: `
|
||||
# HELP x_bytes_total Test metric.
|
||||
# TYPE x_bytes_total gauge
|
||||
x_bytes_total 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes_total",
|
||||
Text: `non-counter metrics should not have "_total" suffix`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "counter with _total suffix",
|
||||
in: `
|
||||
# HELP x_bytes_total Test metric.
|
||||
# TYPE x_bytes_total counter
|
||||
x_bytes_total 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "gauge without _total suffix",
|
||||
in: `
|
||||
# HELP x_bytes Test metric.
|
||||
# TYPE x_bytes gauge
|
||||
x_bytes 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "untyped with _total suffix",
|
||||
in: `
|
||||
# HELP x_bytes_total Test metric.
|
||||
# TYPE x_bytes_total untyped
|
||||
x_bytes_total 10
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "untyped without _total suffix",
|
||||
in: `
|
||||
# HELP x_bytes Test metric.
|
||||
# TYPE x_bytes untyped
|
||||
x_bytes 10
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func TestLintHistogramSummaryReserved(t *testing.T) {
|
||||
tests := []test{
|
||||
{
|
||||
name: "gauge with _bucket suffix",
|
||||
in: `
|
||||
# HELP x_bytes_bucket Test metric.
|
||||
# TYPE x_bytes_bucket gauge
|
||||
x_bytes_bucket 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes_bucket",
|
||||
Text: `non-histogram metrics should not have "_bucket" suffix`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "gauge with _count suffix",
|
||||
in: `
|
||||
# HELP x_bytes_count Test metric.
|
||||
# TYPE x_bytes_count gauge
|
||||
x_bytes_count 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes_count",
|
||||
Text: `non-histogram and non-summary metrics should not have "_count" suffix`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "gauge with _sum suffix",
|
||||
in: `
|
||||
# HELP x_bytes_sum Test metric.
|
||||
# TYPE x_bytes_sum gauge
|
||||
x_bytes_sum 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes_sum",
|
||||
Text: `non-histogram and non-summary metrics should not have "_sum" suffix`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "gauge with le label",
|
||||
in: `
|
||||
# HELP x_bytes Test metric.
|
||||
# TYPE x_bytes gauge
|
||||
x_bytes{le="1"} 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes",
|
||||
Text: `non-histogram metrics should not have "le" label`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "gauge with quantile label",
|
||||
in: `
|
||||
# HELP x_bytes Test metric.
|
||||
# TYPE x_bytes gauge
|
||||
x_bytes{quantile="1"} 10
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "x_bytes",
|
||||
Text: `non-summary metrics should not have "quantile" label`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "histogram with quantile label",
|
||||
in: `
|
||||
# HELP tsdb_compaction_duration Duration of compaction runs.
|
||||
# TYPE tsdb_compaction_duration histogram
|
||||
tsdb_compaction_duration_bucket{le="0.005",quantile="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.01",quantile="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.025",quantile="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.05",quantile="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.1",quantile="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.25",quantile="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.5",quantile="0.01"} 57
|
||||
tsdb_compaction_duration_bucket{le="1",quantile="0.01"} 68
|
||||
tsdb_compaction_duration_bucket{le="2.5",quantile="0.01"} 69
|
||||
tsdb_compaction_duration_bucket{le="5",quantile="0.01"} 69
|
||||
tsdb_compaction_duration_bucket{le="10",quantile="0.01"} 69
|
||||
tsdb_compaction_duration_bucket{le="+Inf",quantile="0.01"} 69
|
||||
tsdb_compaction_duration_sum 28.740810936000006
|
||||
tsdb_compaction_duration_count 69
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "tsdb_compaction_duration",
|
||||
Text: `non-summary metrics should not have "quantile" label`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "summary with le label",
|
||||
in: `
|
||||
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
|
||||
# TYPE go_gc_duration_seconds summary
|
||||
go_gc_duration_seconds{quantile="0",le="0.01"} 4.2365e-05
|
||||
go_gc_duration_seconds{quantile="0.25",le="0.01"} 8.1492e-05
|
||||
go_gc_duration_seconds{quantile="0.5",le="0.01"} 0.000100656
|
||||
go_gc_duration_seconds{quantile="0.75",le="0.01"} 0.000113913
|
||||
go_gc_duration_seconds{quantile="1",le="0.01"} 0.021754305
|
||||
go_gc_duration_seconds_sum 1.769429004
|
||||
go_gc_duration_seconds_count 5962
|
||||
`,
|
||||
problems: []promlint.Problem{{
|
||||
Metric: "go_gc_duration_seconds",
|
||||
Text: `non-histogram metrics should not have "le" label`,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "histogram OK",
|
||||
in: `
|
||||
# HELP tsdb_compaction_duration Duration of compaction runs.
|
||||
# TYPE tsdb_compaction_duration histogram
|
||||
tsdb_compaction_duration_bucket{le="0.005"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.01"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.025"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.05"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.1"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.25"} 0
|
||||
tsdb_compaction_duration_bucket{le="0.5"} 57
|
||||
tsdb_compaction_duration_bucket{le="1"} 68
|
||||
tsdb_compaction_duration_bucket{le="2.5"} 69
|
||||
tsdb_compaction_duration_bucket{le="5"} 69
|
||||
tsdb_compaction_duration_bucket{le="10"} 69
|
||||
tsdb_compaction_duration_bucket{le="+Inf"} 69
|
||||
tsdb_compaction_duration_sum 28.740810936000006
|
||||
tsdb_compaction_duration_count 69
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "summary OK",
|
||||
in: `
|
||||
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
|
||||
# TYPE go_gc_duration_seconds summary
|
||||
go_gc_duration_seconds{quantile="0"} 4.2365e-05
|
||||
go_gc_duration_seconds{quantile="0.25"} 8.1492e-05
|
||||
go_gc_duration_seconds{quantile="0.5"} 0.000100656
|
||||
go_gc_duration_seconds{quantile="0.75"} 0.000113913
|
||||
go_gc_duration_seconds{quantile="1"} 0.021754305
|
||||
go_gc_duration_seconds_sum 1.769429004
|
||||
go_gc_duration_seconds_count 5962
|
||||
`,
|
||||
},
|
||||
}
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func TestLintMetricTypeInName(t *testing.T) {
|
||||
genTest := func(n, t, err string, problems ...promlint.Problem) test {
|
||||
return test{
|
||||
name: fmt.Sprintf("%s with _%s suffix", t, t),
|
||||
in: fmt.Sprintf(`
|
||||
# HELP %s Test metric.
|
||||
# TYPE %s %s
|
||||
%s 10
|
||||
`, n, n, t, n),
|
||||
problems: append(problems, promlint.Problem{
|
||||
Metric: n,
|
||||
Text: fmt.Sprintf(`metric name should not include type '%s'`, err),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
twoProbTest := genTest("http_requests_counter", "counter", "counter", promlint.Problem{
|
||||
Metric: "http_requests_counter",
|
||||
Text: `counter metrics should have "_total" suffix`,
|
||||
})
|
||||
|
||||
tests := []test{
|
||||
twoProbTest,
|
||||
genTest("instance_memory_limit_bytes_gauge", "gauge", "gauge"),
|
||||
genTest("request_duration_seconds_summary", "summary", "summary"),
|
||||
genTest("request_duration_seconds_summary", "histogram", "summary"),
|
||||
genTest("request_duration_seconds_histogram", "histogram", "histogram"),
|
||||
genTest("request_duration_seconds_HISTOGRAM", "histogram", "histogram"),
|
||||
|
||||
genTest("instance_memory_limit_gauge_bytes", "gauge", "gauge"),
|
||||
}
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func TestLintReservedChars(t *testing.T) {
|
||||
tests := []test{
|
||||
{
|
||||
name: "request_duration::_seconds",
|
||||
in: `
|
||||
# HELP request_duration::_seconds Test metric.
|
||||
# TYPE request_duration::_seconds histogram
|
||||
request_duration::_seconds 10
|
||||
`,
|
||||
problems: []promlint.Problem{
|
||||
{
|
||||
Metric: "request_duration::_seconds",
|
||||
Text: "metric names should not contain ':'",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func TestLintCamelCase(t *testing.T) {
|
||||
tests := []test{
|
||||
{
|
||||
name: "requestDuration_seconds",
|
||||
in: `
|
||||
# HELP requestDuration_seconds Test metric.
|
||||
# TYPE requestDuration_seconds histogram
|
||||
requestDuration_seconds 10
|
||||
`,
|
||||
problems: []promlint.Problem{
|
||||
{
|
||||
Metric: "requestDuration_seconds",
|
||||
Text: "metric names should be written in 'snake_case' not 'camelCase'",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "request_duration_seconds",
|
||||
in: `
|
||||
# HELP request_duration_seconds Test metric.
|
||||
# TYPE request_duration_seconds histogram
|
||||
request_duration_seconds{httpService="foo"} 10
|
||||
`,
|
||||
problems: []promlint.Problem{
|
||||
{
|
||||
Metric: "request_duration_seconds",
|
||||
Text: "label names should be written in 'snake_case' not 'camelCase'",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func TestLintUnitAbbreviations(t *testing.T) {
|
||||
genTest := func(n string) test {
|
||||
return test{
|
||||
name: fmt.Sprintf("%s with abbreviated unit", n),
|
||||
in: fmt.Sprintf(`
|
||||
# HELP %s Test metric.
|
||||
# TYPE %s gauge
|
||||
%s 10
|
||||
`, n, n, n),
|
||||
problems: []promlint.Problem{
|
||||
{
|
||||
Metric: n,
|
||||
Text: "metric names should not contain abbreviated units",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
tests := []test{
|
||||
genTest("instance_memory_limit_b"),
|
||||
genTest("instance_memory_limit_kb"),
|
||||
genTest("instance_memory_limit_mb"),
|
||||
genTest("instance_memory_limit_MB"),
|
||||
genTest("instance_memory_limit_gb"),
|
||||
genTest("instance_memory_limit_tb"),
|
||||
genTest("instance_memory_limit_pb"),
|
||||
|
||||
genTest("request_duration_s"),
|
||||
genTest("request_duration_ms"),
|
||||
genTest("request_duration_us"),
|
||||
genTest("request_duration_ns"),
|
||||
genTest("request_duration_sec"),
|
||||
genTest("request_sec_duration"),
|
||||
genTest("request_duration_m"),
|
||||
genTest("request_duration_h"),
|
||||
genTest("request_duration_d"),
|
||||
}
|
||||
runTests(t, tests)
|
||||
}
|
||||
|
||||
func runTests(t *testing.T, tests []test) {
|
||||
t.Helper()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
l := promlint.New(strings.NewReader(tt.in))
|
||||
|
||||
problems, err := l.Lint()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if want, got := tt.problems, problems; !reflect.DeepEqual(want, got) {
|
||||
t.Fatalf("unexpected problems:\n- want: %v\n- got: %v",
|
||||
want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -31,6 +31,10 @@
|
|||
// testing custom prometheus.Collector implementations and in particular whole
|
||||
// exporters, i.e. programs that retrieve telemetry data from a 3rd party source
|
||||
// and convert it into Prometheus metrics.
|
||||
//
|
||||
// In a similar pattern, CollectAndLint and GatherAndLint can be used to detect
|
||||
// metrics that have issues with their name, type, or metadata without being
|
||||
// necessarily invalid, e.g. a counter with a name missing the “_total” suffix.
|
||||
package testutil
|
||||
|
||||
import (
|
||||
|
@ -108,36 +112,48 @@ func ToFloat64(c prometheus.Collector) float64 {
|
|||
panic(fmt.Errorf("collected a non-gauge/counter/untyped metric: %s", pb))
|
||||
}
|
||||
|
||||
// CollectAndCount collects all Metrics from the provided Collector and returns their number.
|
||||
//
|
||||
// This can be used to assert the number of metrics collected by a given collector after certain operations.
|
||||
//
|
||||
// This function is only for testing purposes, and even for testing, other approaches
|
||||
// are often more appropriate (see this package's documentation).
|
||||
func CollectAndCount(c prometheus.Collector) int {
|
||||
var (
|
||||
mCount int
|
||||
mChan = make(chan prometheus.Metric)
|
||||
done = make(chan struct{})
|
||||
)
|
||||
// CollectAndCount registers the provided Collector with a newly created
|
||||
// pedantic Registry. It then calls GatherAndCount with that Registry and with
|
||||
// the provided metricNames. In the unlikely case that the registration or the
|
||||
// gathering fails, this function panics. (This is inconsistent with the other
|
||||
// CollectAnd… functions in this package and has historical reasons. Changing
|
||||
// the function signature would be a breaking change and will therefore only
|
||||
// happen with the next major version bump.)
|
||||
func CollectAndCount(c prometheus.Collector, metricNames ...string) int {
|
||||
reg := prometheus.NewPedanticRegistry()
|
||||
if err := reg.Register(c); err != nil {
|
||||
panic(fmt.Errorf("registering collector failed: %s", err))
|
||||
}
|
||||
result, err := GatherAndCount(reg, metricNames...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
go func() {
|
||||
for range mChan {
|
||||
mCount++
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
// GatherAndCount gathers all metrics from the provided Gatherer and counts
|
||||
// them. It returns the number of metric children in all gathered metric
|
||||
// families together. If any metricNames are provided, only metrics with those
|
||||
// names are counted.
|
||||
func GatherAndCount(g prometheus.Gatherer, metricNames ...string) (int, error) {
|
||||
got, err := g.Gather()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("gathering metrics failed: %s", err)
|
||||
}
|
||||
if metricNames != nil {
|
||||
got = filterMetrics(got, metricNames)
|
||||
}
|
||||
|
||||
c.Collect(mChan)
|
||||
close(mChan)
|
||||
<-done
|
||||
|
||||
return mCount
|
||||
result := 0
|
||||
for _, mf := range got {
|
||||
result += len(mf.GetMetric())
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CollectAndCompare registers the provided Collector with a newly created
|
||||
// pedantic Registry. It then does the same as GatherAndCompare, gathering the
|
||||
// metrics from the pedantic Registry.
|
||||
// pedantic Registry. It then calls GatherAndCompare with that Registry and with
|
||||
// the provided metricNames.
|
||||
func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames ...string) error {
|
||||
reg := prometheus.NewPedanticRegistry()
|
||||
if err := reg.Register(c); err != nil {
|
||||
|
|
|
@ -306,3 +306,30 @@ some_total{label1="value1"} 1
|
|||
t.Errorf("Expected\n%#+v\nGot:\n%#+v", expectedError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectAndCount(t *testing.T) {
|
||||
c := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "some_total",
|
||||
Help: "A value that represents a counter.",
|
||||
},
|
||||
[]string{"foo"},
|
||||
)
|
||||
if got, want := CollectAndCount(c), 0; got != want {
|
||||
t.Errorf("unexpected metric count, got %d, want %d", got, want)
|
||||
}
|
||||
c.WithLabelValues("bar")
|
||||
if got, want := CollectAndCount(c), 1; got != want {
|
||||
t.Errorf("unexpected metric count, got %d, want %d", got, want)
|
||||
}
|
||||
c.WithLabelValues("baz")
|
||||
if got, want := CollectAndCount(c), 2; got != want {
|
||||
t.Errorf("unexpected metric count, got %d, want %d", got, want)
|
||||
}
|
||||
if got, want := CollectAndCount(c, "some_total"), 2; got != want {
|
||||
t.Errorf("unexpected metric count, got %d, want %d", got, want)
|
||||
}
|
||||
if got, want := CollectAndCount(c, "some_other_total"), 0; got != want {
|
||||
t.Errorf("unexpected metric count, got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
|
@ -62,7 +63,7 @@ func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *val
|
|||
desc: desc,
|
||||
valType: valueType,
|
||||
function: function,
|
||||
labelPairs: makeLabelPairs(desc, nil),
|
||||
labelPairs: MakeLabelPairs(desc, nil),
|
||||
}
|
||||
result.init(result)
|
||||
return result
|
||||
|
@ -94,7 +95,7 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues
|
|||
desc: desc,
|
||||
valType: valueType,
|
||||
val: value,
|
||||
labelPairs: makeLabelPairs(desc, labelValues),
|
||||
labelPairs: MakeLabelPairs(desc, labelValues),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -144,7 +145,14 @@ func populateMetric(
|
|||
return nil
|
||||
}
|
||||
|
||||
func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||
// MakeLabelPairs is a helper function to create protobuf LabelPairs from the
|
||||
// variable and constant labels in the provided Desc. The values for the
|
||||
// variable labels are defined by the labelValues slice, which must be in the
|
||||
// same order as the corresponding variable labels in the Desc.
|
||||
//
|
||||
// This function is only needed for custom Metric implementations. See MetricVec
|
||||
// example.
|
||||
func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
|
||||
if totalLen == 0 {
|
||||
// Super fast path.
|
||||
|
|
|
@ -20,12 +20,20 @@ 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, and HistogramVec.
|
||||
// It also handles label currying.
|
||||
type metricVec struct {
|
||||
// MetricVec is a Collector to bundle metrics of the same name that differ in
|
||||
// their label values. MetricVec is not used directly but as a building block
|
||||
// for implementations of vectors of a given metric type, like GaugeVec,
|
||||
// CounterVec, SummaryVec, and HistogramVec. It is exported so that it can be
|
||||
// used for custom Metric implementations.
|
||||
//
|
||||
// To create a FooVec for custom Metric Foo, embed a pointer to MetricVec in
|
||||
// FooVec and initialize it with NewMetricVec. Implement wrappers for
|
||||
// GetMetricWithLabelValues and GetMetricWith that return (Foo, error) rather
|
||||
// than (Metric, error). Similarly, create a wrapper for CurryWith that returns
|
||||
// (*FooVec, error) rather than (*MetricVec, error). It is recommended to also
|
||||
// add the convenience methods WithLabelValues, With, and MustCurryWith, which
|
||||
// panic instead of returning errors. See also the MetricVec example.
|
||||
type MetricVec struct {
|
||||
*metricMap
|
||||
|
||||
curry []curriedLabelValue
|
||||
|
@ -35,9 +43,9 @@ type metricVec struct {
|
|||
hashAddByte func(h uint64, b byte) uint64
|
||||
}
|
||||
|
||||
// newMetricVec returns an initialized metricVec.
|
||||
func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
||||
return &metricVec{
|
||||
// NewMetricVec returns an initialized metricVec.
|
||||
func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
||||
return &MetricVec{
|
||||
metricMap: &metricMap{
|
||||
metrics: map[uint64][]metricWithLabelValues{},
|
||||
desc: desc,
|
||||
|
@ -63,7 +71,7 @@ func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec {
|
|||
// 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 {
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return false
|
||||
|
@ -82,7 +90,7 @@ func (m *metricVec) DeleteLabelValues(lvs ...string) bool {
|
|||
//
|
||||
// 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 {
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return false
|
||||
|
@ -95,15 +103,32 @@ func (m *metricVec) Delete(labels Labels) bool {
|
|||
// show up in GoDoc.
|
||||
|
||||
// Describe implements Collector.
|
||||
func (m *metricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
|
||||
func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) }
|
||||
|
||||
// Collect implements Collector.
|
||||
func (m *metricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
|
||||
func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) }
|
||||
|
||||
// Reset deletes all metrics in this vector.
|
||||
func (m *metricVec) Reset() { m.metricMap.Reset() }
|
||||
func (m *MetricVec) Reset() { m.metricMap.Reset() }
|
||||
|
||||
func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
||||
// CurryWith returns a vector curried with the provided labels, i.e. the
|
||||
// returned vector has those labels pre-set for all labeled operations performed
|
||||
// on it. The cardinality of the curried vector is reduced accordingly. The
|
||||
// order of the remaining labels stays the same (just with the curried labels
|
||||
// taken out of the sequence – which is relevant for the
|
||||
// (GetMetric)WithLabelValues methods). It is possible to curry a curried
|
||||
// vector, but only with labels not yet used for currying before.
|
||||
//
|
||||
// The metrics contained in the MetricVec are shared between the curried and
|
||||
// uncurried vectors. They are just accessed differently. Curried and uncurried
|
||||
// vectors behave identically in terms of collection. Only one must be
|
||||
// registered with a given registry (usually the uncurried version). The Reset
|
||||
// method deletes all metrics, even if called on a curried vector.
|
||||
//
|
||||
// Note that CurryWith is usually not called directly but through a wrapper
|
||||
// around MetricVec, implementing a vector for a specific Metric
|
||||
// implementation, for example GaugeVec.
|
||||
func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||
var (
|
||||
newCurry []curriedLabelValue
|
||||
oldCurry = m.curry
|
||||
|
@ -128,7 +153,7 @@ func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
|||
return nil, fmt.Errorf("%d unknown label(s) found during currying", l)
|
||||
}
|
||||
|
||||
return &metricVec{
|
||||
return &MetricVec{
|
||||
metricMap: m.metricMap,
|
||||
curry: newCurry,
|
||||
hashAdd: m.hashAdd,
|
||||
|
@ -136,7 +161,34 @@ func (m *metricVec) curryWith(labels Labels) (*metricVec, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
// GetMetricWithLabelValues returns the Metric for the given slice of label
|
||||
// values (same order as the variable labels in Desc). If that combination of
|
||||
// label values is accessed for the first time, a new Metric is created (by
|
||||
// calling the newMetric function provided during construction of the
|
||||
// MetricVec).
|
||||
//
|
||||
// It is possible to call this method without using the returned Metric to only
|
||||
// create the new Metric but leave it in its initial state.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// An error is returned if the number of label values is not the same as the
|
||||
// number of variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// Note that for more than one label value, this method is prone to mistakes
|
||||
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
|
||||
// an alternative to avoid that type of mistake. For higher label numbers, the
|
||||
// latter has a much more readable (albeit more verbose) syntax, but it comes
|
||||
// with a performance overhead (for creating and processing the Labels map).
|
||||
//
|
||||
// Note that GetMetricWithLabelValues is usually not called directly but through
|
||||
// a wrapper around MetricVec, implementing a vector for a specific Metric
|
||||
// implementation, for example GaugeVec.
|
||||
func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||
h, err := m.hashLabelValues(lvs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -145,7 +197,23 @@ func (m *metricVec) getMetricWithLabelValues(lvs ...string) (Metric, error) {
|
|||
return m.metricMap.getOrCreateMetricWithLabelValues(h, lvs, m.curry), 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 variable labels 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 variable labels in Desc (minus any curried labels).
|
||||
//
|
||||
// This method is used for the same purpose as
|
||||
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
|
||||
// methods.
|
||||
//
|
||||
// Note that GetMetricWith is usually not called directly but through a wrapper
|
||||
// around MetricVec, implementing a vector for a specific Metric implementation,
|
||||
// for example GaugeVec.
|
||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||
h, err := m.hashLabels(labels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -154,7 +222,7 @@ func (m *metricVec) getMetricWith(labels Labels) (Metric, error) {
|
|||
return m.metricMap.getOrCreateMetricWithLabels(h, labels, m.curry), nil
|
||||
}
|
||||
|
||||
func (m *metricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||
if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -177,7 +245,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 err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -276,7 +344,9 @@ func (m *metricMap) deleteByHashWithLabelValues(
|
|||
}
|
||||
|
||||
if len(metrics) > 1 {
|
||||
old := metrics
|
||||
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||
old[len(old)-1] = metricWithLabelValues{}
|
||||
} else {
|
||||
delete(m.metrics, h)
|
||||
}
|
||||
|
@ -302,7 +372,9 @@ func (m *metricMap) deleteByHashWithLabels(
|
|||
}
|
||||
|
||||
if len(metrics) > 1 {
|
||||
old := metrics
|
||||
m.metrics[h] = append(metrics[:i], metrics[i+1:]...)
|
||||
old[len(old)-1] = metricWithLabelValues{}
|
||||
} else {
|
||||
delete(m.metrics, h)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"fmt"
|
||||
"sort"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
@ -27,10 +28,13 @@ import (
|
|||
// registered with the wrapped Registerer in a modified way. The modified
|
||||
// Collector adds the provided Labels to all Metrics it collects (as
|
||||
// ConstLabels). The Metrics collected by the unmodified Collector must not
|
||||
// duplicate any of those labels.
|
||||
// duplicate any of those labels. Wrapping a nil value is valid, resulting
|
||||
// in a no-op Registerer.
|
||||
//
|
||||
// WrapRegistererWith provides a way to add fixed labels to a subset of
|
||||
// Collectors. It should not be used to add fixed labels to all metrics exposed.
|
||||
// Collectors. It should not be used to add fixed labels to all metrics
|
||||
// exposed. See also
|
||||
// https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
|
||||
//
|
||||
// Conflicts between Collectors registered through the original Registerer with
|
||||
// Collectors registered through the wrapping Registerer will still be
|
||||
|
@ -50,6 +54,7 @@ func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
|
|||
// Registerer. Collectors registered with the returned Registerer will be
|
||||
// registered with the wrapped Registerer in a modified way. The modified
|
||||
// Collector adds the provided prefix to the name of all Metrics it collects.
|
||||
// Wrapping a nil value is valid, resulting in a no-op Registerer.
|
||||
//
|
||||
// WrapRegistererWithPrefix is useful to have one place to prefix all metrics of
|
||||
// a sub-system. To make this work, register metrics of the sub-system with the
|
||||
|
@ -80,6 +85,9 @@ type wrappingRegisterer struct {
|
|||
}
|
||||
|
||||
func (r *wrappingRegisterer) Register(c Collector) error {
|
||||
if r.wrappedRegisterer == nil {
|
||||
return nil
|
||||
}
|
||||
return r.wrappedRegisterer.Register(&wrappingCollector{
|
||||
wrappedCollector: c,
|
||||
prefix: r.prefix,
|
||||
|
@ -88,6 +96,9 @@ func (r *wrappingRegisterer) Register(c Collector) error {
|
|||
}
|
||||
|
||||
func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
|
||||
if r.wrappedRegisterer == nil {
|
||||
return
|
||||
}
|
||||
for _, c := range cs {
|
||||
if err := r.Register(c); err != nil {
|
||||
panic(err)
|
||||
|
@ -96,6 +107,9 @@ func (r *wrappingRegisterer) MustRegister(cs ...Collector) {
|
|||
}
|
||||
|
||||
func (r *wrappingRegisterer) Unregister(c Collector) bool {
|
||||
if r.wrappedRegisterer == nil {
|
||||
return false
|
||||
}
|
||||
return r.wrappedRegisterer.Unregister(&wrappingCollector{
|
||||
wrappedCollector: c,
|
||||
prefix: r.prefix,
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
@ -320,3 +321,12 @@ func TestWrap(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNil(t *testing.T) {
|
||||
// A wrapped nil registerer should be treated as a no-op, and not panic.
|
||||
c := NewCounter(CounterOpts{Name: "test"})
|
||||
err := WrapRegistererWith(Labels{"foo": "bar"}, nil).Register(c)
|
||||
if err != nil {
|
||||
t.Fatal("registering failed:", err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue