Update go dependencies

This commit is contained in:
Manuel Alejandro de Brito Fontes 2018-12-05 13:27:09 -03:00
parent 432f534383
commit f4a4daed84
1299 changed files with 71186 additions and 91183 deletions

View file

@ -1,3 +1,67 @@
## 0.9.1 / 2018-11-03
* [FEATURE] Add `WriteToTextfile` function to facilitate the creation of
*.prom files for the textfile collector of the node exporter. #489
* [ENHANCEMENT] More descriptive error messages for inconsistent label
cardinality. #487
* [ENHANCEMENT] Exposition: Use a GZIP encoder pool to avoid allocations in
high-frequency scrape scenarios. #366
* [ENHANCEMENT] Exposition: Streaming serving of metrics data while encoding.
#482
* [ENHANCEMENT] API client: Add a way to return the body of a 5xx response.
#479
## 0.9.0 / 2018-10-15
* [CHANGE] Go1.6 is no longer supported.
* [CHANGE] More refinements of the `Registry` consistency checks: Duplicated
labels are now detected, but inconsistent label dimensions are now allowed.
Collisions with the “magic” metric and label names in Summaries and
Histograms are detected now. #108 #417 #471
* [CHANGE] Changed `ProcessCollector` constructor. #219
* [CHANGE] Changed Go counter `go_memstats_heap_released_bytes_total` to gauge
`go_memstats_heap_released_bytes`. #229
* [CHANGE] Unexported `LabelPairSorter`. #453
* [CHANGE] Removed the `Untyped` metric from direct instrumentation. #340
* [CHANGE] Unexported `MetricVec`. #319
* [CHANGE] Removed deprecated `Set` method from `Counter` #247
* [CHANGE] Removed deprecated `RegisterOrGet` and `MustRegisterOrGet`. #247
* [CHANGE] API client: Introduced versioned packages.
* [FEATURE] A `Registerer` can be wrapped with prefixes and labels. #357
* [FEATURE] “Describe by collect” helper function. #239
* [FEATURE] Added package `testutil`. #58
* [FEATURE] Timestamp can be explicitly set for const metrics. #187
* [FEATURE] “Unchecked” collectors are possible now without cheating. #47
* [FEATURE] Pushing to the Pushgateway reworked in package `push` to support
many new features. (The old functions are still usable but deprecated.) #372
#341
* [FEATURE] Configurable connection limit for scrapes. #179
* [FEATURE] New HTTP middlewares to instrument `http.Handler` and
`http.RoundTripper`. The old middlewares and the pre-instrumented `/metrics`
handler are (strongly) deprecated. #316 #57 #101 #224
* [FEATURE] “Currying” for metric vectors. #320
* [FEATURE] A `Summary` can be created without quantiles. #118
* [FEATURE] Added a `Timer` helper type. #231
* [FEATURE] Added a Graphite bridge. #197
* [FEATURE] Help strings are now optional. #460
* [FEATURE] Added `process_virtual_memory_max_bytes` metric. #438 #440
* [FEATURE] Added `go_gc_cpu_fraction` and `go_threads` metrics. #281 #277
* [FEATURE] Added `promauto` package with auto-registering metrics. #385 #393
* [FEATURE] Add `SetToCurrentTime` method to `Gauge`. #259
* [FEATURE] API client: Add AlertManager, Status, and Target methods. #402
* [FEATURE] API client: Add admin methods. #398
* [FEATURE] API client: Support series API. #361
* [FEATURE] API client: Support querying label values.
* [ENHANCEMENT] Smarter creation of goroutines during scraping. Solves memory
usage spikes in certain situations. #369
* [ENHANCEMENT] Counters are now faster if dealing with integers only. #367
* [ENHANCEMENT] Improved label validation. #274 #335
* [BUGFIX] Creating a const metric with an invalid `Desc` returns an error. #460
* [BUGFIX] Histogram observations don't race any longer with exposition. #275
* [BUGFIX] Fixed goroutine leaks. #236 #472
* [BUGFIX] Fixed an error message for exponential histogram buckets. #467
* [BUGFIX] Fixed data race writing to the metric map. #401
* [BUGFIX] API client: Decode JSON on a 4xx respons but do not on 204
responses. #476 #414
## 0.8.0 / 2016-08-17
* [CHANGE] Registry is doing more consistency checks. This might break
existing setups that used to export inconsistent metrics.

