Enhancements in Minio Prometheus exporter (#5848)
Standardized Minio collectors based on Prometheus recommendations.master
parent
bb34bd91f1
commit
deb685c5b5
@ -0,0 +1,199 @@ |
|||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"bufio" |
||||||
|
"io" |
||||||
|
"net" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
closeNotifier = 1 << iota |
||||||
|
flusher |
||||||
|
hijacker |
||||||
|
readerFrom |
||||||
|
pusher |
||||||
|
) |
||||||
|
|
||||||
|
type delegator interface { |
||||||
|
http.ResponseWriter |
||||||
|
|
||||||
|
Status() int |
||||||
|
Written() int64 |
||||||
|
} |
||||||
|
|
||||||
|
type responseWriterDelegator struct { |
||||||
|
http.ResponseWriter |
||||||
|
|
||||||
|
handler, method string |
||||||
|
status int |
||||||
|
written int64 |
||||||
|
wroteHeader bool |
||||||
|
observeWriteHeader func(int) |
||||||
|
} |
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Status() int { |
||||||
|
return r.status |
||||||
|
} |
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Written() int64 { |
||||||
|
return r.written |
||||||
|
} |
||||||
|
|
||||||
|
func (r *responseWriterDelegator) WriteHeader(code int) { |
||||||
|
r.status = code |
||||||
|
r.wroteHeader = true |
||||||
|
r.ResponseWriter.WriteHeader(code) |
||||||
|
if r.observeWriteHeader != nil { |
||||||
|
r.observeWriteHeader(code) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (r *responseWriterDelegator) Write(b []byte) (int, error) { |
||||||
|
if !r.wroteHeader { |
||||||
|
r.WriteHeader(http.StatusOK) |
||||||
|
} |
||||||
|
n, err := r.ResponseWriter.Write(b) |
||||||
|
r.written += int64(n) |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
type closeNotifierDelegator struct{ *responseWriterDelegator } |
||||||
|
type flusherDelegator struct{ *responseWriterDelegator } |
||||||
|
type hijackerDelegator struct{ *responseWriterDelegator } |
||||||
|
type readerFromDelegator struct{ *responseWriterDelegator } |
||||||
|
|
||||||
|
func (d *closeNotifierDelegator) CloseNotify() <-chan bool { |
||||||
|
return d.ResponseWriter.(http.CloseNotifier).CloseNotify() |
||||||
|
} |
||||||
|
func (d *flusherDelegator) Flush() { |
||||||
|
d.ResponseWriter.(http.Flusher).Flush() |
||||||
|
} |
||||||
|
func (d *hijackerDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
||||||
|
return d.ResponseWriter.(http.Hijacker).Hijack() |
||||||
|
} |
||||||
|
func (d *readerFromDelegator) ReadFrom(re io.Reader) (int64, error) { |
||||||
|
if !d.wroteHeader { |
||||||
|
d.WriteHeader(http.StatusOK) |
||||||
|
} |
||||||
|
n, err := d.ResponseWriter.(io.ReaderFrom).ReadFrom(re) |
||||||
|
d.written += n |
||||||
|
return n, err |
||||||
|
} |
||||||
|
|
||||||
|
var pickDelegator = make([]func(*responseWriterDelegator) delegator, 32) |
||||||
|
|
||||||
|
func init() { |
||||||
|
// TODO(beorn7): Code generation would help here.
|
||||||
|
pickDelegator[0] = func(d *responseWriterDelegator) delegator { // 0
|
||||||
|
return d |
||||||
|
} |
||||||
|
pickDelegator[closeNotifier] = func(d *responseWriterDelegator) delegator { // 1
|
||||||
|
return &closeNotifierDelegator{d} |
||||||
|
} |
||||||
|
pickDelegator[flusher] = func(d *responseWriterDelegator) delegator { // 2
|
||||||
|
return &flusherDelegator{d} |
||||||
|
} |
||||||
|
pickDelegator[flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 3
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[hijacker] = func(d *responseWriterDelegator) delegator { // 4
|
||||||
|
return &hijackerDelegator{d} |
||||||
|
} |
||||||
|
pickDelegator[hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 5
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Hijacker |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &hijackerDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 6
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
}{d, &hijackerDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 7
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom] = func(d *responseWriterDelegator) delegator { // 8
|
||||||
|
return readerFromDelegator{d} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 9
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &readerFromDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 10
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.Flusher |
||||||
|
}{d, &readerFromDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 11
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &readerFromDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 12
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 13
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 14
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 15
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
} |
181
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
generated
vendored
181
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_1_8.go
generated
vendored
@ -0,0 +1,181 @@ |
|||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
type pusherDelegator struct{ *responseWriterDelegator } |
||||||
|
|
||||||
|
func (d *pusherDelegator) Push(target string, opts *http.PushOptions) error { |
||||||
|
return d.ResponseWriter.(http.Pusher).Push(target, opts) |
||||||
|
} |
||||||
|
|
||||||
|
func init() { |
||||||
|
pickDelegator[pusher] = func(d *responseWriterDelegator) delegator { // 16
|
||||||
|
return &pusherDelegator{d} |
||||||
|
} |
||||||
|
pickDelegator[pusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 17
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+flusher] = func(d *responseWriterDelegator) delegator { // 18
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.Flusher |
||||||
|
}{d, &pusherDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 19
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+hijacker] = func(d *responseWriterDelegator) delegator { // 20
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.Hijacker |
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 21
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.Hijacker |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 22
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { //23
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom] = func(d *responseWriterDelegator) delegator { // 24
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+closeNotifier] = func(d *responseWriterDelegator) delegator { // 25
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+flusher] = func(d *responseWriterDelegator) delegator { // 26
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.Flusher |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 27
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+hijacker] = func(d *responseWriterDelegator) delegator { // 28
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+hijacker+closeNotifier] = func(d *responseWriterDelegator) delegator { // 29
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+hijacker+flusher] = func(d *responseWriterDelegator) delegator { // 30
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}} |
||||||
|
} |
||||||
|
pickDelegator[pusher+readerFrom+hijacker+flusher+closeNotifier] = func(d *responseWriterDelegator) delegator { // 31
|
||||||
|
return struct { |
||||||
|
*responseWriterDelegator |
||||||
|
http.Pusher |
||||||
|
io.ReaderFrom |
||||||
|
http.Hijacker |
||||||
|
http.Flusher |
||||||
|
http.CloseNotifier |
||||||
|
}{d, &pusherDelegator{d}, &readerFromDelegator{d}, &hijackerDelegator{d}, &flusherDelegator{d}, &closeNotifierDelegator{d}} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { |
||||||
|
d := &responseWriterDelegator{ |
||||||
|
ResponseWriter: w, |
||||||
|
observeWriteHeader: observeWriteHeaderFunc, |
||||||
|
} |
||||||
|
|
||||||
|
id := 0 |
||||||
|
if _, ok := w.(http.CloseNotifier); ok { |
||||||
|
id += closeNotifier |
||||||
|
} |
||||||
|
if _, ok := w.(http.Flusher); ok { |
||||||
|
id += flusher |
||||||
|
} |
||||||
|
if _, ok := w.(http.Hijacker); ok { |
||||||
|
id += hijacker |
||||||
|
} |
||||||
|
if _, ok := w.(io.ReaderFrom); ok { |
||||||
|
id += readerFrom |
||||||
|
} |
||||||
|
if _, ok := w.(http.Pusher); ok { |
||||||
|
id += pusher |
||||||
|
} |
||||||
|
|
||||||
|
return pickDelegator[id](d) |
||||||
|
} |
44
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
generated
vendored
44
vendor/github.com/prometheus/client_golang/prometheus/promhttp/delegator_pre_1_8.go
generated
vendored
@ -0,0 +1,44 @@ |
|||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build !go1.8
|
||||||
|
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
func newDelegator(w http.ResponseWriter, observeWriteHeaderFunc func(int)) delegator { |
||||||
|
d := &responseWriterDelegator{ |
||||||
|
ResponseWriter: w, |
||||||
|
observeWriteHeader: observeWriteHeaderFunc, |
||||||
|
} |
||||||
|
|
||||||
|
id := 0 |
||||||
|
if _, ok := w.(http.CloseNotifier); ok { |
||||||
|
id += closeNotifier |
||||||
|
} |
||||||
|
if _, ok := w.(http.Flusher); ok { |
||||||
|
id += flusher |
||||||
|
} |
||||||
|
if _, ok := w.(http.Hijacker); ok { |
||||||
|
id += hijacker |
||||||
|
} |
||||||
|
if _, ok := w.(io.ReaderFrom); ok { |
||||||
|
id += readerFrom |
||||||
|
} |
||||||
|
|
||||||
|
return pickDelegator[id](d) |
||||||
|
} |
@ -0,0 +1,311 @@ |
|||||||
|
// Copyright 2016 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package promhttp provides tooling around HTTP servers and clients.
|
||||||
|
//
|
||||||
|
// First, the package allows the creation of http.Handler instances to expose
|
||||||
|
// Prometheus metrics via HTTP. promhttp.Handler acts on the
|
||||||
|
// prometheus.DefaultGatherer. With HandlerFor, you can create a handler for a
|
||||||
|
// custom registry or anything that implements the Gatherer interface. It also
|
||||||
|
// allows the creation of handlers that act differently on errors or allow to
|
||||||
|
// log errors.
|
||||||
|
//
|
||||||
|
// Second, the package provides tooling to instrument instances of http.Handler
|
||||||
|
// via middleware. Middleware wrappers follow the naming scheme
|
||||||
|
// InstrumentHandlerX, where X describes the intended use of the middleware.
|
||||||
|
// See each function's doc comment for specific details.
|
||||||
|
//
|
||||||
|
// Finally, the package allows for an http.RoundTripper to be instrumented via
|
||||||
|
// middleware. Middleware wrappers follow the naming scheme
|
||||||
|
// InstrumentRoundTripperX, where X describes the intended use of the
|
||||||
|
// middleware. See each function's doc comment for specific details.
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"compress/gzip" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"net/http" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/prometheus/common/expfmt" |
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
contentTypeHeader = "Content-Type" |
||||||
|
contentLengthHeader = "Content-Length" |
||||||
|
contentEncodingHeader = "Content-Encoding" |
||||||
|
acceptEncodingHeader = "Accept-Encoding" |
||||||
|
) |
||||||
|
|
||||||
|
var bufPool sync.Pool |
||||||
|
|
||||||
|
func getBuf() *bytes.Buffer { |
||||||
|
buf := bufPool.Get() |
||||||
|
if buf == nil { |
||||||
|
return &bytes.Buffer{} |
||||||
|
} |
||||||
|
return buf.(*bytes.Buffer) |
||||||
|
} |
||||||
|
|
||||||
|
func giveBuf(buf *bytes.Buffer) { |
||||||
|
buf.Reset() |
||||||
|
bufPool.Put(buf) |
||||||
|
} |
||||||
|
|
||||||
|
// Handler returns an http.Handler for the prometheus.DefaultGatherer, 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.
|
||||||
|
//
|
||||||
|
// 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 InstrumentMetricHandler( |
||||||
|
prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, 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 { |
||||||
|
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 { |
||||||
|
opts.ErrorLog.Println("error gathering metrics:", err) |
||||||
|
} |
||||||
|
switch opts.ErrorHandling { |
||||||
|
case PanicOnError: |
||||||
|
panic(err) |
||||||
|
case ContinueOnError: |
||||||
|
if len(mfs) == 0 { |
||||||
|
http.Error(w, "No metrics gathered, last error:\n\n"+err.Error(), http.StatusInternalServerError) |
||||||
|
return |
||||||
|
} |
||||||
|
case HTTPErrorOnError: |
||||||
|
http.Error(w, "An error has occurred during metrics gathering:\n\n"+err.Error(), http.StatusInternalServerError) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contentType := expfmt.Negotiate(req.Header) |
||||||
|
buf := getBuf() |
||||||
|
defer giveBuf(buf) |
||||||
|
writer, encoding := decorateWriter(req, buf, opts.DisableCompression) |
||||||
|
enc := expfmt.NewEncoder(writer, contentType) |
||||||
|
var lastErr error |
||||||
|
for _, mf := range mfs { |
||||||
|
if err := enc.Encode(mf); err != nil { |
||||||
|
lastErr = err |
||||||
|
if opts.ErrorLog != nil { |
||||||
|
opts.ErrorLog.Println("error encoding metric family:", err) |
||||||
|
} |
||||||
|
switch opts.ErrorHandling { |
||||||
|
case PanicOnError: |
||||||
|
panic(err) |
||||||
|
case ContinueOnError: |
||||||
|
// Handled later.
|
||||||
|
case HTTPErrorOnError: |
||||||
|
http.Error(w, "An error has occurred during metrics encoding:\n\n"+err.Error(), http.StatusInternalServerError) |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if closer, ok := writer.(io.Closer); ok { |
||||||
|
closer.Close() |
||||||
|
} |
||||||
|
if lastErr != nil && buf.Len() == 0 { |
||||||
|
http.Error(w, "No metrics encoded, last error:\n\n"+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 { |
||||||
|
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
|
||||||
|
// errors.
|
||||||
|
type HandlerErrorHandling int |
||||||
|
|
||||||
|
// These constants cause handlers serving metrics to behave as described if
|
||||||
|
// errors are encountered.
|
||||||
|
const ( |
||||||
|
// Serve an HTTP status code 500 upon the first error
|
||||||
|
// encountered. Report the error message in the body.
|
||||||
|
HTTPErrorOnError HandlerErrorHandling = iota |
||||||
|
// Ignore errors and try to serve as many metrics as possible. However,
|
||||||
|
// if no metrics can be served, serve an HTTP status code 500 and the
|
||||||
|
// last error message in the body. Only use this in deliberate "best
|
||||||
|
// effort" metrics collection scenarios. It is recommended to at least
|
||||||
|
// log errors (by providing an ErrorLog in HandlerOpts) to not mask
|
||||||
|
// errors completely.
|
||||||
|
ContinueOnError |
||||||
|
// Panic upon the first error encountered (useful for "crash only" apps).
|
||||||
|
PanicOnError |
||||||
|
) |
||||||
|
|
||||||
|
// Logger is the minimal interface HandlerOpts needs for logging. Note that
|
||||||
|
// log.Logger from the standard library implements this interface, and it is
|
||||||
|
// easy to implement by custom loggers, if they don't do so already anyway.
|
||||||
|
type Logger interface { |
||||||
|
Println(v ...interface{}) |
||||||
|
} |
||||||
|
|
||||||
|
// HandlerOpts specifies options how to serve metrics via an http.Handler. The
|
||||||
|
// zero value of HandlerOpts is a reasonable default.
|
||||||
|
type HandlerOpts struct { |
||||||
|
// ErrorLog specifies an optional logger for errors collecting and
|
||||||
|
// serving metrics. If nil, errors are not logged at all.
|
||||||
|
ErrorLog Logger |
||||||
|
// ErrorHandling defines how errors are handled. Note that errors are
|
||||||
|
// logged regardless of the configured ErrorHandling provided ErrorLog
|
||||||
|
// is not nil.
|
||||||
|
ErrorHandling HandlerErrorHandling |
||||||
|
// If DisableCompression is true, the handler will never compress the
|
||||||
|
// response, even if requested by the client.
|
||||||
|
DisableCompression bool |
||||||
|
// 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
|
||||||
|
// returns the decorated writer and the appropriate "Content-Encoding" header
|
||||||
|
// (which is empty if no compression is enabled).
|
||||||
|
func decorateWriter(request *http.Request, writer io.Writer, compressionDisabled bool) (io.Writer, string) { |
||||||
|
if compressionDisabled { |
||||||
|
return writer, "" |
||||||
|
} |
||||||
|
header := request.Header.Get(acceptEncodingHeader) |
||||||
|
parts := strings.Split(header, ",") |
||||||
|
for _, part := range parts { |
||||||
|
part = strings.TrimSpace(part) |
||||||
|
if part == "gzip" || strings.HasPrefix(part, "gzip;") { |
||||||
|
return gzip.NewWriter(writer), "gzip" |
||||||
|
} |
||||||
|
} |
||||||
|
return writer, "" |
||||||
|
} |
97
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
generated
vendored
97
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go
generated
vendored
@ -0,0 +1,97 @@ |
|||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus" |
||||||
|
) |
||||||
|
|
||||||
|
// The RoundTripperFunc type is an adapter to allow the use of ordinary
|
||||||
|
// functions as RoundTrippers. If f is a function with the appropriate
|
||||||
|
// signature, RountTripperFunc(f) is a RoundTripper that calls f.
|
||||||
|
type RoundTripperFunc func(req *http.Request) (*http.Response, error) |
||||||
|
|
||||||
|
// RoundTrip implements the RoundTripper interface.
|
||||||
|
func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { |
||||||
|
return rt(r) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentRoundTripperInFlight is a middleware that wraps the provided
|
||||||
|
// http.RoundTripper. It sets the provided prometheus.Gauge to the number of
|
||||||
|
// requests currently handled by the wrapped http.RoundTripper.
|
||||||
|
//
|
||||||
|
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||||
|
func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripper) RoundTripperFunc { |
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { |
||||||
|
gauge.Inc() |
||||||
|
defer gauge.Dec() |
||||||
|
return next.RoundTrip(r) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentRoundTripperCounter is a middleware that wraps the provided
|
||||||
|
// http.RoundTripper to observe the request result with the provided CounterVec.
|
||||||
|
// The CounterVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. Partitioning of the CounterVec happens by HTTP status code
|
||||||
|
// and/or HTTP method if the respective instance label names are present in the
|
||||||
|
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||||
|
//
|
||||||
|
// If the wrapped RoundTripper panics or returns a non-nil error, the Counter
|
||||||
|
// is not incremented.
|
||||||
|
//
|
||||||
|
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||||
|
func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper) RoundTripperFunc { |
||||||
|
code, method := checkLabels(counter) |
||||||
|
|
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { |
||||||
|
resp, err := next.RoundTrip(r) |
||||||
|
if err == nil { |
||||||
|
counter.With(labels(code, method, r.Method, resp.StatusCode)).Inc() |
||||||
|
} |
||||||
|
return resp, err |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentRoundTripperDuration is a middleware that wraps the provided
|
||||||
|
// http.RoundTripper to observe the request duration with the provided
|
||||||
|
// ObserverVec. The ObserverVec must have zero, one, or two non-const
|
||||||
|
// non-curried labels. For those, the only allowed label names are "code" and
|
||||||
|
// "method". The function panics otherwise. The Observe method of the Observer
|
||||||
|
// in the ObserverVec is called with the request duration in
|
||||||
|
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||||
|
// respective instance label names are present in the ObserverVec. For
|
||||||
|
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||||
|
// partitioning of Histograms is expensive and should be used judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped RoundTripper panics or returns a non-nil error, no values are
|
||||||
|
// reported.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundTripper) RoundTripperFunc { |
||||||
|
code, method := checkLabels(obs) |
||||||
|
|
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { |
||||||
|
start := time.Now() |
||||||
|
resp, err := next.RoundTrip(r) |
||||||
|
if err == nil { |
||||||
|
obs.With(labels(code, method, r.Method, resp.StatusCode)).Observe(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
return resp, err |
||||||
|
}) |
||||||
|
} |
144
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
generated
vendored
144
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client_1_8.go
generated
vendored
@ -0,0 +1,144 @@ |
|||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"crypto/tls" |
||||||
|
"net/http" |
||||||
|
"net/http/httptrace" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
// InstrumentTrace is used to offer flexibility in instrumenting the available
|
||||||
|
// httptrace.ClientTrace hook functions. Each function is passed a float64
|
||||||
|
// representing the time in seconds since the start of the http request. A user
|
||||||
|
// may choose to use separately buckets Histograms, or implement custom
|
||||||
|
// instance labels on a per function basis.
|
||||||
|
type InstrumentTrace struct { |
||||||
|
GotConn func(float64) |
||||||
|
PutIdleConn func(float64) |
||||||
|
GotFirstResponseByte func(float64) |
||||||
|
Got100Continue func(float64) |
||||||
|
DNSStart func(float64) |
||||||
|
DNSDone func(float64) |
||||||
|
ConnectStart func(float64) |
||||||
|
ConnectDone func(float64) |
||||||
|
TLSHandshakeStart func(float64) |
||||||
|
TLSHandshakeDone func(float64) |
||||||
|
WroteHeaders func(float64) |
||||||
|
Wait100Continue func(float64) |
||||||
|
WroteRequest func(float64) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentRoundTripperTrace is a middleware that wraps the provided
|
||||||
|
// RoundTripper and reports times to hook functions provided in the
|
||||||
|
// InstrumentTrace struct. Hook functions that are not present in the provided
|
||||||
|
// InstrumentTrace struct are ignored. Times reported to the hook functions are
|
||||||
|
// time since the start of the request. Only with Go1.9+, those times are
|
||||||
|
// guaranteed to never be negative. (Earlier Go versions are not using a
|
||||||
|
// monotonic clock.) Note that partitioning of Histograms is expensive and
|
||||||
|
// should be used judiciously.
|
||||||
|
//
|
||||||
|
// For hook functions that receive an error as an argument, no observations are
|
||||||
|
// made in the event of a non-nil error value.
|
||||||
|
//
|
||||||
|
// See the example for ExampleInstrumentRoundTripperDuration for example usage.
|
||||||
|
func InstrumentRoundTripperTrace(it *InstrumentTrace, next http.RoundTripper) RoundTripperFunc { |
||||||
|
return RoundTripperFunc(func(r *http.Request) (*http.Response, error) { |
||||||
|
start := time.Now() |
||||||
|
|
||||||
|
trace := &httptrace.ClientTrace{ |
||||||
|
GotConn: func(_ httptrace.GotConnInfo) { |
||||||
|
if it.GotConn != nil { |
||||||
|
it.GotConn(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
PutIdleConn: func(err error) { |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
if it.PutIdleConn != nil { |
||||||
|
it.PutIdleConn(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
DNSStart: func(_ httptrace.DNSStartInfo) { |
||||||
|
if it.DNSStart != nil { |
||||||
|
it.DNSStart(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
DNSDone: func(_ httptrace.DNSDoneInfo) { |
||||||
|
if it.DNSDone != nil { |
||||||
|
it.DNSDone(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
ConnectStart: func(_, _ string) { |
||||||
|
if it.ConnectStart != nil { |
||||||
|
it.ConnectStart(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
ConnectDone: func(_, _ string, err error) { |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
if it.ConnectDone != nil { |
||||||
|
it.ConnectDone(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
GotFirstResponseByte: func() { |
||||||
|
if it.GotFirstResponseByte != nil { |
||||||
|
it.GotFirstResponseByte(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
Got100Continue: func() { |
||||||
|
if it.Got100Continue != nil { |
||||||
|
it.Got100Continue(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
TLSHandshakeStart: func() { |
||||||
|
if it.TLSHandshakeStart != nil { |
||||||
|
it.TLSHandshakeStart(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
TLSHandshakeDone: func(_ tls.ConnectionState, err error) { |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
if it.TLSHandshakeDone != nil { |
||||||
|
it.TLSHandshakeDone(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
WroteHeaders: func() { |
||||||
|
if it.WroteHeaders != nil { |
||||||
|
it.WroteHeaders(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
Wait100Continue: func() { |
||||||
|
if it.Wait100Continue != nil { |
||||||
|
it.Wait100Continue(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
WroteRequest: func(_ httptrace.WroteRequestInfo) { |
||||||
|
if it.WroteRequest != nil { |
||||||
|
it.WroteRequest(time.Since(start).Seconds()) |
||||||
|
} |
||||||
|
}, |
||||||
|
} |
||||||
|
r = r.WithContext(httptrace.WithClientTrace(context.Background(), trace)) |
||||||
|
|
||||||
|
return next.RoundTrip(r) |
||||||
|
}) |
||||||
|
} |
447
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
generated
vendored
447
vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go
generated
vendored
@ -0,0 +1,447 @@ |
|||||||
|
// Copyright 2017 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package promhttp |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"net/http" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
dto "github.com/prometheus/client_model/go" |
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus" |
||||||
|
) |
||||||
|
|
||||||
|
// magicString is used for the hacky label test in checkLabels. Remove once fixed.
|
||||||
|
const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" |
||||||
|
|
||||||
|
// InstrumentHandlerInFlight is a middleware that wraps the provided
|
||||||
|
// http.Handler. It sets the provided prometheus.Gauge to the number of
|
||||||
|
// requests currently handled by the wrapped http.Handler.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handler { |
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
g.Inc() |
||||||
|
defer g.Dec() |
||||||
|
next.ServeHTTP(w, r) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentHandlerDuration is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe the request duration with the provided ObserverVec.
|
||||||
|
// The ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||||
|
// called with the request duration in seconds. Partitioning happens by HTTP
|
||||||
|
// status code and/or HTTP method if the respective instance label names are
|
||||||
|
// present in the ObserverVec. For unpartitioned observations, use an
|
||||||
|
// ObserverVec with zero labels. Note that partitioning of Histograms is
|
||||||
|
// expensive and should be used judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, no values are reported.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { |
||||||
|
code, method := checkLabels(obs) |
||||||
|
|
||||||
|
if code { |
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
now := time.Now() |
||||||
|
d := newDelegator(w, nil) |
||||||
|
next.ServeHTTP(d, r) |
||||||
|
|
||||||
|
obs.With(labels(code, method, r.Method, d.Status())).Observe(time.Since(now).Seconds()) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
now := time.Now() |
||||||
|
next.ServeHTTP(w, r) |
||||||
|
obs.With(labels(code, method, r.Method, 0)).Observe(time.Since(now).Seconds()) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentHandlerCounter is a middleware that wraps the provided http.Handler
|
||||||
|
// to observe the request result with the provided CounterVec. The CounterVec
|
||||||
|
// must have zero, one, or two non-const non-curried labels. For those, the only
|
||||||
|
// allowed label names are "code" and "method". The function panics
|
||||||
|
// otherwise. Partitioning of the CounterVec happens by HTTP status code and/or
|
||||||
|
// HTTP method if the respective instance label names are present in the
|
||||||
|
// CounterVec. For unpartitioned counting, use a CounterVec with zero labels.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, the Counter is not incremented.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler) http.HandlerFunc { |
||||||
|
code, method := checkLabels(counter) |
||||||
|
|
||||||
|
if code { |
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
d := newDelegator(w, nil) |
||||||
|
next.ServeHTTP(d, r) |
||||||
|
counter.With(labels(code, method, r.Method, d.Status())).Inc() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
next.ServeHTTP(w, r) |
||||||
|
counter.With(labels(code, method, r.Method, 0)).Inc() |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentHandlerTimeToWriteHeader is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe with the provided ObserverVec the request duration
|
||||||
|
// until the response headers are written. The ObserverVec must have zero, one,
|
||||||
|
// or two non-const non-curried labels. For those, the only allowed label names
|
||||||
|
// are "code" and "method". The function panics otherwise. The Observe method of
|
||||||
|
// the Observer in the ObserverVec is called with the request duration in
|
||||||
|
// seconds. Partitioning happens by HTTP status code and/or HTTP method if the
|
||||||
|
// respective instance label names are present in the ObserverVec. For
|
||||||
|
// unpartitioned observations, use an ObserverVec with zero labels. Note that
|
||||||
|
// partitioning of Histograms is expensive and should be used judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics before calling WriteHeader, no value is
|
||||||
|
// reported.
|
||||||
|
//
|
||||||
|
// Note that this method is only guaranteed to never observe negative durations
|
||||||
|
// if used with Go1.9+.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { |
||||||
|
code, method := checkLabels(obs) |
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
now := time.Now() |
||||||
|
d := newDelegator(w, func(status int) { |
||||||
|
obs.With(labels(code, method, r.Method, status)).Observe(time.Since(now).Seconds()) |
||||||
|
}) |
||||||
|
next.ServeHTTP(d, r) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentHandlerRequestSize is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe the request size with the provided ObserverVec. The
|
||||||
|
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||||
|
// called with the request size in bytes. Partitioning happens by HTTP status
|
||||||
|
// code and/or HTTP method if the respective instance label names are present in
|
||||||
|
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||||
|
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||||
|
// judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, no values are reported.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler) http.HandlerFunc { |
||||||
|
code, method := checkLabels(obs) |
||||||
|
|
||||||
|
if code { |
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
d := newDelegator(w, nil) |
||||||
|
next.ServeHTTP(d, r) |
||||||
|
size := computeApproximateRequestSize(r) |
||||||
|
obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(size)) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
next.ServeHTTP(w, r) |
||||||
|
size := computeApproximateRequestSize(r) |
||||||
|
obs.With(labels(code, method, r.Method, 0)).Observe(float64(size)) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// InstrumentHandlerResponseSize is a middleware that wraps the provided
|
||||||
|
// http.Handler to observe the response size with the provided ObserverVec. The
|
||||||
|
// ObserverVec must have zero, one, or two non-const non-curried labels. For
|
||||||
|
// those, the only allowed label names are "code" and "method". The function
|
||||||
|
// panics otherwise. The Observe method of the Observer in the ObserverVec is
|
||||||
|
// called with the response size in bytes. Partitioning happens by HTTP status
|
||||||
|
// code and/or HTTP method if the respective instance label names are present in
|
||||||
|
// the ObserverVec. For unpartitioned observations, use an ObserverVec with zero
|
||||||
|
// labels. Note that partitioning of Histograms is expensive and should be used
|
||||||
|
// judiciously.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler does not set a status code, a status code of 200 is assumed.
|
||||||
|
//
|
||||||
|
// If the wrapped Handler panics, no values are reported.
|
||||||
|
//
|
||||||
|
// See the example for InstrumentHandlerDuration for example usage.
|
||||||
|
func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler) http.Handler { |
||||||
|
code, method := checkLabels(obs) |
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||||
|
d := newDelegator(w, nil) |
||||||
|
next.ServeHTTP(d, r) |
||||||
|
obs.With(labels(code, method, r.Method, d.Status())).Observe(float64(d.Written())) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func checkLabels(c prometheus.Collector) (code bool, method bool) { |
||||||
|
// TODO(beorn7): Remove this hacky way to check for instance labels
|
||||||
|
// once Descriptors can have their dimensionality queried.
|
||||||
|
var ( |
||||||
|
desc *prometheus.Desc |
||||||
|
m prometheus.Metric |
||||||
|
pm dto.Metric |
||||||
|
lvs []string |
||||||
|
) |
||||||
|
|
||||||
|
// Get the Desc from the Collector.
|
||||||
|
descc := make(chan *prometheus.Desc, 1) |
||||||
|
c.Describe(descc) |
||||||
|
|
||||||
|
select { |
||||||
|
case desc = <-descc: |
||||||
|
default: |
||||||
|
panic("no description provided by collector") |
||||||
|
} |
||||||
|
select { |
||||||
|
case <-descc: |
||||||
|
panic("more than one description provided by collector") |
||||||
|
default: |
||||||
|
} |
||||||
|
|
||||||
|
close(descc) |
||||||
|
|
||||||
|
// Create a ConstMetric with the Desc. Since we don't know how many
|
||||||
|
// variable labels there are, try for as long as it needs.
|
||||||
|
for err := errors.New("dummy"); err != nil; lvs = append(lvs, magicString) { |
||||||
|
m, err = prometheus.NewConstMetric(desc, prometheus.UntypedValue, 0, lvs...) |
||||||
|
} |
||||||
|
|
||||||
|
// Write out the metric into a proto message and look at the labels.
|
||||||
|
// If the value is not the magicString, it is a constLabel, which doesn't interest us.
|
||||||
|
// If the label is curried, it doesn't interest us.
|
||||||
|
// In all other cases, only "code" or "method" is allowed.
|
||||||
|
if err := m.Write(&pm); err != nil { |
||||||
|
panic("error checking metric for labels") |
||||||
|
} |
||||||
|
for _, label := range pm.Label { |
||||||
|
name, value := label.GetName(), label.GetValue() |
||||||
|
if value != magicString || isLabelCurried(c, name) { |
||||||
|
continue |
||||||
|
} |
||||||
|
switch name { |
||||||
|
case "code": |
||||||
|
code = true |
||||||
|
case "method": |
||||||
|
method = true |
||||||
|
default: |
||||||
|
panic("metric partitioned with non-supported labels") |
||||||
|
} |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func isLabelCurried(c prometheus.Collector, label string) bool { |
||||||
|
// This is even hackier than the label test above.
|
||||||
|
// We essentially try to curry again and see if it works.
|
||||||
|
// But for that, we need to type-convert to the two
|
||||||
|
// types we use here, ObserverVec or *CounterVec.
|
||||||
|
switch v := c.(type) { |
||||||
|
case *prometheus.CounterVec: |
||||||
|
if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
case prometheus.ObserverVec: |
||||||
|
if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
default: |
||||||
|
panic("unsupported metric vec type") |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// emptyLabels is a one-time allocation for non-partitioned metrics to avoid
|
||||||
|
// unnecessary allocations on each request.
|
||||||
|
var emptyLabels = prometheus.Labels{} |
||||||
|
|
||||||
|
func labels(code, method bool, reqMethod string, status int) prometheus.Labels { |
||||||
|
if !(code || method) { |
||||||
|
return emptyLabels |
||||||
|
} |
||||||
|
labels := prometheus.Labels{} |
||||||
|
|
||||||
|
if code { |
||||||
|
labels["code"] = sanitizeCode(status) |
||||||
|
} |
||||||
|
if method { |
||||||
|
labels["method"] = sanitizeMethod(reqMethod) |
||||||
|
} |
||||||
|
|
||||||
|
return labels |
||||||
|
} |
||||||
|
|
||||||
|
func computeApproximateRequestSize(r *http.Request) int { |
||||||
|
s := 0 |
||||||
|
if r.URL != nil { |
||||||
|
s += len(r.URL.String()) |
||||||
|
} |
||||||
|
|
||||||
|
s += len(r.Method) |
||||||
|
s += len(r.Proto) |
||||||
|
for name, values := range r.Header { |
||||||
|
s += len(name) |
||||||
|
for _, value := range values { |
||||||
|
s += len(value) |
||||||
|
} |
||||||
|
} |
||||||
|
s += len(r.Host) |
||||||
|
|
||||||
|
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
|
||||||
|
|
||||||
|
if r.ContentLength != -1 { |
||||||
|
s += int(r.ContentLength) |
||||||
|
} |
||||||
|
return s |
||||||
|
} |
||||||
|
|
||||||
|
func sanitizeMethod(m string) string { |
||||||
|
switch m { |
||||||
|
case "GET", "get": |
||||||
|
return "get" |
||||||
|
case "PUT", "put": |
||||||
|
return "put" |
||||||
|
case "HEAD", "head": |
||||||
|
return "head" |
||||||
|
case "POST", "post": |
||||||
|
return "post" |
||||||
|
case "DELETE", "delete": |
||||||
|
return "delete" |
||||||
|
case "CONNECT", "connect": |
||||||
|
return "connect" |
||||||
|
case "OPTIONS", "options": |
||||||
|
return "options" |
||||||
|
case "NOTIFY", "notify": |
||||||
|
return "notify" |
||||||
|
default: |
||||||
|
return strings.ToLower(m) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If the wrapped http.Handler has not set a status code, i.e. the value is
|
||||||
|
// currently 0, santizeCode will return 200, for consistency with behavior in
|
||||||
|
// the stdlib.
|
||||||
|
func sanitizeCode(s int) string { |
||||||
|
switch s { |
||||||
|
case 100: |
||||||
|
return "100" |
||||||
|
case 101: |
||||||
|
return "101" |
||||||
|
|
||||||
|
case 200, 0: |
||||||
|
return "200" |
||||||
|
case 201: |
||||||
|
return "201" |
||||||
|
case 202: |
||||||
|
return "202" |
||||||
|
case 203: |
||||||
|
return "203" |
||||||
|
case 204: |
||||||
|
return "204" |
||||||
|
case 205: |
||||||
|
return "205" |
||||||
|
case 206: |
||||||
|
return "206" |
||||||
|
|
||||||
|
case 300: |
||||||
|
return "300" |
||||||
|
case 301: |
||||||
|
return "301" |
||||||
|
case 302: |
||||||
|
return "302" |
||||||
|
case 304: |
||||||
|
return "304" |
||||||
|
case 305: |
||||||
|
return "305" |
||||||
|
case 307: |
||||||
|
return "307" |
||||||
|
|
||||||
|
case 400: |
||||||
|
return "400" |
||||||
|
case 401: |
||||||
|
return "401" |
||||||
|
case 402: |
||||||
|
return "402" |
||||||
|
case 403: |
||||||
|
return "403" |
||||||
|
case 404: |
||||||
|
return "404" |
||||||
|
case 405: |
||||||
|
return "405" |
||||||
|
case 406: |
||||||
|
return "406" |
||||||
|
case 407: |
||||||
|
return "407" |
||||||
|
case 408: |
||||||
|
return "408" |
||||||
|
case 409: |
||||||
|
return "409" |
||||||
|
case 410: |
||||||
|
return "410" |
||||||
|
case 411: |
||||||
|
return "411" |
||||||
|
case 412: |
||||||
|
return "412" |
||||||
|
case 413: |
||||||
|
return "413" |
||||||
|
case 414: |
||||||
|
return "414" |
||||||
|
case 415: |
||||||
|
return "415" |
||||||
|
case 416: |
||||||
|
return "416" |
||||||
|
case 417: |
||||||
|
return "417" |
||||||
|
case 418: |
||||||
|
return "418" |
||||||
|
|
||||||
|
case 500: |
||||||
|
return "500" |
||||||
|
case 501: |
||||||
|
return "501" |
||||||
|
case 502: |
||||||
|
return "502" |
||||||
|
case 503: |
||||||
|
return "503" |
||||||
|
case 504: |
||||||
|
return "504" |
||||||
|
case 505: |
||||||
|
return "505" |
||||||
|
|
||||||
|
case 428: |
||||||
|
return "428" |
||||||
|
case 429: |
||||||
|
return "429" |
||||||
|
case 431: |
||||||
|
return "431" |
||||||
|
case 511: |
||||||
|
return "511" |
||||||
|
|
||||||
|
default: |
||||||
|
return strconv.Itoa(s) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue