Update prometheus/client_golang to v1.0.0 (#5682)
Signed-off-by: beorn7 <beorn@grafana.com>pull/5693/head
parent
4cd81dfa5d
commit
372b3438e5
@ -0,0 +1,29 @@ |
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.12
|
||||
|
||||
package prometheus |
||||
|
||||
import "runtime/debug" |
||||
|
||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go 1.12+.
|
||||
func readBuildInfo() (path, version, sum string) { |
||||
path, version, sum = "unknown", "unknown", "unknown" |
||||
if bi, ok := debug.ReadBuildInfo(); ok { |
||||
path = bi.Main.Path |
||||
version = bi.Main.Version |
||||
sum = bi.Main.Sum |
||||
} |
||||
return |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !go1.12
|
||||
|
||||
package prometheus |
||||
|
||||
// readBuildInfo is a wrapper around debug.ReadBuildInfo for Go versions before
|
||||
// 1.12. Remove this whole file once the minimum supported Go version is 1.12.
|
||||
func readBuildInfo() (path, version, sum string) { |
||||
return "unknown", "unknown", "unknown" |
||||
} |
||||
@ -1,505 +0,0 @@ |
||||
// Copyright 2014 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus |
||||
|
||||
import ( |
||||
"bufio" |
||||
"compress/gzip" |
||||
"io" |
||||
"net" |
||||
"net/http" |
||||
"strconv" |
||||
"strings" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/prometheus/common/expfmt" |
||||
) |
||||
|
||||
// TODO(beorn7): Remove this whole file. It is a partial mirror of
|
||||
// promhttp/http.go (to avoid circular import chains) where everything HTTP
|
||||
// related should live. The functions here are just for avoiding
|
||||
// breakage. Everything is deprecated.
|
||||
|
||||
const ( |
||||
contentTypeHeader = "Content-Type" |
||||
contentEncodingHeader = "Content-Encoding" |
||||
acceptEncodingHeader = "Accept-Encoding" |
||||
) |
||||
|
||||
var gzipPool = sync.Pool{ |
||||
New: func() interface{} { |
||||
return gzip.NewWriter(nil) |
||||
}, |
||||
} |
||||
|
||||
// Handler returns an HTTP handler for the DefaultGatherer. It is
|
||||
// already instrumented with InstrumentHandler (using "prometheus" as handler
|
||||
// name).
|
||||
//
|
||||
// Deprecated: Please note the issues described in the doc comment of
|
||||
// InstrumentHandler. You might want to consider using promhttp.Handler instead.
|
||||
func Handler() http.Handler { |
||||
return InstrumentHandler("prometheus", UninstrumentedHandler()) |
||||
} |
||||
|
||||
// UninstrumentedHandler returns an HTTP handler for the DefaultGatherer.
|
||||
//
|
||||
// Deprecated: Use promhttp.HandlerFor(DefaultGatherer, promhttp.HandlerOpts{})
|
||||
// instead. See there for further documentation.
|
||||
func UninstrumentedHandler() http.Handler { |
||||
return http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) { |
||||
mfs, err := DefaultGatherer.Gather() |
||||
if err != nil { |
||||
httpError(rsp, err) |
||||
return |
||||
} |
||||
|
||||
contentType := expfmt.Negotiate(req.Header) |
||||
header := rsp.Header() |
||||
header.Set(contentTypeHeader, string(contentType)) |
||||
|
||||
w := io.Writer(rsp) |
||||
if gzipAccepted(req.Header) { |
||||
header.Set(contentEncodingHeader, "gzip") |
||||
gz := gzipPool.Get().(*gzip.Writer) |
||||
defer gzipPool.Put(gz) |
||||
|
||||
gz.Reset(w) |
||||
defer gz.Close() |
||||
|
||||
w = gz |
||||
} |
||||
|
||||
enc := expfmt.NewEncoder(w, contentType) |
||||
|
||||
for _, mf := range mfs { |
||||
if err := enc.Encode(mf); err != nil { |
||||
httpError(rsp, err) |
||||
return |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
|
||||
var instLabels = []string{"method", "code"} |
||||
|
||||
type nower interface { |
||||
Now() time.Time |
||||
} |
||||
|
||||
type nowFunc func() time.Time |
||||
|
||||
func (n nowFunc) Now() time.Time { |
||||
return n() |
||||
} |
||||
|
||||
var now nower = nowFunc(func() time.Time { |
||||
return time.Now() |
||||
}) |
||||
|
||||
// InstrumentHandler wraps the given HTTP handler for instrumentation. It
|
||||
// registers four metric collectors (if not already done) and reports HTTP
|
||||
// metrics to the (newly or already) registered collectors: http_requests_total
|
||||
// (CounterVec), http_request_duration_microseconds (Summary),
|
||||
// http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each
|
||||
// has a constant label named "handler" with the provided handlerName as
|
||||
// value. http_requests_total is a metric vector partitioned by HTTP method
|
||||
// (label name "method") and HTTP status code (label name "code").
|
||||
//
|
||||
// Deprecated: InstrumentHandler has several issues. Use the tooling provided in
|
||||
// package promhttp instead. The issues are the following: (1) It uses Summaries
|
||||
// rather than Histograms. Summaries are not useful if aggregation across
|
||||
// multiple instances is required. (2) It uses microseconds as unit, which is
|
||||
// deprecated and should be replaced by seconds. (3) The size of the request is
|
||||
// calculated in a separate goroutine. Since this calculator requires access to
|
||||
// the request header, it creates a race with any writes to the header performed
|
||||
// during request handling. httputil.ReverseProxy is a prominent example for a
|
||||
// handler performing such writes. (4) It has additional issues with HTTP/2, cf.
|
||||
// https://github.com/prometheus/client_golang/issues/272.
|
||||
func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc { |
||||
return InstrumentHandlerFunc(handlerName, handler.ServeHTTP) |
||||
} |
||||
|
||||
// InstrumentHandlerFunc wraps the given function for instrumentation. It
|
||||
// otherwise works in the same way as InstrumentHandler (and shares the same
|
||||
// issues).
|
||||
//
|
||||
// Deprecated: InstrumentHandlerFunc is deprecated for the same reasons as
|
||||
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||
func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { |
||||
return InstrumentHandlerFuncWithOpts( |
||||
SummaryOpts{ |
||||
Subsystem: "http", |
||||
ConstLabels: Labels{"handler": handlerName}, |
||||
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, |
||||
}, |
||||
handlerFunc, |
||||
) |
||||
} |
||||
|
||||
// InstrumentHandlerWithOpts works like InstrumentHandler (and shares the same
|
||||
// issues) but provides more flexibility (at the cost of a more complex call
|
||||
// syntax). As InstrumentHandler, this function registers four metric
|
||||
// collectors, but it uses the provided SummaryOpts to create them. However, the
|
||||
// fields "Name" and "Help" in the SummaryOpts are ignored. "Name" is replaced
|
||||
// by "requests_total", "request_duration_microseconds", "request_size_bytes",
|
||||
// and "response_size_bytes", respectively. "Help" is replaced by an appropriate
|
||||
// help string. The names of the variable labels of the http_requests_total
|
||||
// CounterVec are "method" (get, post, etc.), and "code" (HTTP status code).
|
||||
//
|
||||
// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
|
||||
// behavior of InstrumentHandler:
|
||||
//
|
||||
// prometheus.InstrumentHandlerWithOpts(
|
||||
// prometheus.SummaryOpts{
|
||||
// Subsystem: "http",
|
||||
// ConstLabels: prometheus.Labels{"handler": handlerName},
|
||||
// },
|
||||
// handler,
|
||||
// )
|
||||
//
|
||||
// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
|
||||
// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
|
||||
// and all its fields are set to the equally named fields in the provided
|
||||
// SummaryOpts.
|
||||
//
|
||||
// Deprecated: InstrumentHandlerWithOpts is deprecated for the same reasons as
|
||||
// InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||
func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc { |
||||
return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP) |
||||
} |
||||
|
||||
// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc (and shares
|
||||
// the same issues) but provides more flexibility (at the cost of a more complex
|
||||
// call syntax). See InstrumentHandlerWithOpts for details how the provided
|
||||
// SummaryOpts are used.
|
||||
//
|
||||
// Deprecated: InstrumentHandlerFuncWithOpts is deprecated for the same reasons
|
||||
// as InstrumentHandler is. Use the tooling provided in package promhttp instead.
|
||||
func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc { |
||||
reqCnt := NewCounterVec( |
||||
CounterOpts{ |
||||
Namespace: opts.Namespace, |
||||
Subsystem: opts.Subsystem, |
||||
Name: "requests_total", |
||||
Help: "Total number of HTTP requests made.", |
||||
ConstLabels: opts.ConstLabels, |
||||
}, |
||||
instLabels, |
||||
) |
||||
if err := Register(reqCnt); err != nil { |
||||
if are, ok := err.(AlreadyRegisteredError); ok { |
||||
reqCnt = are.ExistingCollector.(*CounterVec) |
||||
} else { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
opts.Name = "request_duration_microseconds" |
||||
opts.Help = "The HTTP request latencies in microseconds." |
||||
reqDur := NewSummary(opts) |
||||
if err := Register(reqDur); err != nil { |
||||
if are, ok := err.(AlreadyRegisteredError); ok { |
||||
reqDur = are.ExistingCollector.(Summary) |
||||
} else { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
opts.Name = "request_size_bytes" |
||||
opts.Help = "The HTTP request sizes in bytes." |
||||
reqSz := NewSummary(opts) |
||||
if err := Register(reqSz); err != nil { |
||||
if are, ok := err.(AlreadyRegisteredError); ok { |
||||
reqSz = are.ExistingCollector.(Summary) |
||||
} else { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
opts.Name = "response_size_bytes" |
||||
opts.Help = "The HTTP response sizes in bytes." |
||||
resSz := NewSummary(opts) |
||||
if err := Register(resSz); err != nil { |
||||
if are, ok := err.(AlreadyRegisteredError); ok { |
||||
resSz = are.ExistingCollector.(Summary) |
||||
} else { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||
now := time.Now() |
||||
|
||||
delegate := &responseWriterDelegator{ResponseWriter: w} |
||||
out := computeApproximateRequestSize(r) |
||||
|
||||
_, cn := w.(http.CloseNotifier) |
||||
_, fl := w.(http.Flusher) |
||||
_, hj := w.(http.Hijacker) |
||||
_, rf := w.(io.ReaderFrom) |
||||
var rw http.ResponseWriter |
||||
if cn && fl && hj && rf { |
||||
rw = &fancyResponseWriterDelegator{delegate} |
||||
} else { |
||||
rw = delegate |
||||
} |
||||
handlerFunc(rw, r) |
||||
|
||||
elapsed := float64(time.Since(now)) / float64(time.Microsecond) |
||||
|
||||
method := sanitizeMethod(r.Method) |
||||
code := sanitizeCode(delegate.status) |
||||
reqCnt.WithLabelValues(method, code).Inc() |
||||
reqDur.Observe(elapsed) |
||||
resSz.Observe(float64(delegate.written)) |
||||
reqSz.Observe(float64(<-out)) |
||||
}) |
||||
} |
||||
|
||||
func computeApproximateRequestSize(r *http.Request) <-chan int { |
||||
// Get URL length in current goroutine for avoiding a race condition.
|
||||
// HandlerFunc that runs in parallel may modify the URL.
|
||||
s := 0 |
||||
if r.URL != nil { |
||||
s += len(r.URL.String()) |
||||
} |
||||
|
||||
out := make(chan int, 1) |
||||
|
||||
go func() { |
||||
s += len(r.Method) |
||||
s += len(r.Proto) |
||||
for name, values := range r.Header { |
||||
s += len(name) |
||||
for _, value := range values { |
||||
s += len(value) |
||||
} |
||||
} |
||||
s += len(r.Host) |
||||
|
||||
// N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
|
||||
|
||||
if r.ContentLength != -1 { |
||||
s += int(r.ContentLength) |
||||
} |
||||
out <- s |
||||
close(out) |
||||
}() |
||||
|
||||
return out |
||||
} |
||||
|
||||
type responseWriterDelegator struct { |
||||
http.ResponseWriter |
||||
|
||||
status int |
||||
written int64 |
||||
wroteHeader bool |
||||
} |
||||
|
||||
func (r *responseWriterDelegator) WriteHeader(code int) { |
||||
r.status = code |
||||
r.wroteHeader = true |
||||
r.ResponseWriter.WriteHeader(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 fancyResponseWriterDelegator struct { |
||||
*responseWriterDelegator |
||||
} |
||||
|
||||
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool { |
||||
//lint:ignore SA1019 http.CloseNotifier is deprecated but we don't want to
|
||||
//remove support from client_golang yet.
|
||||
return f.ResponseWriter.(http.CloseNotifier).CloseNotify() |
||||
} |
||||
|
||||
func (f *fancyResponseWriterDelegator) Flush() { |
||||
f.ResponseWriter.(http.Flusher).Flush() |
||||
} |
||||
|
||||
func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { |
||||
return f.ResponseWriter.(http.Hijacker).Hijack() |
||||
} |
||||
|
||||
func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) { |
||||
if !f.wroteHeader { |
||||
f.WriteHeader(http.StatusOK) |
||||
} |
||||
n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r) |
||||
f.written += n |
||||
return n, err |
||||
} |
||||
|
||||
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) |
||||
} |
||||
} |
||||
|
||||
func sanitizeCode(s int) string { |
||||
switch s { |
||||
case 100: |
||||
return "100" |
||||
case 101: |
||||
return "101" |
||||
|
||||
case 200: |
||||
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) |
||||
} |
||||
} |
||||
|
||||
// gzipAccepted returns whether the client will accept gzip-encoded content.
|
||||
func gzipAccepted(header http.Header) bool { |
||||
a := header.Get(acceptEncodingHeader) |
||||
parts := strings.Split(a, ",") |
||||
for _, part := range parts { |
||||
part = strings.TrimSpace(part) |
||||
if part == "gzip" || strings.HasPrefix(part, "gzip;") { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
// httpError removes any content-encoding header and then calls http.Error with
|
||||
// the provided error and http.StatusInternalServerErrer. Error contents is
|
||||
// supposed to be uncompressed plain text. However, same as with a plain
|
||||
// http.Error, any header settings will be void if the header has already been
|
||||
// sent. The error message will still be written to the writer, but it will
|
||||
// probably be of limited use.
|
||||
func httpError(rsp http.ResponseWriter, err error) { |
||||
rsp.Header().Del(contentEncodingHeader) |
||||
http.Error( |
||||
rsp, |
||||
"An error has occurred while serving metrics:\n\n"+err.Error(), |
||||
http.StatusInternalServerError, |
||||
) |
||||
} |
||||
65
vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go
generated
vendored
65
vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go
generated
vendored
@ -0,0 +1,65 @@ |
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package prometheus |
||||
|
||||
import ( |
||||
"github.com/prometheus/procfs" |
||||
) |
||||
|
||||
func canCollectProcess() bool { |
||||
_, err := procfs.NewDefaultFS() |
||||
return err == nil |
||||
} |
||||
|
||||
func (c *processCollector) processCollect(ch chan<- Metric) { |
||||
pid, err := c.pidFn() |
||||
if err != nil { |
||||
c.reportError(ch, nil, err) |
||||
return |
||||
} |
||||
|
||||
p, err := procfs.NewProc(pid) |
||||
if err != nil { |
||||
c.reportError(ch, nil, err) |
||||
return |
||||
} |
||||
|
||||
if stat, err := p.Stat(); err == nil { |
||||
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, stat.CPUTime()) |
||||
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(stat.VirtualMemory())) |
||||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(stat.ResidentMemory())) |
||||
if startTime, err := stat.StartTime(); err == nil { |
||||
ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime) |
||||
} else { |
||||
c.reportError(ch, c.startTime, err) |
||||
} |
||||
} else { |
||||
c.reportError(ch, nil, err) |
||||
} |
||||
|
||||
if fds, err := p.FileDescriptorsLen(); err == nil { |
||||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(fds)) |
||||
} else { |
||||
c.reportError(ch, c.openFDs, err) |
||||
} |
||||
|
||||
if limits, err := p.Limits(); err == nil { |
||||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(limits.OpenFiles)) |
||||
ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(limits.AddressSpace)) |
||||
} else { |
||||
c.reportError(ch, nil, err) |
||||
} |
||||
} |
||||
112
vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go
generated
vendored
112
vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go
generated
vendored
@ -0,0 +1,112 @@ |
||||
// Copyright 2019 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package prometheus |
||||
|
||||
import ( |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
func canCollectProcess() bool { |
||||
return true |
||||
} |
||||
|
||||
var ( |
||||
modpsapi = syscall.NewLazyDLL("psapi.dll") |
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll") |
||||
|
||||
procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") |
||||
procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount") |
||||
) |
||||
|
||||
type processMemoryCounters struct { |
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/psapi/ns-psapi-_process_memory_counters_ex
|
||||
_ uint32 |
||||
PageFaultCount uint32 |
||||
PeakWorkingSetSize uint64 |
||||
WorkingSetSize uint64 |
||||
QuotaPeakPagedPoolUsage uint64 |
||||
QuotaPagedPoolUsage uint64 |
||||
QuotaPeakNonPagedPoolUsage uint64 |
||||
QuotaNonPagedPoolUsage uint64 |
||||
PagefileUsage uint64 |
||||
PeakPagefileUsage uint64 |
||||
PrivateUsage uint64 |
||||
} |
||||
|
||||
func getProcessMemoryInfo(handle windows.Handle) (processMemoryCounters, error) { |
||||
mem := processMemoryCounters{} |
||||
r1, _, err := procGetProcessMemoryInfo.Call( |
||||
uintptr(handle), |
||||
uintptr(unsafe.Pointer(&mem)), |
||||
uintptr(unsafe.Sizeof(mem)), |
||||
) |
||||
if r1 != 1 { |
||||
return mem, err |
||||
} else { |
||||
return mem, nil |
||||
} |
||||
} |
||||
|
||||
func getProcessHandleCount(handle windows.Handle) (uint32, error) { |
||||
var count uint32 |
||||
r1, _, err := procGetProcessHandleCount.Call( |
||||
uintptr(handle), |
||||
uintptr(unsafe.Pointer(&count)), |
||||
) |
||||
if r1 != 1 { |
||||
return 0, err |
||||
} else { |
||||
return count, nil |
||||
} |
||||
} |
||||
|
||||
func (c *processCollector) processCollect(ch chan<- Metric) { |
||||
h, err := windows.GetCurrentProcess() |
||||
if err != nil { |
||||
c.reportError(ch, nil, err) |
||||
return |
||||
} |
||||
|
||||
var startTime, exitTime, kernelTime, userTime windows.Filetime |
||||
err = windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) |
||||
if err != nil { |
||||
c.reportError(ch, nil, err) |
||||
return |
||||
} |
||||
ch <- MustNewConstMetric(c.startTime, GaugeValue, float64(startTime.Nanoseconds()/1e9)) |
||||
ch <- MustNewConstMetric(c.cpuTotal, CounterValue, fileTimeToSeconds(kernelTime)+fileTimeToSeconds(userTime)) |
||||
|
||||
mem, err := getProcessMemoryInfo(h) |
||||
if err != nil { |
||||
c.reportError(ch, nil, err) |
||||
return |
||||
} |
||||
ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(mem.PrivateUsage)) |
||||
ch <- MustNewConstMetric(c.rss, GaugeValue, float64(mem.WorkingSetSize)) |
||||
|
||||
handles, err := getProcessHandleCount(h) |
||||
if err != nil { |
||||
c.reportError(ch, nil, err) |
||||
return |
||||
} |
||||
ch <- MustNewConstMetric(c.openFDs, GaugeValue, float64(handles)) |
||||
ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(16*1024*1024)) // Windows has a hard-coded max limit, not per-process.
|
||||
} |
||||
|
||||
func fileTimeToSeconds(ft windows.Filetime) float64 { |
||||
return float64(uint64(ft.HighDateTime)<<32+uint64(ft.LowDateTime)) / 1e7 |
||||
} |
||||
@ -0,0 +1,275 @@ |
||||
// 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 procfs |
||||
|
||||
import ( |
||||
"bufio" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
// For the proc file format details,
|
||||
// see https://elixir.bootlin.com/linux/v4.17/source/net/unix/af_unix.c#L2815
|
||||
// and https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/net.h#L48.
|
||||
|
||||
const ( |
||||
netUnixKernelPtrIdx = iota |
||||
netUnixRefCountIdx |
||||
_ |
||||
netUnixFlagsIdx |
||||
netUnixTypeIdx |
||||
netUnixStateIdx |
||||
netUnixInodeIdx |
||||
|
||||
// Inode and Path are optional.
|
||||
netUnixStaticFieldsCnt = 6 |
||||
) |
||||
|
||||
const ( |
||||
netUnixTypeStream = 1 |
||||
netUnixTypeDgram = 2 |
||||
netUnixTypeSeqpacket = 5 |
||||
|
||||
netUnixFlagListen = 1 << 16 |
||||
|
||||
netUnixStateUnconnected = 1 |
||||
netUnixStateConnecting = 2 |
||||
netUnixStateConnected = 3 |
||||
netUnixStateDisconnected = 4 |
||||
) |
||||
|
||||
var errInvalidKernelPtrFmt = errors.New("Invalid Num(the kernel table slot number) format") |
||||
|
||||
// NetUnixType is the type of the type field.
|
||||
type NetUnixType uint64 |
||||
|
||||
// NetUnixFlags is the type of the flags field.
|
||||
type NetUnixFlags uint64 |
||||
|
||||
// NetUnixState is the type of the state field.
|
||||
type NetUnixState uint64 |
||||
|
||||
// NetUnixLine represents a line of /proc/net/unix.
|
||||
type NetUnixLine struct { |
||||
KernelPtr string |
||||
RefCount uint64 |
||||
Protocol uint64 |
||||
Flags NetUnixFlags |
||||
Type NetUnixType |
||||
State NetUnixState |
||||
Inode uint64 |
||||
Path string |
||||
} |
||||
|
||||
// NetUnix holds the data read from /proc/net/unix.
|
||||
type NetUnix struct { |
||||
Rows []*NetUnixLine |
||||
} |
||||
|
||||
// NewNetUnix returns data read from /proc/net/unix.
|
||||
func NewNetUnix() (*NetUnix, error) { |
||||
fs, err := NewFS(DefaultMountPoint) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return fs.NewNetUnix() |
||||
} |
||||
|
||||
// NewNetUnix returns data read from /proc/net/unix.
|
||||
func (fs FS) NewNetUnix() (*NetUnix, error) { |
||||
return NewNetUnixByPath(fs.proc.Path("net/unix")) |
||||
} |
||||
|
||||
// NewNetUnixByPath returns data read from /proc/net/unix by file path.
|
||||
// It might returns an error with partial parsed data, if an error occur after some data parsed.
|
||||
func NewNetUnixByPath(path string) (*NetUnix, error) { |
||||
f, err := os.Open(path) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer f.Close() |
||||
return NewNetUnixByReader(f) |
||||
} |
||||
|
||||
// NewNetUnixByReader returns data read from /proc/net/unix by a reader.
|
||||
// It might returns an error with partial parsed data, if an error occur after some data parsed.
|
||||
func NewNetUnixByReader(reader io.Reader) (*NetUnix, error) { |
||||
nu := &NetUnix{ |
||||
Rows: make([]*NetUnixLine, 0, 32), |
||||
} |
||||
scanner := bufio.NewScanner(reader) |
||||
// Omit the header line.
|
||||
scanner.Scan() |
||||
header := scanner.Text() |
||||
// From the man page of proc(5), it does not contain an Inode field,
|
||||
// but in actually it exists.
|
||||
// This code works for both cases.
|
||||
hasInode := strings.Contains(header, "Inode") |
||||
|
||||
minFieldsCnt := netUnixStaticFieldsCnt |
||||
if hasInode { |
||||
minFieldsCnt++ |
||||
} |
||||
for scanner.Scan() { |
||||
line := scanner.Text() |
||||
item, err := nu.parseLine(line, hasInode, minFieldsCnt) |
||||
if err != nil { |
||||
return nu, err |
||||
} |
||||
nu.Rows = append(nu.Rows, item) |
||||
} |
||||
|
||||
return nu, scanner.Err() |
||||
} |
||||
|
||||
func (u *NetUnix) parseLine(line string, hasInode bool, minFieldsCnt int) (*NetUnixLine, error) { |
||||
fields := strings.Fields(line) |
||||
fieldsLen := len(fields) |
||||
if fieldsLen < minFieldsCnt { |
||||
return nil, fmt.Errorf( |
||||
"Parse Unix domain failed: expect at least %d fields but got %d", |
||||
minFieldsCnt, fieldsLen) |
||||
} |
||||
kernelPtr, err := u.parseKernelPtr(fields[netUnixKernelPtrIdx]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Parse Unix domain num(%s) failed: %s", fields[netUnixKernelPtrIdx], err) |
||||
} |
||||
users, err := u.parseUsers(fields[netUnixRefCountIdx]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Parse Unix domain ref count(%s) failed: %s", fields[netUnixRefCountIdx], err) |
||||
} |
||||
flags, err := u.parseFlags(fields[netUnixFlagsIdx]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Parse Unix domain flags(%s) failed: %s", fields[netUnixFlagsIdx], err) |
||||
} |
||||
typ, err := u.parseType(fields[netUnixTypeIdx]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Parse Unix domain type(%s) failed: %s", fields[netUnixTypeIdx], err) |
||||
} |
||||
state, err := u.parseState(fields[netUnixStateIdx]) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Parse Unix domain state(%s) failed: %s", fields[netUnixStateIdx], err) |
||||
} |
||||
var inode uint64 |
||||
if hasInode { |
||||
inodeStr := fields[netUnixInodeIdx] |
||||
inode, err = u.parseInode(inodeStr) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("Parse Unix domain inode(%s) failed: %s", inodeStr, err) |
||||
} |
||||
} |
||||
|
||||
nuLine := &NetUnixLine{ |
||||
KernelPtr: kernelPtr, |
||||
RefCount: users, |
||||
Type: typ, |
||||
Flags: flags, |
||||
State: state, |
||||
Inode: inode, |
||||
} |
||||
|
||||
// Path field is optional.
|
||||
if fieldsLen > minFieldsCnt { |
||||
pathIdx := netUnixInodeIdx + 1 |
||||
if !hasInode { |
||||
pathIdx-- |
||||
} |
||||
nuLine.Path = fields[pathIdx] |
||||
} |
||||
|
||||
return nuLine, nil |
||||
} |
||||
|
||||
func (u NetUnix) parseKernelPtr(str string) (string, error) { |
||||
if !strings.HasSuffix(str, ":") { |
||||
return "", errInvalidKernelPtrFmt |
||||
} |
||||
return str[:len(str)-1], nil |
||||
} |
||||
|
||||
func (u NetUnix) parseUsers(hexStr string) (uint64, error) { |
||||
return strconv.ParseUint(hexStr, 16, 32) |
||||
} |
||||
|
||||
func (u NetUnix) parseProtocol(hexStr string) (uint64, error) { |
||||
return strconv.ParseUint(hexStr, 16, 32) |
||||
} |
||||
|
||||
func (u NetUnix) parseType(hexStr string) (NetUnixType, error) { |
||||
typ, err := strconv.ParseUint(hexStr, 16, 16) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return NetUnixType(typ), nil |
||||
} |
||||
|
||||
func (u NetUnix) parseFlags(hexStr string) (NetUnixFlags, error) { |
||||
flags, err := strconv.ParseUint(hexStr, 16, 32) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return NetUnixFlags(flags), nil |
||||
} |
||||
|
||||
func (u NetUnix) parseState(hexStr string) (NetUnixState, error) { |
||||
st, err := strconv.ParseInt(hexStr, 16, 8) |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return NetUnixState(st), nil |
||||
} |
||||
|
||||
func (u NetUnix) parseInode(inodeStr string) (uint64, error) { |
||||
return strconv.ParseUint(inodeStr, 10, 64) |
||||
} |
||||
|
||||
func (t NetUnixType) String() string { |
||||
switch t { |
||||
case netUnixTypeStream: |
||||
return "stream" |
||||
case netUnixTypeDgram: |
||||
return "dgram" |
||||
case netUnixTypeSeqpacket: |
||||
return "seqpacket" |
||||
} |
||||
return "unknown" |
||||
} |
||||
|
||||
func (f NetUnixFlags) String() string { |
||||
switch f { |
||||
case netUnixFlagListen: |
||||
return "listen" |
||||
default: |
||||
return "default" |
||||
} |
||||
} |
||||
|
||||
func (s NetUnixState) String() string { |
||||
switch s { |
||||
case netUnixStateUnconnected: |
||||
return "unconnected" |
||||
case netUnixStateConnecting: |
||||
return "connecting" |
||||
case netUnixStateConnected: |
||||
return "connected" |
||||
case netUnixStateDisconnected: |
||||
return "disconnected" |
||||
} |
||||
return "unknown" |
||||
} |
||||
@ -0,0 +1,162 @@ |
||||
// 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 procfs |
||||
|
||||
import ( |
||||
"bytes" |
||||
"io/ioutil" |
||||
"os" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
// ProcStat provides status information about the process,
|
||||
// read from /proc/[pid]/stat.
|
||||
type ProcStatus struct { |
||||
// The process ID.
|
||||
PID int |
||||
// The process name.
|
||||
Name string |
||||
|
||||
// Peak virtual memory size.
|
||||
VmPeak uint64 |
||||
// Virtual memory size.
|
||||
VmSize uint64 |
||||
// Locked memory size.
|
||||
VmLck uint64 |
||||
// Pinned memory size.
|
||||
VmPin uint64 |
||||
// Peak resident set size.
|
||||
VmHWM uint64 |
||||
// Resident set size (sum of RssAnnon RssFile and RssShmem).
|
||||
VmRSS uint64 |
||||
// Size of resident anonymous memory.
|
||||
RssAnon uint64 |
||||
// Size of resident file mappings.
|
||||
RssFile uint64 |
||||
// Size of resident shared memory.
|
||||
RssShmem uint64 |
||||
// Size of data segments.
|
||||
VmData uint64 |
||||
// Size of stack segments.
|
||||
VmStk uint64 |
||||
// Size of text segments.
|
||||
VmExe uint64 |
||||
// Shared library code size.
|
||||
VmLib uint64 |
||||
// Page table entries size.
|
||||
VmPTE uint64 |
||||
// Size of second-level page tables.
|
||||
VmPMD uint64 |
||||
// Swapped-out virtual memory size by anonymous private.
|
||||
VmSwap uint64 |
||||
// Size of hugetlb memory portions
|
||||
HugetlbPages uint64 |
||||
|
||||
// Number of voluntary context switches.
|
||||
VoluntaryCtxtSwitches uint64 |
||||
// Number of involuntary context switches.
|
||||
NonVoluntaryCtxtSwitches uint64 |
||||
} |
||||
|
||||
// NewStatus returns the current status information of the process.
|
||||
func (p Proc) NewStatus() (ProcStatus, error) { |
||||
f, err := os.Open(p.path("status")) |
||||
if err != nil { |
||||
return ProcStatus{}, err |
||||
} |
||||
defer f.Close() |
||||
|
||||
data, err := ioutil.ReadAll(f) |
||||
if err != nil { |
||||
return ProcStatus{}, err |
||||
} |
||||
|
||||
s := ProcStatus{PID: p.PID} |
||||
|
||||
lines := strings.Split(string(data), "\n") |
||||
for _, line := range lines { |
||||
if !bytes.Contains([]byte(line), []byte(":")) { |
||||
continue |
||||
} |
||||
|
||||
kv := strings.SplitN(line, ":", 2) |
||||
|
||||
// removes spaces
|
||||
k := string(strings.TrimSpace(kv[0])) |
||||
v := string(strings.TrimSpace(kv[1])) |
||||
// removes "kB"
|
||||
v = string(bytes.Trim([]byte(v), " kB")) |
||||
|
||||
// value to int when possible
|
||||
// we can skip error check here, 'cause vKBytes is not used when value is a string
|
||||
vKBytes, _ := strconv.ParseUint(v, 10, 64) |
||||
// convert kB to B
|
||||
vBytes := vKBytes * 1024 |
||||
|
||||
s.fillStatus(k, v, vKBytes, vBytes) |
||||
} |
||||
|
||||
return s, nil |
||||
} |
||||
|
||||
func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintBytes uint64) { |
||||
switch k { |
||||
case "Name": |
||||
s.Name = vString |
||||
case "VmPeak": |
||||
s.VmPeak = vUintBytes |
||||
case "VmSize": |
||||
s.VmSize = vUintBytes |
||||
case "VmLck": |
||||
s.VmLck = vUintBytes |
||||
case "VmPin": |
||||
s.VmPin = vUintBytes |
||||
case "VmHWM": |
||||
s.VmHWM = vUintBytes |
||||
case "VmRSS": |
||||
s.VmRSS = vUintBytes |
||||
case "RssAnon": |
||||
s.RssAnon = vUintBytes |
||||
case "RssFile": |
||||
s.RssFile = vUintBytes |
||||
case "RssShmem": |
||||
s.RssShmem = vUintBytes |
||||
case "VmData": |
||||
s.VmData = vUintBytes |
||||
case "VmStk": |
||||
s.VmStk = vUintBytes |
||||
case "VmExe": |
||||
s.VmExe = vUintBytes |
||||
case "VmLib": |
||||
s.VmLib = vUintBytes |
||||
case "VmPTE": |
||||
s.VmPTE = vUintBytes |
||||
case "VmPMD": |
||||
s.VmPMD = vUintBytes |
||||
case "VmSwap": |
||||
s.VmSwap = vUintBytes |
||||
case "HugetlbPages": |
||||
s.HugetlbPages = vUintBytes |
||||
case "voluntary_ctxt_switches": |
||||
s.VoluntaryCtxtSwitches = vUint |
||||
case "nonvoluntary_ctxt_switches": |
||||
s.NonVoluntaryCtxtSwitches = vUint |
||||
} |
||||
} |
||||
|
||||
// TotalCtxtSwitches returns the total context switch.
|
||||
func (s ProcStatus) TotalCtxtSwitches() uint64 { |
||||
return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches |
||||
} |
||||
Loading…
Reference in new issue