Upgrading server dependancies (#8308)
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
b112747de7
Коммит
6d8f122a51
8
vendor/github.com/prometheus/client_golang/ISSUE_TEMPLATE.md
сгенерированный
поставляемый
Обычный файл
8
vendor/github.com/prometheus/client_golang/ISSUE_TEMPLATE.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
47
vendor/github.com/prometheus/client_golang/README.md
сгенерированный
поставляемый
47
vendor/github.com/prometheus/client_golang/README.md
сгенерированный
поставляемый
@@ -2,6 +2,7 @@
|
||||
|
||||
[](https://travis-ci.org/prometheus/client_golang)
|
||||
[](https://goreportcard.com/report/github.com/prometheus/client_golang)
|
||||
[](https://godoc.org/github.com/prometheus/client_golang)
|
||||
|
||||
This is the [Go](http://golang.org) client library for
|
||||
[Prometheus](http://prometheus.io). It has two separate parts, one for
|
||||
@@ -10,6 +11,50 @@ Prometheus HTTP API.
|
||||
|
||||
__This library requires Go1.7 or later.__
|
||||
|
||||
## Important note about releases, versioning, tagging, stability, and your favorite Go dependency management tool
|
||||
|
||||
While our goal is to follow [Semantic Versioning](https://semver.org/), this
|
||||
repository is still pre-1.0.0. To quote the
|
||||
[Semantic Versioning spec](https://semver.org/#spec-item-4): “Anything may
|
||||
change at any time. The public API should not be considered stable.” We know
|
||||
that this is at odds with the widespread use of this library. However, just
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
- 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.
|
||||
|
||||
## Instrumenting applications
|
||||
|
||||
[](http://gocover.io/github.com/prometheus/client_golang/prometheus) [](https://godoc.org/github.com/prometheus/client_golang/prometheus)
|
||||
@@ -26,7 +71,7 @@ contains simple examples of instrumented code.
|
||||
|
||||
## Client for the Prometheus HTTP API
|
||||
|
||||
[](http://gocover.io/github.com/prometheus/client_golang/api/prometheus) [](https://godoc.org/github.com/prometheus/client_golang/api/prometheus)
|
||||
[](http://gocover.io/github.com/prometheus/client_golang/api/prometheus/v1) [](https://godoc.org/github.com/prometheus/client_golang/api)
|
||||
|
||||
The
|
||||
[`api/prometheus` directory](https://github.com/prometheus/client_golang/tree/master/api/prometheus)
|
||||
|
||||
8
vendor/github.com/prometheus/client_golang/prometheus/doc.go
сгенерированный
поставляемый
8
vendor/github.com/prometheus/client_golang/prometheus/doc.go
сгенерированный
поставляемый
@@ -11,10 +11,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package prometheus provides metrics primitives to instrument code for
|
||||
// monitoring. It also offers a registry for metrics. Sub-packages allow to
|
||||
// expose the registered metrics via HTTP (package promhttp) or push them to a
|
||||
// Pushgateway (package push).
|
||||
// Package prometheus is the core instrumentation package. It provides metrics
|
||||
// primitives to instrument code for monitoring. It also offers a registry for
|
||||
// metrics. Sub-packages allow to expose the registered metrics via HTTP
|
||||
// (package promhttp) or push them to a Pushgateway (package push).
|
||||
//
|
||||
// All exported functions and methods are safe to be used concurrently unless
|
||||
// specified otherwise.
|
||||
|
||||
5
vendor/github.com/prometheus/client_golang/prometheus/http.go
сгенерированный
поставляемый
5
vendor/github.com/prometheus/client_golang/prometheus/http.go
сгенерированный
поставляемый
@@ -61,9 +61,8 @@ func giveBuf(buf *bytes.Buffer) {
|
||||
// name).
|
||||
//
|
||||
// Deprecated: Please note the issues described in the doc comment of
|
||||
// InstrumentHandler. You might want to consider using promhttp.Handler instead
|
||||
// (which is not instrumented, but can be instrumented with the tooling provided
|
||||
// in package promhttp).
|
||||
// InstrumentHandler. You might want to consider using
|
||||
// promhttp.InstrumentedHandler instead.
|
||||
func Handler() http.Handler {
|
||||
return InstrumentHandler("prometheus", UninstrumentedHandler())
|
||||
}
|
||||
|
||||
14
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
сгенерированный
поставляемый
14
vendor/github.com/prometheus/client_golang/prometheus/process_collector.go
сгенерированный
поставляемый
@@ -26,8 +26,11 @@ type processCollector struct {
|
||||
}
|
||||
|
||||
// NewProcessCollector returns a collector which exports the current state of
|
||||
// process metrics including cpu, memory and file descriptor usage as well as
|
||||
// the process start time for the given process id under the given namespace.
|
||||
// process metrics including CPU, memory and file descriptor usage as well as
|
||||
// the process start time for the given process ID under the given namespace.
|
||||
//
|
||||
// Currently, the collector depends on a Linux-style proc filesystem and
|
||||
// therefore only exports metrics for Linux.
|
||||
func NewProcessCollector(pid int, namespace string) Collector {
|
||||
return NewProcessCollectorPIDFn(
|
||||
func() (int, error) { return pid, nil },
|
||||
@@ -35,11 +38,8 @@ func NewProcessCollector(pid int, namespace string) Collector {
|
||||
)
|
||||
}
|
||||
|
||||
// NewProcessCollectorPIDFn returns a collector which exports the current state
|
||||
// of process metrics including cpu, memory and file descriptor usage as well
|
||||
// as the process start time under the given namespace. The given pidFn is
|
||||
// called on each collect and is used to determine the process to export
|
||||
// metrics for.
|
||||
// NewProcessCollectorPIDFn works like NewProcessCollector but the process ID is
|
||||
// determined on each collect anew by calling the given pidFn function.
|
||||
func NewProcessCollectorPIDFn(
|
||||
pidFn func() (int, error),
|
||||
namespace string,
|
||||
|
||||
6
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
сгенерированный
поставляемый
6
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator.go
сгенерированный
поставляемый
@@ -102,10 +102,10 @@ func init() {
|
||||
return d
|
||||
}
|
||||
pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1
|
||||
return closeNotifierDelegator{d}
|
||||
return &closeNotifierDelegator{d}
|
||||
}
|
||||
pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2
|
||||
return flusherDelegator{d}
|
||||
return &flusherDelegator{d}
|
||||
}
|
||||
pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3
|
||||
return struct {
|
||||
@@ -115,7 +115,7 @@ func init() {
|
||||
}{d, &flusherDelegator{d}, &closeNotifierDelegator{d}}
|
||||
}
|
||||
pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4
|
||||
return hijackerDelegator{d}
|
||||
return &hijackerDelegator{d}
|
||||
}
|
||||
pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5
|
||||
return struct {
|
||||
|
||||
2
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
сгенерированный
поставляемый
2
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
сгенерированный
поставляемый
@@ -28,7 +28,7 @@ func (d *pusherDelegator) Push(target string, opts *http.PushOptions) error {
|
||||
|
||||
func init() {
|
||||
pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16
|
||||
return pusherDelegator{d}
|
||||
return &pusherDelegator{d}
|
||||
}
|
||||
pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17
|
||||
return struct {
|
||||
|
||||
129
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
сгенерированный
поставляемый
129
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go
сгенерированный
поставляемый
@@ -39,6 +39,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
|
||||
@@ -67,21 +68,51 @@ func giveBuf(buf *bytes.Buffer) {
|
||||
bufPool.Put(buf)
|
||||
}
|
||||
|
||||
// Handler returns an HTTP handler for the prometheus.DefaultGatherer. The
|
||||
// Handler uses the default HandlerOpts, i.e. report the first error as an HTTP
|
||||
// error, no error logging, and compression if requested by the client.
|
||||
// Handler returns an http.Handler for the prometheus.DefaultGatherer, using
|
||||
// default HandlerOpts, i.e. it reports the first error as an HTTP error, it has
|
||||
// no error logging, and it applies compression if requested by the client.
|
||||
//
|
||||
// If you want to create a Handler for the DefaultGatherer with different
|
||||
// HandlerOpts, create it with HandlerFor with prometheus.DefaultGatherer and
|
||||
// your desired HandlerOpts.
|
||||
// The returned http.Handler is already instrumented using the
|
||||
// InstrumentMetricHandler function and the prometheus.DefaultRegisterer. If you
|
||||
// create multiple http.Handlers by separate calls of the Handler function, the
|
||||
// metrics used for instrumentation will be shared between them, providing
|
||||
// global scrape counts.
|
||||
//
|
||||
// This function is meant to cover the bulk of basic use cases. If you are doing
|
||||
// anything that requires more customization (including using a non-default
|
||||
// Gatherer, different instrumentation, and non-default HandlerOpts), use the
|
||||
// HandlerFor function. See there for details.
|
||||
func Handler() http.Handler {
|
||||
return HandlerFor(prometheus.DefaultGatherer, HandlerOpts{})
|
||||
return InstrumentMetricHandler(
|
||||
prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}),
|
||||
)
|
||||
}
|
||||
|
||||
// HandlerFor returns an http.Handler for the provided Gatherer. The behavior
|
||||
// of the Handler is defined by the provided HandlerOpts.
|
||||
// HandlerFor returns an uninstrumented http.Handler for the provided
|
||||
// Gatherer. The behavior of the Handler is defined by the provided
|
||||
// HandlerOpts. Thus, HandlerFor is useful to create http.Handlers for custom
|
||||
// Gatherers, with non-default HandlerOpts, and/or with custom (or no)
|
||||
// instrumentation. Use the InstrumentMetricHandler function to apply the same
|
||||
// kind of instrumentation as it is used by the Handler function.
|
||||
func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
var inFlightSem chan struct{}
|
||||
if opts.MaxRequestsInFlight > 0 {
|
||||
inFlightSem = make(chan struct{}, opts.MaxRequestsInFlight)
|
||||
}
|
||||
|
||||
h := http.HandlerFunc(func(w 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(
|
||||
"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 {
|
||||
@@ -137,9 +168,70 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {
|
||||
if encoding != "" {
|
||||
header.Set(contentEncodingHeader, encoding)
|
||||
}
|
||||
w.Write(buf.Bytes())
|
||||
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 {
|
||||
return h
|
||||
}
|
||||
return http.TimeoutHandler(h, opts.Timeout, fmt.Sprintf(
|
||||
"Exceeded configured timeout of %v.\n",
|
||||
opts.Timeout,
|
||||
))
|
||||
}
|
||||
|
||||
// InstrumentMetricHandler is usually used with an http.Handler returned by the
|
||||
// HandlerFor function. It instruments the provided http.Handler with two
|
||||
// metrics: A counter vector "promhttp_metric_handler_requests_total" to count
|
||||
// scrapes partitioned by HTTP status code, and a gauge
|
||||
// "promhttp_metric_handler_requests_in_flight" to track the number of
|
||||
// simultaneous scrapes. This function idempotently registers collectors for
|
||||
// both metrics with the provided Registerer. It panics if the registration
|
||||
// fails. The provided metrics are useful to see how many scrapes hit the
|
||||
// monitored target (which could be from different Prometheus servers or other
|
||||
// scrapers), and how often they overlap (which would result in more than one
|
||||
// scrape in flight at the same time). Note that the scrapes-in-flight gauge
|
||||
// will contain the scrape by which it is exposed, while the scrape counter will
|
||||
// only get incremented after the scrape is complete (as only then the status
|
||||
// code is known). For tracking scrape durations, use the
|
||||
// "scrape_duration_seconds" gauge created by the Prometheus server upon each
|
||||
// scrape.
|
||||
func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) http.Handler {
|
||||
cnt := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "promhttp_metric_handler_requests_total",
|
||||
Help: "Total number of scrapes by HTTP status code.",
|
||||
},
|
||||
[]string{"code"},
|
||||
)
|
||||
// Initialize the most likely HTTP status codes.
|
||||
cnt.WithLabelValues("200")
|
||||
cnt.WithLabelValues("500")
|
||||
cnt.WithLabelValues("503")
|
||||
if err := reg.Register(cnt); err != nil {
|
||||
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
||||
cnt = are.ExistingCollector.(*prometheus.CounterVec)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
gge := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "promhttp_metric_handler_requests_in_flight",
|
||||
Help: "Current number of scrapes being served.",
|
||||
})
|
||||
if err := reg.Register(gge); err != nil {
|
||||
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
||||
gge = are.ExistingCollector.(prometheus.Gauge)
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return InstrumentHandlerCounter(cnt, InstrumentHandlerInFlight(gge, handler))
|
||||
}
|
||||
|
||||
// HandlerErrorHandling defines how a Handler serving metrics will handle
|
||||
@@ -183,6 +275,21 @@ type HandlerOpts struct {
|
||||
// If DisableCompression is true, the handler will never compress the
|
||||
// response, even if requested by the client.
|
||||
DisableCompression bool
|
||||
// The number of concurrent HTTP requests is limited to
|
||||
// MaxRequestsInFlight. Additional requests are responded to with 503
|
||||
// Service Unavailable and a suitable message in the body. If
|
||||
// MaxRequestsInFlight is 0 or negative, no limit is applied.
|
||||
MaxRequestsInFlight int
|
||||
// If handling a request takes longer than Timeout, it is responded to
|
||||
// with 503 ServiceUnavailable and a suitable Message. No timeout is
|
||||
// applied if Timeout is 0 or negative. Note that with the current
|
||||
// implementation, reaching the timeout simply ends the HTTP requests as
|
||||
// described above (and even that only if sending of the body hasn't
|
||||
// started yet), while the bulk work of gathering all the metrics keeps
|
||||
// running in the background (with the eventual result to be thrown
|
||||
// away). Until the implementation is improved, it is recommended to
|
||||
// implement a separate timeout in potentially slow Collectors.
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// decorateWriter wraps a writer to handle gzip compression if requested. It
|
||||
|
||||
119
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
сгенерированный
поставляемый
119
vendor/github.com/prometheus/client_golang/prometheus/promhttp/http_test.go
сгенерированный
поставляемый
@@ -19,7 +19,9 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
@@ -37,6 +39,23 @@ func (e errorCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
)
|
||||
}
|
||||
|
||||
type blockingCollector struct {
|
||||
CollectStarted, Block chan struct{}
|
||||
}
|
||||
|
||||
func (b blockingCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- prometheus.NewDesc("dummy_desc", "not helpful", nil, nil)
|
||||
}
|
||||
|
||||
func (b blockingCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
select {
|
||||
case b.CollectStarted <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
// Collects nothing, just waits for a channel receive.
|
||||
<-b.Block
|
||||
}
|
||||
|
||||
func TestHandlerErrorHandling(t *testing.T) {
|
||||
|
||||
// Create a registry that collects a MetricFamily with two elements,
|
||||
@@ -129,3 +148,103 @@ the_count 0
|
||||
}()
|
||||
panicHandler.ServeHTTP(writer, request)
|
||||
}
|
||||
|
||||
func TestInstrumentMetricHandler(t *testing.T) {
|
||||
reg := prometheus.NewRegistry()
|
||||
handler := InstrumentMetricHandler(reg, HandlerFor(reg, HandlerOpts{}))
|
||||
// Do it again to test idempotency.
|
||||
InstrumentMetricHandler(reg, HandlerFor(reg, HandlerOpts{}))
|
||||
writer := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/", nil)
|
||||
request.Header.Add("Accept", "test/plain")
|
||||
|
||||
handler.ServeHTTP(writer, request)
|
||||
if got, want := writer.Code, http.StatusOK; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
|
||||
want := "promhttp_metric_handler_requests_in_flight 1\n"
|
||||
if got := writer.Body.String(); !strings.Contains(got, want) {
|
||||
t.Errorf("got body %q, does not contain %q", got, want)
|
||||
}
|
||||
want = "promhttp_metric_handler_requests_total{code=\"200\"} 0\n"
|
||||
if got := writer.Body.String(); !strings.Contains(got, want) {
|
||||
t.Errorf("got body %q, does not contain %q", got, want)
|
||||
}
|
||||
|
||||
writer.Body.Reset()
|
||||
handler.ServeHTTP(writer, request)
|
||||
if got, want := writer.Code, http.StatusOK; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
|
||||
want = "promhttp_metric_handler_requests_in_flight 1\n"
|
||||
if got := writer.Body.String(); !strings.Contains(got, want) {
|
||||
t.Errorf("got body %q, does not contain %q", got, want)
|
||||
}
|
||||
want = "promhttp_metric_handler_requests_total{code=\"200\"} 1\n"
|
||||
if got := writer.Body.String(); !strings.Contains(got, want) {
|
||||
t.Errorf("got body %q, does not contain %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerMaxRequestsInFlight(t *testing.T) {
|
||||
reg := prometheus.NewRegistry()
|
||||
handler := HandlerFor(reg, HandlerOpts{MaxRequestsInFlight: 1})
|
||||
w1 := httptest.NewRecorder()
|
||||
w2 := httptest.NewRecorder()
|
||||
w3 := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/", nil)
|
||||
request.Header.Add("Accept", "test/plain")
|
||||
|
||||
c := blockingCollector{Block: make(chan struct{}), CollectStarted: make(chan struct{}, 1)}
|
||||
reg.MustRegister(c)
|
||||
|
||||
rq1Done := make(chan struct{})
|
||||
go func() {
|
||||
handler.ServeHTTP(w1, request)
|
||||
close(rq1Done)
|
||||
}()
|
||||
<-c.CollectStarted
|
||||
|
||||
handler.ServeHTTP(w2, request)
|
||||
|
||||
if got, want := w2.Code, http.StatusServiceUnavailable; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
if got, want := w2.Body.String(), "Limit of concurrent requests reached (1), try again later.\n"; got != want {
|
||||
t.Errorf("got body %q, want %q", got, want)
|
||||
}
|
||||
|
||||
close(c.Block)
|
||||
<-rq1Done
|
||||
|
||||
handler.ServeHTTP(w3, request)
|
||||
|
||||
if got, want := w3.Code, http.StatusOK; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerTimeout(t *testing.T) {
|
||||
reg := prometheus.NewRegistry()
|
||||
handler := HandlerFor(reg, HandlerOpts{Timeout: time.Millisecond})
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
request, _ := http.NewRequest("GET", "/", nil)
|
||||
request.Header.Add("Accept", "test/plain")
|
||||
|
||||
c := blockingCollector{Block: make(chan struct{}), CollectStarted: make(chan struct{}, 1)}
|
||||
reg.MustRegister(c)
|
||||
|
||||
handler.ServeHTTP(w, request)
|
||||
|
||||
if got, want := w.Code, http.StatusServiceUnavailable; got != want {
|
||||
t.Errorf("got HTTP status code %d, want %d", got, want)
|
||||
}
|
||||
if got, want := w.Body.String(), "Exceeded configured timeout of 1ms.\n"; got != want {
|
||||
t.Errorf("got body %q, want %q", got, want)
|
||||
}
|
||||
|
||||
close(c.Block) // To not leak a goroutine.
|
||||
}
|
||||
|
||||
26
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server_test.go
сгенерированный
поставляемый
26
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server_test.go
сгенерированный
поставляемый
@@ -281,6 +281,16 @@ func (t *testResponseWriter) ReadFrom(io.Reader) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// testFlusher is an http.ResponseWriter that also implements http.Flusher.
|
||||
type testFlusher struct {
|
||||
flushCalled bool
|
||||
}
|
||||
|
||||
func (t *testFlusher) Header() http.Header { return nil }
|
||||
func (t *testFlusher) Write([]byte) (int, error) { return 0, nil }
|
||||
func (t *testFlusher) WriteHeader(int) {}
|
||||
func (t *testFlusher) Flush() { t.flushCalled = true }
|
||||
|
||||
func TestInterfaceUpgrade(t *testing.T) {
|
||||
w := &testResponseWriter{}
|
||||
d := newDelegator(w, nil)
|
||||
@@ -299,6 +309,22 @@ func TestInterfaceUpgrade(t *testing.T) {
|
||||
if _, ok := d.(http.Hijacker); ok {
|
||||
t.Error("delegator unexpectedly implements http.Hijacker")
|
||||
}
|
||||
|
||||
f := &testFlusher{}
|
||||
d = newDelegator(f, nil)
|
||||
if _, ok := d.(http.CloseNotifier); ok {
|
||||
t.Error("delegator unexpectedly implements http.CloseNotifier")
|
||||
}
|
||||
d.(http.Flusher).Flush()
|
||||
if !w.flushCalled {
|
||||
t.Error("Flush not called")
|
||||
}
|
||||
if _, ok := d.(io.ReaderFrom); ok {
|
||||
t.Error("delegator unexpectedly implements io.ReaderFrom")
|
||||
}
|
||||
if _, ok := d.(http.Hijacker); ok {
|
||||
t.Error("delegator unexpectedly implements http.Hijacker")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleInstrumentHandlerDuration() {
|
||||
|
||||
172
vendor/github.com/prometheus/client_golang/prometheus/push/deprecated.go
сгенерированный
поставляемый
Обычный файл
172
vendor/github.com/prometheus/client_golang/prometheus/push/deprecated.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,172 @@
|
||||
// Copyright 2018 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
// This file contains only deprecated code. Remove after v0.9 is released.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// FromGatherer triggers a metric collection by the provided Gatherer (which is
|
||||
// usually implemented by a prometheus.Registry) and pushes all gathered metrics
|
||||
// to the Pushgateway specified by url, using the provided job name and the
|
||||
// (optional) further grouping labels (the grouping map may be nil). See the
|
||||
// Pushgateway documentation for detailed implications of the job and other
|
||||
// grouping labels. Neither the job name nor any grouping label value may
|
||||
// contain a "/". The metrics pushed must not contain a job label of their own
|
||||
// nor any of the grouping labels.
|
||||
//
|
||||
// You can use just host:port or ip:port as url, in which case 'http://' is
|
||||
// added automatically. You can also include the schema in the URL. However, do
|
||||
// not include the '/metrics/jobs/...' part.
|
||||
//
|
||||
// Note that all previously pushed metrics with the same job and other grouping
|
||||
// labels will be replaced with the metrics pushed by this call. (It uses HTTP
|
||||
// method 'PUT' to push to the Pushgateway.)
|
||||
//
|
||||
// Deprecated: Please use a Pusher created with New instead.
|
||||
func FromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
|
||||
return push(job, grouping, url, g, "PUT")
|
||||
}
|
||||
|
||||
// AddFromGatherer works like FromGatherer, but only previously pushed metrics
|
||||
// with the same name (and the same job and other grouping labels) will be
|
||||
// replaced. (It uses HTTP method 'POST' to push to the Pushgateway.)
|
||||
//
|
||||
// Deprecated: Please use a Pusher created with New instead.
|
||||
func AddFromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
|
||||
return push(job, grouping, url, g, "POST")
|
||||
}
|
||||
|
||||
func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
|
||||
if !strings.Contains(pushURL, "://") {
|
||||
pushURL = "http://" + pushURL
|
||||
}
|
||||
if strings.HasSuffix(pushURL, "/") {
|
||||
pushURL = pushURL[:len(pushURL)-1]
|
||||
}
|
||||
|
||||
if strings.Contains(job, "/") {
|
||||
return fmt.Errorf("job contains '/': %s", job)
|
||||
}
|
||||
urlComponents := []string{url.QueryEscape(job)}
|
||||
for ln, lv := range grouping {
|
||||
if !model.LabelName(ln).IsValid() {
|
||||
return fmt.Errorf("grouping label has invalid name: %s", ln)
|
||||
}
|
||||
if strings.Contains(lv, "/") {
|
||||
return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
|
||||
}
|
||||
urlComponents = append(urlComponents, ln, lv)
|
||||
}
|
||||
pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))
|
||||
|
||||
mfs, err := g.Gather()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
enc := expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)
|
||||
// Check for pre-existing grouping labels:
|
||||
for _, mf := range mfs {
|
||||
for _, m := range mf.GetMetric() {
|
||||
for _, l := range m.GetLabel() {
|
||||
if l.GetName() == "job" {
|
||||
return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
|
||||
}
|
||||
if _, ok := grouping[l.GetName()]; ok {
|
||||
return fmt.Errorf(
|
||||
"pushed metric %s (%s) already contains grouping label %s",
|
||||
mf.GetName(), m, l.GetName(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
enc.Encode(mf)
|
||||
}
|
||||
req, err := http.NewRequest(method, pushURL, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 202 {
|
||||
body, _ := ioutil.ReadAll(resp.Body) // Ignore any further error as this is for an error message only.
|
||||
return fmt.Errorf("unexpected status code %d while pushing to %s: %s", resp.StatusCode, pushURL, body)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collectors works like FromGatherer, but it does not use a Gatherer. Instead,
|
||||
// it collects from the provided collectors directly. It is a convenient way to
|
||||
// push only a few metrics.
|
||||
//
|
||||
// Deprecated: Please use a Pusher created with New instead.
|
||||
func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
|
||||
return pushCollectors(job, grouping, url, "PUT", collectors...)
|
||||
}
|
||||
|
||||
// AddCollectors works like AddFromGatherer, but it does not use a Gatherer.
|
||||
// Instead, it collects from the provided collectors directly. It is a
|
||||
// convenient way to push only a few metrics.
|
||||
//
|
||||
// Deprecated: Please use a Pusher created with New instead.
|
||||
func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
|
||||
return pushCollectors(job, grouping, url, "POST", collectors...)
|
||||
}
|
||||
|
||||
func pushCollectors(job string, grouping map[string]string, url, method string, collectors ...prometheus.Collector) error {
|
||||
r := prometheus.NewRegistry()
|
||||
for _, collector := range collectors {
|
||||
if err := r.Register(collector); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return push(job, grouping, url, r, method)
|
||||
}
|
||||
|
||||
// HostnameGroupingKey returns a label map with the only entry
|
||||
// {instance="<hostname>"}. This can be conveniently used as the grouping
|
||||
// parameter if metrics should be pushed with the hostname as label. The
|
||||
// returned map is created upon each call so that the caller is free to add more
|
||||
// labels to the map.
|
||||
//
|
||||
// Deprecated: Usually, metrics pushed to the Pushgateway should not be
|
||||
// host-centric. (You would use https://github.com/prometheus/node_exporter in
|
||||
// that case.) If you have the need to add the hostname to the grouping key, you
|
||||
// are probably doing something wrong. See
|
||||
// https://prometheus.io/docs/practices/pushing/ for details.
|
||||
func HostnameGroupingKey() map[string]string {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return map[string]string{"instance": "unknown"}
|
||||
}
|
||||
return map[string]string{"instance": hostname}
|
||||
}
|
||||
32
vendor/github.com/prometheus/client_golang/prometheus/push/example_add_from_gatherer_test.go
сгенерированный
поставляемый
32
vendor/github.com/prometheus/client_golang/prometheus/push/example_add_from_gatherer_test.go
сгенерированный
поставляемый
@@ -11,12 +11,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package push_test
|
||||
|
||||
import (
|
||||
@@ -53,10 +47,14 @@ func performBackup() (int, error) {
|
||||
return 42, nil
|
||||
}
|
||||
|
||||
func ExampleAddFromGatherer() {
|
||||
func ExamplePusher_Add() {
|
||||
// We use a registry here to benefit from the consistency checks that
|
||||
// happen during registration.
|
||||
registry := prometheus.NewRegistry()
|
||||
registry.MustRegister(completionTime, duration, records)
|
||||
// Note that successTime is not registered at this time.
|
||||
// Note that successTime is not registered.
|
||||
|
||||
pusher := push.New("http://pushgateway:9091", "db_backup").Gatherer(registry)
|
||||
|
||||
start := time.Now()
|
||||
n, err := performBackup()
|
||||
@@ -67,18 +65,16 @@ func ExampleAddFromGatherer() {
|
||||
if err != nil {
|
||||
fmt.Println("DB backup failed:", err)
|
||||
} else {
|
||||
// Only now register successTime.
|
||||
registry.MustRegister(successTime)
|
||||
// Add successTime to pusher only in case of success.
|
||||
// We could as well register it with the registry.
|
||||
// This example, however, demonstrates that you can
|
||||
// mix Gatherers and Collectors when handling a Pusher.
|
||||
pusher.Collector(successTime)
|
||||
successTime.SetToCurrentTime()
|
||||
}
|
||||
// AddFromGatherer is used here rather than FromGatherer to not delete a
|
||||
// previously pushed success timestamp in case of a failure of this
|
||||
// backup.
|
||||
if err := push.AddFromGatherer(
|
||||
"db_backup", nil,
|
||||
"http://pushgateway:9091",
|
||||
registry,
|
||||
); err != nil {
|
||||
// Add is used here rather than Push to not delete a previously pushed
|
||||
// success timestamp in case of a failure of this backup.
|
||||
if err := pusher.Add(); err != nil {
|
||||
fmt.Println("Could not push to Pushgateway:", err)
|
||||
}
|
||||
}
|
||||
|
||||
11
vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
сгенерированный
поставляемый
11
vendor/github.com/prometheus/client_golang/prometheus/push/examples_test.go
сгенерированный
поставляемый
@@ -20,17 +20,16 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus/push"
|
||||
)
|
||||
|
||||
func ExampleCollectors() {
|
||||
func ExamplePusher_Push() {
|
||||
completionTime := prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "db_backup_last_completion_timestamp_seconds",
|
||||
Help: "The timestamp of the last successful completion of a DB backup.",
|
||||
})
|
||||
completionTime.SetToCurrentTime()
|
||||
if err := push.Collectors(
|
||||
"db_backup", push.HostnameGroupingKey(),
|
||||
"http://pushgateway:9091",
|
||||
completionTime,
|
||||
); err != nil {
|
||||
if err := push.New("http://pushgateway:9091", "db_backup").
|
||||
Collector(completionTime).
|
||||
Grouping("db", "customers").
|
||||
Push(); err != nil {
|
||||
fmt.Println("Could not push completion time to Pushgateway:", err)
|
||||
}
|
||||
}
|
||||
|
||||
248
vendor/github.com/prometheus/client_golang/prometheus/push/push.go
сгенерированный
поставляемый
248
vendor/github.com/prometheus/client_golang/prometheus/push/push.go
сгенерированный
поставляемый
@@ -11,20 +11,27 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
// Package push provides functions to push metrics to a Pushgateway. It uses a
|
||||
// builder approach. Create a Pusher with New and then add the various options
|
||||
// by using its methods, finally calling Add or Push, like this:
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
// Package push provides functions to push metrics to a Pushgateway. The metrics
|
||||
// to push are either collected from a provided registry, or from explicitly
|
||||
// listed collectors.
|
||||
// // Easy case:
|
||||
// push.New("http://example.org/metrics", "my_job").Gatherer(myRegistry).Push()
|
||||
//
|
||||
// See the documentation of the Pushgateway to understand the meaning of the
|
||||
// grouping parameters and the differences between push.Registry and
|
||||
// push.Collectors on the one hand and push.AddRegistry and push.AddCollectors
|
||||
// on the other hand: https://github.com/prometheus/pushgateway
|
||||
// // Complex case:
|
||||
// push.New("http://example.org/metrics", "my_job").
|
||||
// Collector(myCollector1).
|
||||
// Collector(myCollector2).
|
||||
// Grouping("zone", "xy").
|
||||
// Client(&myHTTPClient).
|
||||
// BasicAuth("top", "secret").
|
||||
// Add()
|
||||
//
|
||||
// See the examples section for more detailed examples.
|
||||
//
|
||||
// See the documentation of the Pushgateway to understand the meaning of
|
||||
// the grouping key and the differences between Push and Add:
|
||||
// https://github.com/prometheus/pushgateway
|
||||
package push
|
||||
|
||||
import (
|
||||
@@ -33,7 +40,6 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
@@ -44,57 +50,149 @@ import (
|
||||
|
||||
const contentTypeHeader = "Content-Type"
|
||||
|
||||
// FromGatherer triggers a metric collection by the provided Gatherer (which is
|
||||
// usually implemented by a prometheus.Registry) and pushes all gathered metrics
|
||||
// to the Pushgateway specified by url, using the provided job name and the
|
||||
// (optional) further grouping labels (the grouping map may be nil). See the
|
||||
// Pushgateway documentation for detailed implications of the job and other
|
||||
// grouping labels. Neither the job name nor any grouping label value may
|
||||
// contain a "/". The metrics pushed must not contain a job label of their own
|
||||
// nor any of the grouping labels.
|
||||
//
|
||||
// You can use just host:port or ip:port as url, in which case 'http://' is
|
||||
// added automatically. You can also include the schema in the URL. However, do
|
||||
// not include the '/metrics/jobs/...' part.
|
||||
//
|
||||
// Note that all previously pushed metrics with the same job and other grouping
|
||||
// labels will be replaced with the metrics pushed by this call. (It uses HTTP
|
||||
// method 'PUT' to push to the Pushgateway.)
|
||||
func FromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
|
||||
return push(job, grouping, url, g, "PUT")
|
||||
// Pusher manages a push to the Pushgateway. Use New to create one, configure it
|
||||
// with its methods, and finally use the Add or Push method to push.
|
||||
type Pusher struct {
|
||||
error error
|
||||
|
||||
url, job string
|
||||
grouping map[string]string
|
||||
|
||||
gatherers prometheus.Gatherers
|
||||
registerer prometheus.Registerer
|
||||
|
||||
client *http.Client
|
||||
useBasicAuth bool
|
||||
username, password string
|
||||
}
|
||||
|
||||
// AddFromGatherer works like FromGatherer, but only previously pushed metrics
|
||||
// with the same name (and the same job and other grouping labels) will be
|
||||
// replaced. (It uses HTTP method 'POST' to push to the Pushgateway.)
|
||||
func AddFromGatherer(job string, grouping map[string]string, url string, g prometheus.Gatherer) error {
|
||||
return push(job, grouping, url, g, "POST")
|
||||
}
|
||||
|
||||
func push(job string, grouping map[string]string, pushURL string, g prometheus.Gatherer, method string) error {
|
||||
if !strings.Contains(pushURL, "://") {
|
||||
pushURL = "http://" + pushURL
|
||||
// New creates a new Pusher to push to the provided URL withe 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.
|
||||
//
|
||||
// Note that until https://github.com/prometheus/pushgateway/issues/97 is
|
||||
// resolved, a “/” character in the job name is prohibited.
|
||||
func New(url, job string) *Pusher {
|
||||
var (
|
||||
reg = prometheus.NewRegistry()
|
||||
err error
|
||||
)
|
||||
if !strings.Contains(url, "://") {
|
||||
url = "http://" + url
|
||||
}
|
||||
if strings.HasSuffix(pushURL, "/") {
|
||||
pushURL = pushURL[:len(pushURL)-1]
|
||||
if strings.HasSuffix(url, "/") {
|
||||
url = url[:len(url)-1]
|
||||
}
|
||||
|
||||
if strings.Contains(job, "/") {
|
||||
return fmt.Errorf("job contains '/': %s", job)
|
||||
err = fmt.Errorf("job contains '/': %s", job)
|
||||
}
|
||||
urlComponents := []string{url.QueryEscape(job)}
|
||||
for ln, lv := range grouping {
|
||||
if !model.LabelName(ln).IsValid() {
|
||||
return fmt.Errorf("grouping label has invalid name: %s", ln)
|
||||
|
||||
return &Pusher{
|
||||
error: err,
|
||||
url: url,
|
||||
job: job,
|
||||
grouping: map[string]string{},
|
||||
gatherers: prometheus.Gatherers{reg},
|
||||
registerer: reg,
|
||||
client: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
// Push collects/gathers all metrics from all Collectors and Gatherers added to
|
||||
// this Pusher. Then, it pushes them to the Pushgateway configured while
|
||||
// creating this Pusher, using the configured job name and any added grouping
|
||||
// labels as grouping key. All previously pushed metrics with the same job and
|
||||
// other grouping labels will be replaced with the metrics pushed by this
|
||||
// call. (It uses HTTP method “PUT” to push to the Pushgateway.)
|
||||
//
|
||||
// Push returns the first error encountered by any method call (including this
|
||||
// one) in the lifetime of the Pusher.
|
||||
func (p *Pusher) Push() error {
|
||||
return p.push("PUT")
|
||||
}
|
||||
|
||||
// Add works like push, but only previously pushed metrics with the same name
|
||||
// (and the same job and other grouping labels) will be replaced. (It uses HTTP
|
||||
// method “POST” to push to the Pushgateway.)
|
||||
func (p *Pusher) Add() error {
|
||||
return p.push("POST")
|
||||
}
|
||||
|
||||
// Gatherer adds a Gatherer to the Pusher, from which metrics will be gathered
|
||||
// to push them to the Pushgateway. The gathered metrics must not contain a job
|
||||
// label of their own.
|
||||
//
|
||||
// For convenience, this method returns a pointer to the Pusher itself.
|
||||
func (p *Pusher) Gatherer(g prometheus.Gatherer) *Pusher {
|
||||
p.gatherers = append(p.gatherers, g)
|
||||
return p
|
||||
}
|
||||
|
||||
// Collector adds a Collector to the Pusher, from which metrics will be
|
||||
// collected to push them to the Pushgateway. The collected metrics must not
|
||||
// contain a job label of their own.
|
||||
//
|
||||
// For convenience, this method returns a pointer to the Pusher itself.
|
||||
func (p *Pusher) Collector(c prometheus.Collector) *Pusher {
|
||||
if p.error == nil {
|
||||
p.error = p.registerer.Register(c)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Grouping adds a label pair to the grouping key of the Pusher, replacing any
|
||||
// previously added label pair with the same label name. Note that setting any
|
||||
// labels in the grouping key that are already contained in the metrics to push
|
||||
// will lead to an error.
|
||||
//
|
||||
// For convenience, this method returns a pointer to the Pusher itself.
|
||||
//
|
||||
// Note that until https://github.com/prometheus/pushgateway/issues/97 is
|
||||
// resolved, this method does not allow a “/” character in the label value.
|
||||
func (p *Pusher) Grouping(name, value string) *Pusher {
|
||||
if p.error == nil {
|
||||
if !model.LabelName(name).IsValid() {
|
||||
p.error = fmt.Errorf("grouping label has invalid name: %s", name)
|
||||
return p
|
||||
}
|
||||
if strings.Contains(lv, "/") {
|
||||
return fmt.Errorf("value of grouping label %s contains '/': %s", ln, lv)
|
||||
if strings.Contains(value, "/") {
|
||||
p.error = fmt.Errorf("value of grouping label %s contains '/': %s", name, value)
|
||||
return p
|
||||
}
|
||||
p.grouping[name] = value
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Client sets a custom HTTP client for the Pusher. For convenience, this method
|
||||
// returns a pointer to the Pusher itself.
|
||||
func (p *Pusher) Client(c *http.Client) *Pusher {
|
||||
p.client = c
|
||||
return p
|
||||
}
|
||||
|
||||
// BasicAuth configures the Pusher to use HTTP Basic Authentication with the
|
||||
// provided username and password. For convenience, this method returns a
|
||||
// pointer to the Pusher itself.
|
||||
func (p *Pusher) BasicAuth(username, password string) *Pusher {
|
||||
p.useBasicAuth = true
|
||||
p.username = username
|
||||
p.password = password
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Pusher) push(method string) error {
|
||||
if p.error != nil {
|
||||
return p.error
|
||||
}
|
||||
urlComponents := []string{url.QueryEscape(p.job)}
|
||||
for ln, lv := range p.grouping {
|
||||
urlComponents = append(urlComponents, ln, lv)
|
||||
}
|
||||
pushURL = fmt.Sprintf("%s/metrics/job/%s", pushURL, strings.Join(urlComponents, "/"))
|
||||
pushURL := fmt.Sprintf("%s/metrics/job/%s", p.url, strings.Join(urlComponents, "/"))
|
||||
|
||||
mfs, err := g.Gather()
|
||||
mfs, err := p.gatherers.Gather()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -107,7 +205,7 @@ func push(job string, grouping map[string]string, pushURL string, g prometheus.G
|
||||
if l.GetName() == "job" {
|
||||
return fmt.Errorf("pushed metric %s (%s) already contains a job label", mf.GetName(), m)
|
||||
}
|
||||
if _, ok := grouping[l.GetName()]; ok {
|
||||
if _, ok := p.grouping[l.GetName()]; ok {
|
||||
return fmt.Errorf(
|
||||
"pushed metric %s (%s) already contains grouping label %s",
|
||||
mf.GetName(), m, l.GetName(),
|
||||
@@ -121,8 +219,11 @@ func push(job string, grouping map[string]string, pushURL string, g prometheus.G
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.useBasicAuth {
|
||||
req.SetBasicAuth(p.username, p.password)
|
||||
}
|
||||
req.Header.Set(contentTypeHeader, string(expfmt.FmtProtoDelim))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -133,40 +234,3 @@ func push(job string, grouping map[string]string, pushURL string, g prometheus.G
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collectors works like FromGatherer, but it does not use a Gatherer. Instead,
|
||||
// it collects from the provided collectors directly. It is a convenient way to
|
||||
// push only a few metrics.
|
||||
func Collectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
|
||||
return pushCollectors(job, grouping, url, "PUT", collectors...)
|
||||
}
|
||||
|
||||
// AddCollectors works like AddFromGatherer, but it does not use a Gatherer.
|
||||
// Instead, it collects from the provided collectors directly. It is a
|
||||
// convenient way to push only a few metrics.
|
||||
func AddCollectors(job string, grouping map[string]string, url string, collectors ...prometheus.Collector) error {
|
||||
return pushCollectors(job, grouping, url, "POST", collectors...)
|
||||
}
|
||||
|
||||
func pushCollectors(job string, grouping map[string]string, url, method string, collectors ...prometheus.Collector) error {
|
||||
r := prometheus.NewRegistry()
|
||||
for _, collector := range collectors {
|
||||
if err := r.Register(collector); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return push(job, grouping, url, r, method)
|
||||
}
|
||||
|
||||
// HostnameGroupingKey returns a label map with the only entry
|
||||
// {instance="<hostname>"}. This can be conveniently used as the grouping
|
||||
// parameter if metrics should be pushed with the hostname as label. The
|
||||
// returned map is created upon each call so that the caller is free to add more
|
||||
// labels to the map.
|
||||
func HostnameGroupingKey() map[string]string {
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return map[string]string{"instance": "unknown"}
|
||||
}
|
||||
return map[string]string{"instance": hostname}
|
||||
}
|
||||
|
||||
98
vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
сгенерированный
поставляемый
98
vendor/github.com/prometheus/client_golang/prometheus/push/push_test.go
сгенерированный
поставляемый
@@ -11,12 +11,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Copyright (c) 2013, The Prometheus Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found
|
||||
// in the LICENSE file.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
@@ -24,7 +18,6 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
@@ -40,11 +33,6 @@ func TestPush(t *testing.T) {
|
||||
lastPath string
|
||||
)
|
||||
|
||||
host, err := os.Hostname()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Fake a Pushgateway that always responds with 202.
|
||||
pgwOK := httptest.NewServer(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -98,26 +86,15 @@ func TestPush(t *testing.T) {
|
||||
}
|
||||
wantBody := buf.Bytes()
|
||||
|
||||
// PushCollectors, all good.
|
||||
if err := Collectors("testjob", HostnameGroupingKey(), pgwOK.URL, metric1, metric2); err != nil {
|
||||
// Push some Collectors, all good.
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "PUT" {
|
||||
t.Error("want method PUT for PushCollectors, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
if lastPath != "/metrics/job/testjob/instance/"+host {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// PushAddCollectors, with nil grouping, all good.
|
||||
if err := AddCollectors("testjob", nil, pgwOK.URL, metric1, metric2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "POST" {
|
||||
t.Error("want method POST for PushAddCollectors, got", lastMethod)
|
||||
t.Error("want method PUT for Push, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
@@ -126,8 +103,28 @@ func TestPush(t *testing.T) {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// PushCollectors with a broken PGW.
|
||||
if err := Collectors("testjob", nil, pgwErr.URL, metric1, metric2); err == nil {
|
||||
// Add some Collectors, with nil grouping, all good.
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Add(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "POST" {
|
||||
t.Error("want method POST for Add, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
if lastPath != "/metrics/job/testjob" {
|
||||
t.Error("unexpected path:", lastPath)
|
||||
}
|
||||
|
||||
// Push some Collectors with a broken PGW.
|
||||
if err := New(pgwErr.URL, "testjob").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err == nil {
|
||||
t.Error("push to broken Pushgateway succeeded")
|
||||
} else {
|
||||
if got, want := err.Error(), "unexpected status code 500 while pushing to "+pgwErr.URL+"/metrics/job/testjob: fake error\n"; got != want {
|
||||
@@ -135,22 +132,39 @@ func TestPush(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// PushCollectors with invalid grouping or job.
|
||||
if err := Collectors("testjob", map[string]string{"foo": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
|
||||
// Push some Collectors with invalid grouping or job.
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Grouping("foo", "bums").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err == nil {
|
||||
t.Error("push with grouping contained in metrics succeeded")
|
||||
}
|
||||
if err := Collectors("test/job", nil, pgwErr.URL, metric1, metric2); err == nil {
|
||||
if err := New(pgwOK.URL, "test/job").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err == nil {
|
||||
t.Error("push with invalid job value succeeded")
|
||||
}
|
||||
if err := Collectors("testjob", map[string]string{"foo/bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Grouping("foobar", "bu/ms").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err == nil {
|
||||
t.Error("push with invalid grouping succeeded")
|
||||
}
|
||||
if err := Collectors("testjob", map[string]string{"foo-bar": "bums"}, pgwErr.URL, metric1, metric2); err == nil {
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Grouping("foo-bar", "bums").
|
||||
Collector(metric1).
|
||||
Collector(metric2).
|
||||
Push(); err == nil {
|
||||
t.Error("push with invalid grouping succeeded")
|
||||
}
|
||||
|
||||
// Push registry, all good.
|
||||
if err := FromGatherer("testjob", HostnameGroupingKey(), pgwOK.URL, reg); err != nil {
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Gatherer(reg).
|
||||
Push(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "PUT" {
|
||||
@@ -160,12 +174,16 @@ func TestPush(t *testing.T) {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
}
|
||||
|
||||
// PushAdd registry, all good.
|
||||
if err := AddFromGatherer("testjob", map[string]string{"a": "x", "b": "y"}, pgwOK.URL, reg); err != nil {
|
||||
// Add registry, all good.
|
||||
if err := New(pgwOK.URL, "testjob").
|
||||
Grouping("a", "x").
|
||||
Grouping("b", "y").
|
||||
Gatherer(reg).
|
||||
Add(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lastMethod != "POST" {
|
||||
t.Error("want method POSTT for PushAdd, got", lastMethod)
|
||||
t.Error("want method POST for Add, got", lastMethod)
|
||||
}
|
||||
if bytes.Compare(lastBody, wantBody) != 0 {
|
||||
t.Errorf("got body %v, want %v", lastBody, wantBody)
|
||||
|
||||
303
vendor/github.com/prometheus/client_golang/prometheus/registry.go
сгенерированный
поставляемый
303
vendor/github.com/prometheus/client_golang/prometheus/registry.go
сгенерированный
поставляемый
@@ -18,6 +18,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
@@ -36,13 +37,13 @@ const (
|
||||
// DefaultRegisterer and DefaultGatherer are the implementations of the
|
||||
// Registerer and Gatherer interface a number of convenience functions in this
|
||||
// package act on. Initially, both variables point to the same Registry, which
|
||||
// has a process collector (see NewProcessCollector) and a Go collector (see
|
||||
// NewGoCollector) already registered. This approach to keep default instances
|
||||
// as global state mirrors the approach of other packages in the Go standard
|
||||
// library. Note that there are caveats. Change the variables with caution and
|
||||
// only if you understand the consequences. Users who want to avoid global state
|
||||
// altogether should not use the convenience function and act on custom
|
||||
// instances instead.
|
||||
// has a process collector (currently on Linux only, see NewProcessCollector)
|
||||
// and a Go collector (see NewGoCollector) already registered. This approach to
|
||||
// keep default instances as global state mirrors the approach of other packages
|
||||
// in the Go standard library. Note that there are caveats. Change the variables
|
||||
// with caution and only if you understand the consequences. Users who want to
|
||||
// avoid global state altogether should not use the convenience functions and
|
||||
// act on custom instances instead.
|
||||
var (
|
||||
defaultRegistry = NewRegistry()
|
||||
DefaultRegisterer Registerer = defaultRegistry
|
||||
@@ -202,6 +203,13 @@ func (errs MultiError) Error() string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Append appends the provided error if it is not nil.
|
||||
func (errs *MultiError) Append(err error) {
|
||||
if err != nil {
|
||||
*errs = append(*errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
// MaybeUnwrap returns nil if len(errs) is 0. It returns the first and only
|
||||
// contained error as error if len(errs is 1). In all other cases, it returns
|
||||
// the MultiError directly. This is helpful for returning a MultiError in a way
|
||||
@@ -368,22 +376,12 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||
)
|
||||
|
||||
r.mtx.RLock()
|
||||
goroutineBudget := len(r.collectorsByID)
|
||||
metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
|
||||
|
||||
// Scatter.
|
||||
// (Collectors could be complex and slow, so we call them all at once.)
|
||||
wg.Add(len(r.collectorsByID))
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(metricChan)
|
||||
}()
|
||||
collectors := make(chan Collector, len(r.collectorsByID))
|
||||
for _, collector := range r.collectorsByID {
|
||||
go func(collector Collector) {
|
||||
defer wg.Done()
|
||||
collector.Collect(metricChan)
|
||||
}(collector)
|
||||
collectors <- collector
|
||||
}
|
||||
|
||||
// In case pedantic checks are enabled, we have to copy the map before
|
||||
// giving up the RLock.
|
||||
if r.pedanticChecksEnabled {
|
||||
@@ -392,129 +390,176 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) {
|
||||
registeredDescIDs[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
r.mtx.RUnlock()
|
||||
|
||||
wg.Add(goroutineBudget)
|
||||
|
||||
collectWorker := func() {
|
||||
for {
|
||||
select {
|
||||
case collector := <-collectors:
|
||||
collector.Collect(metricChan)
|
||||
wg.Done()
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the first worker now to make sure at least one is running.
|
||||
go collectWorker()
|
||||
goroutineBudget--
|
||||
|
||||
// Close the metricChan once all collectors are collected.
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(metricChan)
|
||||
}()
|
||||
|
||||
// Drain metricChan in case of premature return.
|
||||
defer func() {
|
||||
for range metricChan {
|
||||
}
|
||||
}()
|
||||
|
||||
// Gather.
|
||||
for metric := range metricChan {
|
||||
// This could be done concurrently, too, but it required locking
|
||||
// of metricFamiliesByName (and of metricHashes if checks are
|
||||
// enabled). Most likely not worth it.
|
||||
desc := metric.Desc()
|
||||
dtoMetric := &dto.Metric{}
|
||||
if err := metric.Write(dtoMetric); err != nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"error collecting metric %v: %s", desc, err,
|
||||
collectLoop:
|
||||
for {
|
||||
select {
|
||||
case metric, ok := <-metricChan:
|
||||
if !ok {
|
||||
// metricChan is closed, we are done.
|
||||
break collectLoop
|
||||
}
|
||||
errs.Append(processMetric(
|
||||
metric, metricFamiliesByName,
|
||||
metricHashes, dimHashes,
|
||||
registeredDescIDs,
|
||||
))
|
||||
continue
|
||||
default:
|
||||
if goroutineBudget <= 0 || len(collectors) == 0 {
|
||||
// All collectors are aleady being worked on or
|
||||
// we have already as many goroutines started as
|
||||
// there are collectors. Just process metrics
|
||||
// from now on.
|
||||
for metric := range metricChan {
|
||||
errs.Append(processMetric(
|
||||
metric, metricFamiliesByName,
|
||||
metricHashes, dimHashes,
|
||||
registeredDescIDs,
|
||||
))
|
||||
}
|
||||
break collectLoop
|
||||
}
|
||||
// Start more workers.
|
||||
go collectWorker()
|
||||
goroutineBudget--
|
||||
runtime.Gosched()
|
||||
}
|
||||
metricFamily, ok := metricFamiliesByName[desc.fqName]
|
||||
if ok {
|
||||
if metricFamily.GetHelp() != desc.help {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s has help %q but should have %q",
|
||||
desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
|
||||
))
|
||||
continue
|
||||
}
|
||||
// TODO(beorn7): Simplify switch once Desc has type.
|
||||
switch metricFamily.GetType() {
|
||||
case dto.MetricType_COUNTER:
|
||||
if dtoMetric.Counter == nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s should be a Counter",
|
||||
desc.fqName, dtoMetric,
|
||||
))
|
||||
continue
|
||||
}
|
||||
case dto.MetricType_GAUGE:
|
||||
if dtoMetric.Gauge == nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s should be a Gauge",
|
||||
desc.fqName, dtoMetric,
|
||||
))
|
||||
continue
|
||||
}
|
||||
case dto.MetricType_SUMMARY:
|
||||
if dtoMetric.Summary == nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s should be a Summary",
|
||||
desc.fqName, dtoMetric,
|
||||
))
|
||||
continue
|
||||
}
|
||||
case dto.MetricType_UNTYPED:
|
||||
if dtoMetric.Untyped == nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s should be Untyped",
|
||||
desc.fqName, dtoMetric,
|
||||
))
|
||||
continue
|
||||
}
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
if dtoMetric.Histogram == nil {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s should be a Histogram",
|
||||
desc.fqName, dtoMetric,
|
||||
))
|
||||
continue
|
||||
}
|
||||
default:
|
||||
panic("encountered MetricFamily with invalid type")
|
||||
}
|
||||
} else {
|
||||
metricFamily = &dto.MetricFamily{}
|
||||
metricFamily.Name = proto.String(desc.fqName)
|
||||
metricFamily.Help = proto.String(desc.help)
|
||||
// TODO(beorn7): Simplify switch once Desc has type.
|
||||
switch {
|
||||
case dtoMetric.Gauge != nil:
|
||||
metricFamily.Type = dto.MetricType_GAUGE.Enum()
|
||||
case dtoMetric.Counter != nil:
|
||||
metricFamily.Type = dto.MetricType_COUNTER.Enum()
|
||||
case dtoMetric.Summary != nil:
|
||||
metricFamily.Type = dto.MetricType_SUMMARY.Enum()
|
||||
case dtoMetric.Untyped != nil:
|
||||
metricFamily.Type = dto.MetricType_UNTYPED.Enum()
|
||||
case dtoMetric.Histogram != nil:
|
||||
metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
|
||||
default:
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"empty metric collected: %s", dtoMetric,
|
||||
))
|
||||
continue
|
||||
}
|
||||
metricFamiliesByName[desc.fqName] = metricFamily
|
||||
}
|
||||
if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
if r.pedanticChecksEnabled {
|
||||
// Is the desc registered at all?
|
||||
if _, exist := registeredDescIDs[desc.id]; !exist {
|
||||
errs = append(errs, fmt.Errorf(
|
||||
"collected metric %s %s with unregistered descriptor %s",
|
||||
metricFamily.GetName(), dtoMetric, desc,
|
||||
))
|
||||
continue
|
||||
}
|
||||
if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
|
||||
}
|
||||
return normalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap()
|
||||
}
|
||||
|
||||
// processMetric is an internal helper method only used by the Gather method.
|
||||
func processMetric(
|
||||
metric Metric,
|
||||
metricFamiliesByName map[string]*dto.MetricFamily,
|
||||
metricHashes map[uint64]struct{},
|
||||
dimHashes map[string]uint64,
|
||||
registeredDescIDs map[uint64]struct{},
|
||||
) error {
|
||||
desc := metric.Desc()
|
||||
dtoMetric := &dto.Metric{}
|
||||
if err := metric.Write(dtoMetric); err != nil {
|
||||
return fmt.Errorf("error collecting metric %v: %s", desc, err)
|
||||
}
|
||||
metricFamily, ok := metricFamiliesByName[desc.fqName]
|
||||
if ok {
|
||||
if metricFamily.GetHelp() != desc.help {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s has help %q but should have %q",
|
||||
desc.fqName, dtoMetric, desc.help, metricFamily.GetHelp(),
|
||||
)
|
||||
}
|
||||
// TODO(beorn7): Simplify switch once Desc has type.
|
||||
switch metricFamily.GetType() {
|
||||
case dto.MetricType_COUNTER:
|
||||
if dtoMetric.Counter == nil {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s should be a Counter",
|
||||
desc.fqName, dtoMetric,
|
||||
)
|
||||
}
|
||||
case dto.MetricType_GAUGE:
|
||||
if dtoMetric.Gauge == nil {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s should be a Gauge",
|
||||
desc.fqName, dtoMetric,
|
||||
)
|
||||
}
|
||||
case dto.MetricType_SUMMARY:
|
||||
if dtoMetric.Summary == nil {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s should be a Summary",
|
||||
desc.fqName, dtoMetric,
|
||||
)
|
||||
}
|
||||
case dto.MetricType_UNTYPED:
|
||||
if dtoMetric.Untyped == nil {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s should be Untyped",
|
||||
desc.fqName, dtoMetric,
|
||||
)
|
||||
}
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
if dtoMetric.Histogram == nil {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s should be a Histogram",
|
||||
desc.fqName, dtoMetric,
|
||||
)
|
||||
}
|
||||
default:
|
||||
panic("encountered MetricFamily with invalid type")
|
||||
}
|
||||
} else {
|
||||
metricFamily = &dto.MetricFamily{}
|
||||
metricFamily.Name = proto.String(desc.fqName)
|
||||
metricFamily.Help = proto.String(desc.help)
|
||||
// TODO(beorn7): Simplify switch once Desc has type.
|
||||
switch {
|
||||
case dtoMetric.Gauge != nil:
|
||||
metricFamily.Type = dto.MetricType_GAUGE.Enum()
|
||||
case dtoMetric.Counter != nil:
|
||||
metricFamily.Type = dto.MetricType_COUNTER.Enum()
|
||||
case dtoMetric.Summary != nil:
|
||||
metricFamily.Type = dto.MetricType_SUMMARY.Enum()
|
||||
case dtoMetric.Untyped != nil:
|
||||
metricFamily.Type = dto.MetricType_UNTYPED.Enum()
|
||||
case dtoMetric.Histogram != nil:
|
||||
metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
|
||||
default:
|
||||
return fmt.Errorf("empty metric collected: %s", dtoMetric)
|
||||
}
|
||||
metricFamiliesByName[desc.fqName] = metricFamily
|
||||
}
|
||||
if err := checkMetricConsistency(metricFamily, dtoMetric, metricHashes, dimHashes); err != nil {
|
||||
return err
|
||||
}
|
||||
if registeredDescIDs != nil {
|
||||
// Is the desc registered at all?
|
||||
if _, exist := registeredDescIDs[desc.id]; !exist {
|
||||
return fmt.Errorf(
|
||||
"collected metric %s %s with unregistered descriptor %s",
|
||||
metricFamily.GetName(), dtoMetric, desc,
|
||||
)
|
||||
}
|
||||
if err := checkDescConsistency(metricFamily, dtoMetric, desc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gatherers is a slice of Gatherer instances that implements the Gatherer
|
||||
// interface itself. Its Gather method calls Gather on all Gatherers in the
|
||||
// slice in order and returns the merged results. Errors returned from the
|
||||
|
||||
4
vendor/github.com/prometheus/procfs/fs.go
сгенерированный
поставляемый
4
vendor/github.com/prometheus/procfs/fs.go
сгенерированный
поставляемый
@@ -46,8 +46,8 @@ func (fs FS) XFSStats() (*xfs.Stats, error) {
|
||||
return xfs.ParseStats(f)
|
||||
}
|
||||
|
||||
// NFSdClientRPCStats retrieves NFS daemon RPC statistics.
|
||||
func (fs FS) NFSdClientRPCStats() (*nfs.ClientRPCStats, error) {
|
||||
// NFSClientRPCStats retrieves NFS client RPC statistics.
|
||||
func (fs FS) NFSClientRPCStats() (*nfs.ClientRPCStats, error) {
|
||||
f, err := os.Open(fs.Path("net/rpc/nfs"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
13
vendor/github.com/prometheus/procfs/nfs/parse.go
сгенерированный
поставляемый
13
vendor/github.com/prometheus/procfs/nfs/parse.go
сгенерированный
поставляемый
@@ -178,8 +178,17 @@ func parseV3Stats(v []uint64) (V3Stats, error) {
|
||||
|
||||
func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
|
||||
values := int(v[0])
|
||||
if len(v[1:]) != values || values < 59 {
|
||||
return ClientV4Stats{}, fmt.Errorf("invalid V4Stats line %q", v)
|
||||
if len(v[1:]) != values {
|
||||
return ClientV4Stats{}, fmt.Errorf("invalid ClientV4Stats line %q", v)
|
||||
}
|
||||
|
||||
// This function currently supports mapping 59 NFS v4 client stats. Older
|
||||
// kernels may emit fewer stats, so we must detect this and pad out the
|
||||
// values to match the expected slice size.
|
||||
if values < 59 {
|
||||
newValues := make([]uint64, 60)
|
||||
copy(newValues, v)
|
||||
v = newValues
|
||||
}
|
||||
|
||||
return ClientV4Stats{
|
||||
|
||||
10
vendor/github.com/prometheus/procfs/nfs/parse_nfs.go
сгенерированный
поставляемый
10
vendor/github.com/prometheus/procfs/nfs/parse_nfs.go
сгенерированный
поставляемый
@@ -32,12 +32,12 @@ func ParseClientRPCStats(r io.Reader) (*ClientRPCStats, error) {
|
||||
parts := strings.Fields(scanner.Text())
|
||||
// require at least <key> <value>
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf("invalid NFSd metric line %q", line)
|
||||
return nil, fmt.Errorf("invalid NFS metric line %q", line)
|
||||
}
|
||||
|
||||
values, err := util.ParseUint64s(parts[1:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing NFSd metric line: %s", err)
|
||||
return nil, fmt.Errorf("error parsing NFS metric line: %s", err)
|
||||
}
|
||||
|
||||
switch metricLine := parts[0]; metricLine {
|
||||
@@ -52,15 +52,15 @@ func ParseClientRPCStats(r io.Reader) (*ClientRPCStats, error) {
|
||||
case "proc4":
|
||||
stats.ClientV4Stats, err = parseClientV4Stats(values)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown NFSd metric line %q", metricLine)
|
||||
return nil, fmt.Errorf("unknown NFS metric line %q", metricLine)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("errors parsing NFSd metric line: %s", err)
|
||||
return nil, fmt.Errorf("errors parsing NFS metric line: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error scanning NFSd file: %s", err)
|
||||
return nil, fmt.Errorf("error scanning NFS file: %s", err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
|
||||
127
vendor/github.com/prometheus/procfs/nfs/parse_nfs_test.go
сгенерированный
поставляемый
127
vendor/github.com/prometheus/procfs/nfs/parse_nfs_test.go
сгенерированный
поставляемый
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/prometheus/procfs/nfs"
|
||||
)
|
||||
|
||||
func TestNewNFSdClientRPCStats(t *testing.T) {
|
||||
func TestNewNFSClientRPCStats(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
content string
|
||||
@@ -32,6 +32,131 @@ func TestNewNFSdClientRPCStats(t *testing.T) {
|
||||
name: "invalid file",
|
||||
content: "invalid",
|
||||
invalid: true,
|
||||
}, {
|
||||
name: "good old kernel version file",
|
||||
content: `net 70 70 69 45
|
||||
rpc 1218785755 374636 1218815394
|
||||
proc2 18 16 57 74 52 71 73 45 86 0 52 83 61 17 53 50 23 70 82
|
||||
proc3 22 0 1061909262 48906 4077635 117661341 5 29391916 2570425 2993289 590 0 0 7815 15 1130 0 3983 92385 13332 2 1 23729
|
||||
proc4 48 98 51 54 83 85 23 24 1 28 73 68 83 12 84 39 68 59 58 88 29 74 69 96 21 84 15 53 86 54 66 56 97 36 49 32 85 81 11 58 32 67 13 28 35 90 1 26 1337
|
||||
`,
|
||||
stats: &nfs.ClientRPCStats{
|
||||
Network: nfs.Network{
|
||||
NetCount: 70,
|
||||
UDPCount: 70,
|
||||
TCPCount: 69,
|
||||
TCPConnect: 45,
|
||||
},
|
||||
ClientRPC: nfs.ClientRPC{
|
||||
RPCCount: 1218785755,
|
||||
Retransmissions: 374636,
|
||||
AuthRefreshes: 1218815394,
|
||||
},
|
||||
V2Stats: nfs.V2Stats{
|
||||
Null: 16,
|
||||
GetAttr: 57,
|
||||
SetAttr: 74,
|
||||
Root: 52,
|
||||
Lookup: 71,
|
||||
ReadLink: 73,
|
||||
Read: 45,
|
||||
WrCache: 86,
|
||||
Write: 0,
|
||||
Create: 52,
|
||||
Remove: 83,
|
||||
Rename: 61,
|
||||
Link: 17,
|
||||
SymLink: 53,
|
||||
MkDir: 50,
|
||||
RmDir: 23,
|
||||
ReadDir: 70,
|
||||
FsStat: 82,
|
||||
},
|
||||
V3Stats: nfs.V3Stats{
|
||||
Null: 0,
|
||||
GetAttr: 1061909262,
|
||||
SetAttr: 48906,
|
||||
Lookup: 4077635,
|
||||
Access: 117661341,
|
||||
ReadLink: 5,
|
||||
Read: 29391916,
|
||||
Write: 2570425,
|
||||
Create: 2993289,
|
||||
MkDir: 590,
|
||||
SymLink: 0,
|
||||
MkNod: 0,
|
||||
Remove: 7815,
|
||||
RmDir: 15,
|
||||
Rename: 1130,
|
||||
Link: 0,
|
||||
ReadDir: 3983,
|
||||
ReadDirPlus: 92385,
|
||||
FsStat: 13332,
|
||||
FsInfo: 2,
|
||||
PathConf: 1,
|
||||
Commit: 23729, },
|
||||
ClientV4Stats: nfs.ClientV4Stats{
|
||||
Null: 98,
|
||||
Read: 51,
|
||||
Write: 54,
|
||||
Commit: 83,
|
||||
Open: 85,
|
||||
OpenConfirm: 23,
|
||||
OpenNoattr: 24,
|
||||
OpenDowngrade: 1,
|
||||
Close: 28,
|
||||
Setattr: 73,
|
||||
FsInfo: 68,
|
||||
Renew: 83,
|
||||
SetClientId: 12,
|
||||
SetClientIdConfirm: 84,
|
||||
Lock: 39,
|
||||
Lockt: 68,
|
||||
Locku: 59,
|
||||
Access: 58,
|
||||
Getattr: 88,
|
||||
Lookup: 29,
|
||||
LookupRoot: 74,
|
||||
Remove: 69,
|
||||
Rename: 96,
|
||||
Link: 21,
|
||||
Symlink: 84,
|
||||
Create: 15,
|
||||
Pathconf: 53,
|
||||
StatFs: 86,
|
||||
ReadLink: 54,
|
||||
ReadDir: 66,
|
||||
ServerCaps: 56,
|
||||
DelegReturn: 97,
|
||||
GetAcl: 36,
|
||||
SetAcl: 49,
|
||||
FsLocations: 32,
|
||||
ReleaseLockowner: 85,
|
||||
Secinfo: 81,
|
||||
FsidPresent: 11,
|
||||
ExchangeId: 58,
|
||||
CreateSession: 32,
|
||||
DestroySession: 67,
|
||||
Sequence: 13,
|
||||
GetLeaseTime: 28,
|
||||
ReclaimComplete: 35,
|
||||
LayoutGet: 90,
|
||||
GetDeviceInfo: 1,
|
||||
LayoutCommit: 26,
|
||||
LayoutReturn: 1337,
|
||||
SecinfoNoName: 0,
|
||||
TestStateId: 0,
|
||||
FreeStateId: 0,
|
||||
GetDeviceList: 0,
|
||||
BindConnToSession: 0,
|
||||
DestroyClientId: 0,
|
||||
Seek: 0,
|
||||
Allocate: 0,
|
||||
DeAllocate: 0,
|
||||
LayoutStats: 0,
|
||||
Clone: 0,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "good file",
|
||||
content: `net 18628 0 18628 6
|
||||
|
||||
Ссылка в новой задаче
Block a user