View file

@ -1,8 +0,0 @@
<!--
If you are unhappy how your favorite Go dependency management tool deals
with this repository, please do not file an issue but read
https://github.com/prometheus/client_golang#important-note-about-releases-versioning-tagging-stability-and-your-favorite-go-dependency-management-tool
instead. Thank you very much.
-->

View file

@ -16,7 +16,7 @@
# !!! Open PRs only against the prometheus/prometheus/Makefile.common repository!
# Example usage :
# Create the main Makefile in the root project directory.
# Create the main Makefile in the root project directory.
# include Makefile.common
# customTarget:
# @echo ">> Running customTarget"
@ -28,18 +28,53 @@ unexport GOBIN
GO ?= go
GOFMT ?= $(GO)fmt
FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
GOOPTS ?=
GO_VERSION ?= $(shell $(GO) version)
GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.')
unexport GOVENDOR
ifeq (, $(PRE_GO_111))
ifneq (,$(wildcard go.mod))
# Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI).
GO111MODULE := on
ifneq (,$(wildcard vendor))
# Always use the local vendor/ directory to satisfy the dependencies.
GOOPTS := $(GOOPTS) -mod=vendor
endif
endif
else
ifneq (,$(wildcard go.mod))
ifneq (,$(wildcard vendor))
$(warning This repository requires Go >= 1.11 because of Go modules)
$(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)')
endif
else
# This repository isn't using Go modules (yet).
GOVENDOR := $(FIRST_GOPATH)/bin/govendor
endif
unexport GO111MODULE
endif
PROMU := $(FIRST_GOPATH)/bin/promu
STATICCHECK := $(FIRST_GOPATH)/bin/staticcheck
GOVENDOR := $(FIRST_GOPATH)/bin/govendor
pkgs = ./...
GO_VERSION ?= $(shell $(GO) version)
GO_BUILD_PLATFORM ?= $(subst /,-,$(lastword $(GO_VERSION)))
PROMU_VERSION ?= 0.2.0
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
PREFIX ?= $(shell pwd)
BIN_DIR ?= $(shell pwd)
DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
DOCKER_REPO ?= prom
.PHONY: all
all: style staticcheck unused build test
all: precheck style staticcheck unused build test
# This rule is used to forward a target like "build" to "common-build". This
# allows a new "build" target to be defined in a Makefile which includes this
@ -70,37 +105,54 @@ common-check_license:
.PHONY: common-test-short
common-test-short:
@echo ">> running short tests"
$(GO) test -short $(pkgs)
GO111MODULE=$(GO111MODULE) $(GO) test -short $(GOOPTS) $(pkgs)
.PHONY: common-test
common-test:
@echo ">> running all tests"
$(GO) test -race $(pkgs)
GO111MODULE=$(GO111MODULE) $(GO) test -race $(GOOPTS) $(pkgs)
.PHONY: common-format
common-format:
@echo ">> formatting code"
$(GO) fmt $(pkgs)
GO111MODULE=$(GO111MODULE) $(GO) fmt $(GOOPTS) $(pkgs)
.PHONY: common-vet
common-vet:
@echo ">> vetting code"
$(GO) vet $(pkgs)
GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs)
.PHONY: common-staticcheck
common-staticcheck: $(STATICCHECK)
@echo ">> running staticcheck"
ifdef GO111MODULE
GO111MODULE=$(GO111MODULE) $(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" -checks "SA*" $(pkgs)
else
$(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" $(pkgs)
endif
.PHONY: common-unused
common-unused: $(GOVENDOR)
ifdef GOVENDOR
@echo ">> running check for unused packages"
@$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages'
else
ifdef GO111MODULE
@echo ">> running check for unused/missing packages in go.mod"
GO111MODULE=$(GO111MODULE) $(GO) mod tidy
@git diff --exit-code -- go.sum go.mod
ifneq (,$(wildcard vendor))
@echo ">> running check for unused packages in vendor/"
GO111MODULE=$(GO111MODULE) $(GO) mod vendor
@git diff --exit-code -- go.sum go.mod vendor/
endif
endif
endif
.PHONY: common-build
common-build: promu
@echo ">> building binaries"
$(PROMU) build --prefix $(PREFIX)
GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX)
.PHONY: common-tarball
common-tarball: promu
@ -120,13 +172,52 @@ common-docker-tag-latest:
docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):latest"
.PHONY: promu
promu:
GOOS= GOARCH= $(GO) get -u github.com/prometheus/promu
promu: $(PROMU)
$(PROMU):
curl -s -L $(PROMU_URL) | tar -xvz -C /tmp
mkdir -v -p $(FIRST_GOPATH)/bin
cp -v /tmp/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(PROMU)
.PHONY: proto
proto:
@echo ">> generating code from proto files"
@./scripts/genproto.sh
.PHONY: $(STATICCHECK)
$(STATICCHECK):
GOOS= GOARCH= $(GO) get -u honnef.co/go/tools/cmd/staticcheck
ifdef GO111MODULE
# Get staticcheck from a temporary directory to avoid modifying the local go.{mod,sum}.
# See https://github.com/golang/go/issues/27643.
# For now, we are using the next branch of staticcheck because master isn't compatible yet with Go modules.
tmpModule=$$(mktemp -d 2>&1) && \
mkdir -p $${tmpModule}/staticcheck && \
cd "$${tmpModule}"/staticcheck && \
GO111MODULE=on $(GO) mod init example.com/staticcheck && \
GO111MODULE=on GOOS= GOARCH= $(GO) get -u honnef.co/go/tools/cmd/staticcheck@next && \
rm -rf $${tmpModule};
else
GOOS= GOARCH= GO111MODULE=off $(GO) get -u honnef.co/go/tools/cmd/staticcheck
endif
ifdef GOVENDOR
.PHONY: $(GOVENDOR)
$(GOVENDOR):
GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor
endif
.PHONY: precheck
precheck::
define PRECHECK_COMMAND_template =
precheck:: $(1)_precheck
PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1)))
.PHONY: $(1)_precheck
$(1)_precheck:
@if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \
echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \
exit 1; \
fi
endef

View file

@ -11,7 +11,7 @@ Prometheus HTTP API.
__This library requires Go1.7 or later.__
## Important note about releases, versioning, tagging, stability, and your favorite Go dependency management tool
## Important note about releases, versioning, tagging, and stability
While our goal is to follow [Semantic Versioning](https://semver.org/), this
repository is still pre-1.0.0. To quote the
@ -22,38 +22,42 @@ declaring something 1.0.0 doesn't make it 1.0.0. Instead, we are working
towards a 1.0.0 release that actually deserves its major version number.
Having said that, we aim for always keeping the tip of master in a workable
state and for only introducing ”mildly” breaking changes up to and including
[v0.9.0](https://github.com/prometheus/client_golang/milestone/1). After that,
a number of ”hard” breaking changes are planned, see the
[v0.10.0 milestone](https://github.com/prometheus/client_golang/milestone/2),
which should get the library much closer to 1.0.0 state.
state. We occasionally tag versions and track their changes in CHANGELOG.md,
but this happens mostly to keep dependency management tools happy and to give
people a handle they can talk about easily. In particular, all commits in the
master branch have passed the same testing and reviewing. There is no QA
process in place that would render tagged commits more stable or better tested
than others.
Dependency management in Go projects is still in flux, and there are many tools
floating around. While [dep](https://golang.github.io/dep/) might develop into
the de-facto standard tool, it is still officially experimental. The roadmap
for this library has been laid out with a lot of sometimes painful experience
in mind. We really cannot adjust it every other month to the needs of the
currently most popular or most promising Go dependency management tool. The
recommended course of action with dependency management tools is the following:
There is a plan behind the current (pre-1.0.0) versioning, though:
- Do not expect strict post-1.0.0 semver semantics prior to the 1.0.0
release. If your dependency management tool expects strict post-1.0.0 semver
semantics, you have to wait. Sorry.
- If you want absolute certainty, please lock to a specific commit. You can
also lock to tags, but please don't ask for more tagging. This would suggest
some release or stability testing procedure that simply is not in place. As
said above, we are aiming for stability of the tip of master, but if we
tagged every single commit, locking to tags would be the same as locking to
commits.
- If you want to get the newer features and improvements and are willing to
take the minor risk of newly introduced bugs and “mild” breakage, just always
update to the tip of master (which is essentially the original idea of Go
dependency management). We recommend to not use features marked as
_deprecated_ in this case.
- Once [v0.9.0](https://github.com/prometheus/client_golang/milestone/1) is
out, you could lock to v0.9.x to get bugfixes (and perhaps minor new
features) while avoiding the “hard” breakage that will come with post-0.9
features.
- v0.9 is the “production release”, currently tracked in the master
branch. “Patch” releases will usually be just bug fixes, indeed, but
important new features that do not require invasive code changes might also
be included in those. We do not plan any breaking changes from one v0.9.x
release to any later v0.9.y release, but nothing is guaranteed. Since the
master branch will eventually be switched over to track the upcoming v0.10
(see below), we recommend to tell your dependency management tool of choice
to use the latest v0.9.x release, at least for your production software. In
that way, you should get bug fixes and non-invasive, low-risk new features
without the need to change anything on your part.
- v0.10 is a planned release that will have a _lot_ of breaking changes
(despite being only a “minor” release in the Semantic Versioning terminology,
but as said, pre-1.0.0 means nothing is guaranteed). Essentially, we have
been piling up feature requests that require breaking changes for a while,
and they are all collected in the
[v0.10 milestone](https://github.com/prometheus/client_golang/milestone/2).
Since there will be so many breaking changes, the development for v0.10 is
currently not happening in the master branch, but in the
[dev-0.10 branch](https://github.com/prometheus/client_golang/tree/dev-0.10).
It will violently change for a while, and it will definitely be in a
non-working state now and then. It should only be used for sneak-peaks and
discussions of the new features and designs.
- Once v0.10 is ready for real-life use, it will be merged into the master
branch (which is the reason why you should lock your dependency management
tool to v0.9.x and only migrate to v0.10 when both you and v0.10 are ready
for it). In the ideal case, v0.10 will be the basis for the future v1.0
release, but we cannot provide an ETA at this time.
## Instrumenting applications
@ -62,8 +66,8 @@ recommended course of action with dependency management tools is the following:
The
[`prometheus` directory](https://github.com/prometheus/client_golang/tree/master/prometheus)
contains the instrumentation library. See the
[best practices section](http://prometheus.io/docs/practices/naming/) of the
Prometheus documentation to learn more about instrumenting applications.
[guide](https://prometheus.io/docs/guides/go-application/) on the Prometheus
website to learn more about instrumenting applications.
The
[`examples` directory](https://github.com/prometheus/client_golang/tree/master/examples)

View file

@ -1 +1 @@
0.8.0
0.9.1

12
vendor/github.com/prometheus/client_golang/go.mod generated vendored Normal file
View file

@ -0,0 +1,12 @@
module github.com/prometheus/client_golang
require (
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973
github.com/golang/protobuf v1.2.0
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
)

16
vendor/github.com/prometheus/client_golang/go.sum generated vendored Normal file
View file

@ -0,0 +1,16 @@
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce h1:X0jFYGnHemYDIW6jlc+fSI8f9Cg+jqCnClYP2WgZT/A=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View file

@ -40,7 +40,8 @@ type Collector interface {
// Collector may yield any Metric it sees fit in its Collect method.
//
// This method idempotently sends the same descriptors throughout the
// lifetime of the Collector.
// lifetime of the Collector. It may be called concurrently and
// therefore must be implemented in a concurrency safe way.
//
// If a Collector encounters an error while executing this method, it
// must send an invalid descriptor (created with NewInvalidDesc) to

View file

@ -136,7 +136,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
return &CounterVec{
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
if len(lvs) != len(desc.variableLabels) {
panic(errInconsistentCardinality)
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
}
result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
result.init(result) // Init self-collection.

View file

@ -93,7 +93,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
// First add only the const label names and sort them...
for labelName := range constLabels {
if !checkLabelName(labelName) {
d.err = fmt.Errorf("%q is not a valid label name", labelName)
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
return d
}
labelNames = append(labelNames, labelName)
@ -115,7 +115,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
// dimension with a different mix between preset and variable labels.
for _, labelName := range variableLabels {
if !checkLabelName(labelName) {
d.err = fmt.Errorf("%q is not a valid label name", labelName)
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
return d
}
labelNames = append(labelNames, "$"+labelName)

View file

@ -147,7 +147,7 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
return &GaugeVec{
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
if len(lvs) != len(desc.variableLabels) {
panic(errInconsistentCardinality)
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
}
result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
result.init(result) // Init self-collection.

View file

@ -165,7 +165,7 @@ func NewHistogram(opts HistogramOpts) Histogram {
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
if len(desc.variableLabels) != len(labelValues) {
panic(errInconsistentCardinality)
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
}
for _, n := range desc.variableLabels {

View file

@ -15,9 +15,7 @@ package prometheus
import (
"bufio"
"bytes"
"compress/gzip"
"fmt"
"io"
"net"
"net/http"
@ -41,19 +39,10 @@ const (
acceptEncodingHeader = "Accept-Encoding"
)
var bufPool sync.Pool
func getBuf() *bytes.Buffer {
buf := bufPool.Get()
if buf == nil {
return &bytes.Buffer{}
}
return buf.(*bytes.Buffer)
}
func giveBuf(buf *bytes.Buffer) {
buf.Reset()
bufPool.Put(buf)
var gzipPool = sync.Pool{
New: func() interface{} {
return gzip.NewWriter(nil)
},
}
// Handler returns an HTTP handler for the DefaultGatherer. It is
@ -71,58 +60,40 @@ func Handler() http.Handler {
// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
// instead. See there for further documentation.
func UninstrumentedHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
mfs, err := DefaultGatherer.Gather()
if err != nil {
http.Error(w, "An error has occurred during metrics collection:\n\n"+err.Error(), http.StatusInternalServerError)
httpError(rsp, err)
return
}
contentType := expfmt.Negotiate(req.Header)
buf := getBuf()
defer giveBuf(buf)
writer, encoding := decorateWriter(req, buf)
enc := expfmt.NewEncoder(writer, contentType)
var lastErr error
header := rsp.Header()
header.Set(contentTypeHeader, string(contentType))
w := io.Writer(rsp)
if gzipAccepted(req.Header) {
header.Set(contentEncodingHeader, "gzip")
gz := gzipPool.Get().(*gzip.Writer)
defer gzipPool.Put(gz)
gz.Reset(w)
defer gz.Close()
w = gz
}
enc := expfmt.NewEncoder(w, contentType)
for _, mf := range mfs {
if err := enc.Encode(mf); err != nil {
lastErr = err
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
httpError(rsp, err)
return
}
}
if closer, ok := writer.(io.Closer); ok {
closer.Close()
}
if lastErr != nil && buf.Len() == 0 {
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
return
}
header := w.Header()
header.Set(contentTypeHeader, string(contentType))
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
if encoding != "" {
header.Set(contentEncodingHeader, encoding)
}
w.Write(buf.Bytes())
})
}
// decorateWriter wraps a writer to handle gzip compression if requested. It
// returns the decorated writer and the appropriate "Content-Encoding" header
// (which is empty if no compression is enabled).
func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) {
header := request.Header.Get(acceptEncodingHeader)
parts := strings.Split(header, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
return gzip.NewWriter(writer), "gzip"
}
}
return writer, ""
}
var instLabels = []string{"method", "code"}
type nower interface {
@ -503,3 +474,31 @@ func sanitizeCode(s int) string {
return strconv.Itoa(s)
}
}
// gzipAccepted returns whether the client will accept gzip-encoded content.
func gzipAccepted(header http.Header) bool {
a := header.Get(acceptEncodingHeader)
parts := strings.Split(a, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
return true
}
}
return false
}
// 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.
func httpError(rsp http.ResponseWriter, err error) {
rsp.Header().Del(contentEncodingHeader)
http.Error(
rsp,
"An error has occurred while serving metrics:\n\n"+err.Error(),
http.StatusInternalServerError,
)
}

View file

@ -37,9 +37,22 @@ const reservedLabelPrefix = "__"
var errInconsistentCardinality = errors.New("inconsistent label cardinality")
func makeInconsistentCardinalityError(fqName string, labels, labelValues []string) error {
return fmt.Errorf(
"%s: %q has %d variable labels named %q but %d values %q were provided",
errInconsistentCardinality, fqName,
len(labels), labels,
len(labelValues), labelValues,
)
}
func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
if len(labels) != expectedNumberOfValues {
return errInconsistentCardinality
return fmt.Errorf(
"%s: expected %d label values but got %d in %#v",
errInconsistentCardinality, expectedNumberOfValues,
len(labels), labels,
)
}
for name, val := range labels {
@ -53,7 +66,11 @@ func validateValuesInLabels(labels Labels, expectedNumberOfValues int) error {
func validateLabelValues(vals []string, expectedNumberOfValues int) error {
if len(vals) != expectedNumberOfValues {
return errInconsistentCardinality
return fmt.Errorf(
"%s: expected %d label values but got %d in %#v",
errInconsistentCardinality, expectedNumberOfValues,
len(vals), vals,
)
}
for _, val := range vals {

View file

@ -32,7 +32,6 @@
package promhttp
import (
"bytes"
"compress/gzip"
"fmt"
"io"
@ -53,19 +52,10 @@ const (
acceptEncodingHeader = "Accept-Encoding"
)
var bufPool sync.Pool
func getBuf() *bytes.Buffer {
buf := bufPool.Get()
if buf == nil {
return &bytes.Buffer{}
}
return buf.(*bytes.Buffer)
}
func giveBuf(buf *bytes.Buffer) {
buf.Reset()
bufPool.Put(buf)
var gzipPool = sync.Pool{
New: func() interface{} {
return gzip.NewWriter(nil)
},
}
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
@ -100,19 +90,18 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight)
}
h := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
if inFlightSem != nil {
select {
case inFlightSem <- struct{}{}: // All good, carry on.
defer func() { <-inFlightSem }()
default:
http.Error(w, fmt.Sprintf(
http.Error(rsp, fmt.Sprintf(
"Limit of concurrent requests reached (%d), try again later.", opts.MaxRequestsInFlight,
), http.StatusServiceUnavailable)
return
}
}
mfs, err := reg.Gather()
if err != nil {
if opts.ErrorLog != nil {
@ -123,26 +112,40 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
panic(err)
case ContinueOnError:
if len(mfs) == 0 {
http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError)
// Still report the error if no metrics have been gathered.
httpError(rsp, err)
return
}
case HTTPErrorOnError:
http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError)
httpError(rsp, err)
return
}
}
contentType := expfmt.Negotiate(req.Header)
buf := getBuf()
defer giveBuf(buf)
writer, encoding := decorateWriter(req, buf, opts.DisableCompression)
enc := expfmt.NewEncoder(writer, contentType)
header := rsp.Header()
header.Set(contentTypeHeader, string(contentType))
w := io.Writer(rsp)
if !opts.DisableCompression && gzipAccepted(req.Header) {
header.Set(contentEncodingHeader, "gzip")
gz := gzipPool.Get().(*gzip.Writer)
defer gzipPool.Put(gz)
gz.Reset(w)
defer gz.Close()
w = gz
}
enc := expfmt.NewEncoder(w, contentType)
var lastErr error
for _, mf := range mfs {
if err := enc.Encode(mf); err != nil {
lastErr = err
if opts.ErrorLog != nil {
opts.ErrorLog.Println("error encoding metric family:", err)
opts.ErrorLog.Println("error encoding and sending metric family:", err)
}
switch opts.ErrorHandling {
case PanicOnError:
@ -150,28 +153,15 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
case ContinueOnError:
// Handled later.
case HTTPErrorOnError:
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError)
httpError(rsp, err)
return
}
}
}
if closer, ok := writer.(io.Closer); ok {
closer.Close()
if lastErr != nil {
httpError(rsp, lastErr)
}
if lastErr != nil && buf.Len() == 0 {
http.Error(w, "No metrics encoded, last error:\n\n"+lastErr.Error(), http.StatusInternalServerError)
return
}
header := w.Header()
header.Set(contentTypeHeader, string(contentType))
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
if encoding != "" {
header.Set(contentEncodingHeader, encoding)
}
if _, err := w.Write(buf.Bytes()); err != nil && opts.ErrorLog != nil {
opts.ErrorLog.Println("error while sending encoded metrics:", err)
}
// TODO(beorn7): Consider streaming serving of metrics.
})
if opts.Timeout <= 0 {
@ -292,20 +282,30 @@ type HandlerOpts struct {
Timeout time.Duration
}
// decorateWriter wraps a writer to handle gzip compression if requested. It
// returns the decorated writer and the appropriate "Content-Encoding" header
// (which is empty if no compression is enabled).
func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) {
if compressionDisabled {
return writer, ""
}
header := request.Header.Get(acceptEncodingHeader)
parts := strings.Split(header, ",")
// gzipAccepted returns whether the client will accept gzip-encoded content.
func gzipAccepted(header http.Header) bool {
a := header.Get(acceptEncodingHeader)
parts := strings.Split(a, ",")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "gzip" || strings.HasPrefix(part, "gzip;") {
return gzip.NewWriter(writer), "gzip"
return true
}
}
return writer, ""
return false
}
// 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.
func httpError(rsp http.ResponseWriter, err error) {
rsp.Header().Del(contentEncodingHeader)
http.Error(
rsp,
"An error has occurred while serving metrics:\n\n"+err.Error(),
http.StatusInternalServerError,
)
}

View file

@ -16,6 +16,9 @@ package prometheus
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
@ -23,6 +26,7 @@ import (
"unicode/utf8"
"github.com/golang/protobuf/proto"
"github.com/prometheus/common/expfmt"
dto "github.com/prometheus/client_model/go"
@ -107,9 +111,6 @@ type Registerer interface {
// Collector, and for providing a Collector that will not cause
// inconsistent metrics on collection. (This would lead to scrape
// errors.)
//
// It is in general not safe to register the same Collector multiple
// times concurrently.
Register(Collector) error
// MustRegister works like Register but registers any number of
// Collectors and panics upon the first registration that causes an
@ -273,7 +274,12 @@ func (r *Registry) Register(c Collector) error {
close(descChan)
}()
r.mtx.Lock()
defer r.mtx.Unlock()
defer func() {
// Drain channel in case of premature return to not leak a goroutine.
for range descChan {
}
r.mtx.Unlock()
}()
// Conduct various tests...
for desc := range descChan {
@ -531,6 +537,38 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
}
// WriteToTextfile calls Gather on the provided Gatherer, encodes the result in the
// Prometheus text format, and writes it to a temporary file. Upon success, the
// temporary file is renamed to the provided filename.
//
// This is intended for use with the textfile collector of the node exporter.
// Note that the node exporter expects the filename to be suffixed with ".prom".
func WriteToTextfile(filename string, g Gatherer) error {
tmp, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
if err != nil {
return err
}
defer os.Remove(tmp.Name())
mfs, err := g.Gather()
if err != nil {
return err
}
for _, mf := range mfs {
if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil {
return err
}
}
if err := tmp.Close(); err != nil {
return err
}
if err := os.Chmod(tmp.Name(), 0644); err != nil {
return err
}
return os.Rename(tmp.Name(), filename)
}
// processMetric is an internal helper method only used by the Gather method.
func processMetric(
metric Metric,
@ -785,6 +823,8 @@ func checkMetricConsistency(
dtoMetric *dto.Metric,
metricHashes map[uint64]struct{},
) error {
name := metricFamily.GetName()
// Type consistency with metric family.
if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
@ -793,33 +833,42 @@ func checkMetricConsistency(
metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
return fmt.Errorf(
"collected metric %q { %s} is not a %s",
metricFamily.GetName(), dtoMetric, metricFamily.GetType(),
name, dtoMetric, metricFamily.GetType(),
)
}
previousLabelName := ""
for _, labelPair := range dtoMetric.GetLabel() {
if !checkLabelName(labelPair.GetName()) {
labelName := labelPair.GetName()
if labelName == previousLabelName {
return fmt.Errorf(
"collected metric %q { %s} has a label with an invalid name: %s",
metricFamily.GetName(), dtoMetric, labelPair.GetName(),
"collected metric %q { %s} has two or more labels with the same name: %s",
name, dtoMetric, labelName,
)
}
if dtoMetric.Summary != nil && labelPair.GetName() == quantileLabel {
if !checkLabelName(labelName) {
return fmt.Errorf(
"collected metric %q { %s} has a label with an invalid name: %s",
name, dtoMetric, labelName,
)
}
if dtoMetric.Summary != nil && labelName == quantileLabel {
return fmt.Errorf(
"collected metric %q { %s} must not have an explicit %q label",
metricFamily.GetName(), dtoMetric, quantileLabel,
name, dtoMetric, quantileLabel,
)
}
if !utf8.ValidString(labelPair.GetValue()) {
return fmt.Errorf(
"collected metric %q { %s} has a label named %q whose value is not utf8: %#v",
metricFamily.GetName(), dtoMetric, labelPair.GetName(), labelPair.GetValue())
name, dtoMetric, labelName, labelPair.GetValue())
}
previousLabelName = labelName
}
// Is the metric unique (i.e. no other metric with the same name and the same labels)?
h := hashNew()
h = hashAdd(h, metricFamily.GetName())
h = hashAdd(h, name)
h = hashAddByte(h, separatorByte)
// Make sure label pairs are sorted. We depend on it for the consistency
// check.
@ -833,7 +882,7 @@ func checkMetricConsistency(
if _, exists := metricHashes[h]; exists {
return fmt.Errorf(
"collected metric %q { %s} was collected before with the same name and label values",
metricFamily.GetName(), dtoMetric,
name, dtoMetric,
)
}
metricHashes[h] = struct{}{}

View file

@ -181,7 +181,7 @@ func NewSummary(opts SummaryOpts) Summary {
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
if len(desc.variableLabels) != len(labelValues) {
panic(errInconsistentCardinality)
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
}
for _, n := range desc.variableLabels {