forked from mirror/client_golang
Merge branch 'master' into dev-v2
This commit is contained in:
commit
01b8d6317e
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -1,3 +1,16 @@
|
||||||
|
## 1.0.0 / 2019-06-15
|
||||||
|
|
||||||
|
_This release removes all previously deprecated features, resulting in the breaking changes listed below. As this is v1.0.0, semantic versioning applies from now on, with the exception of the API client and parts marked explicitly as experimental._
|
||||||
|
|
||||||
|
* [CHANGE] Remove objectives from the default `Summary`. (Objectives have to be set explicitly in the `SummaryOpts`.) #600
|
||||||
|
* [CHANGE] Remove all HTTP related feature in the `prometheus` package. (Use the `promhttp` package instead.) #600
|
||||||
|
* [CHANGE] Remove `push.FromGatherer`, `push.AddFromGatherer`, `push.Collectors`. (Use `push.New` instead.) #600
|
||||||
|
* [CHANGE] API client: Pass warnings through on non-error responses. #599
|
||||||
|
* [CHANGE] API client: Add warnings to `Series` call. #603
|
||||||
|
* [FEATURE] Make process collector work on Microsoft Windows. **EXPERIMENTAL** #596
|
||||||
|
* [FEATURE] API client: Add `/labels` call. #604
|
||||||
|
* [BUGFIX] Make `AlreadyRegisteredError` usable for wrapped registries. #607
|
||||||
|
|
||||||
## 0.9.4 / 2019-06-07
|
## 0.9.4 / 2019-06-07
|
||||||
* [CHANGE] API client: Switch to alert values as strings. #585
|
* [CHANGE] API client: Switch to alert values as strings. #585
|
||||||
* [FEATURE] Add a collector for Go module build information. #595
|
* [FEATURE] Add a collector for Go module build information. #595
|
||||||
|
|
|
@ -248,7 +248,9 @@ proto:
|
||||||
ifdef GOLANGCI_LINT
|
ifdef GOLANGCI_LINT
|
||||||
$(GOLANGCI_LINT):
|
$(GOLANGCI_LINT):
|
||||||
mkdir -p $(FIRST_GOPATH)/bin
|
mkdir -p $(FIRST_GOPATH)/bin
|
||||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION)
|
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_LINT_VERSION)/install.sh \
|
||||||
|
| sed -e '/install -d/d' \
|
||||||
|
| sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef GOVENDOR
|
ifdef GOVENDOR
|
||||||
|
|
48
README.md
48
README.md
|
@ -11,41 +11,23 @@ Prometheus HTTP API.
|
||||||
|
|
||||||
__This library requires Go1.9 or later.__
|
__This library requires Go1.9 or later.__
|
||||||
|
|
||||||
## Important note about releases, versioning, tagging, and stability
|
## Important note about releases and stability
|
||||||
|
|
||||||
In this repository, we used to mostly ignore the many coming and going
|
This repository generally follows [Semantic
|
||||||
dependency management tools for Go and instead wait for a tool that most of the
|
Versioning](https://semver.org/). However, the API client in
|
||||||
community would converge on. Our bet is that this tool has arrived now in the
|
prometheus/client_golang/api/… is still considered experimental. Breaking
|
||||||
form of [Go
|
changes of the API client will _not_ trigger a new major release. The same is
|
||||||
Modules](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies).
|
true for selected other new features explicitly marked as **EXPERIMENTAL** in
|
||||||
|
CHANGELOG.md.
|
||||||
|
|
||||||
To make full use of what Go Modules are offering, the previous versioning
|
Features that require breaking changes in the stable parts of the repository
|
||||||
roadmap for this repository had to be changed. In particular, Go Modules
|
are being batched up and tracked in the [v2
|
||||||
finally provide a way for incompatible versions of the same package to coexist
|
milestone](https://github.com/prometheus/client_golang/milestone/2). The v2
|
||||||
in the same binary. For that, however, the versions must be tagged with
|
development happens in a [separate
|
||||||
different major versions of 1 or greater (following [Semantic
|
branch](https://github.com/prometheus/client_golang/tree/dev-v2) for the time
|
||||||
Versioning](https://semver.org/)). Thus, we decided to abandon the original
|
being. v2 releases off that branch will happen once sufficient stability is
|
||||||
plan of introducing a lot of breaking changes _before_ releasing v1 of this
|
reached. In view of the widespread use of this repository, v1 and v2 will
|
||||||
repository, mostly driven by the widespread use this repository already has and
|
coexist for a while to enable a convenient transition.
|
||||||
the relatively stable state it is in.
|
|
||||||
|
|
||||||
To leverage the mechanism Go Modules offers for a transition between major
|
|
||||||
version, the current plan is the following:
|
|
||||||
|
|
||||||
- The v0.9.x series of releases will see a small number of bugfix releases to
|
|
||||||
deal with a few remaining minor issues (#543, #542, #539).
|
|
||||||
- After that, all features currently marked as _deprecated_ will be removed,
|
|
||||||
and the result will be released as v1.0.0.
|
|
||||||
- The planned breaking changes previously gathered as part of the v0.10
|
|
||||||
milestone will now go into the v2 milestone. The v2 development happens in a
|
|
||||||
[separate branch](https://github.com/prometheus/client_golang/tree/dev-v2)
|
|
||||||
for the time being. v2 releases off that branch will happen once sufficient
|
|
||||||
stability is reached. v1 and v2 will coexist for a while to enable a
|
|
||||||
convenient transition.
|
|
||||||
- The API client in prometheus/client_golang/api/… is still considered
|
|
||||||
experimental. While it will be tagged alongside the rest of the code
|
|
||||||
according to the plan above, we cannot strictly guarantee semver semantics
|
|
||||||
for it.
|
|
||||||
|
|
||||||
## Instrumenting applications
|
## Instrumenting applications
|
||||||
|
|
||||||
|
|
|
@ -25,41 +25,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewErrorAPI(err error, warnings []string) Error {
|
type Warnings []string
|
||||||
if err == nil && warnings == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &ErrorAPI{err, warnings}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorAPI struct {
|
|
||||||
err error
|
|
||||||
warnings []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *ErrorAPI) Err() error {
|
|
||||||
return w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *ErrorAPI) Error() string {
|
|
||||||
if w.err != nil {
|
|
||||||
return w.err.Error()
|
|
||||||
}
|
|
||||||
return "Warnings: " + strings.Join(w.warnings, " , ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *ErrorAPI) Warnings() []string {
|
|
||||||
return w.warnings
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error encapsulates an error + warning
|
|
||||||
type Error interface {
|
|
||||||
error
|
|
||||||
// Err returns the underlying error.
|
|
||||||
Err() error
|
|
||||||
// Warnings returns a list of warnings.
|
|
||||||
Warnings() []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultRoundTripper is used if no RoundTripper is set in Config.
|
// DefaultRoundTripper is used if no RoundTripper is set in Config.
|
||||||
var DefaultRoundTripper http.RoundTripper = &http.Transport{
|
var DefaultRoundTripper http.RoundTripper = &http.Transport{
|
||||||
|
@ -91,30 +57,30 @@ func (cfg *Config) roundTripper() http.RoundTripper {
|
||||||
// Client is the interface for an API client.
|
// Client is the interface for an API client.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
URL(ep string, args map[string]string) *url.URL
|
URL(ep string, args map[string]string) *url.URL
|
||||||
Do(context.Context, *http.Request) (*http.Response, []byte, Error)
|
Do(context.Context, *http.Request) (*http.Response, []byte, Warnings, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoGetFallback will attempt to do the request as-is, and on a 405 it will fallback to a GET request.
|
// DoGetFallback will attempt to do the request as-is, and on a 405 it will fallback to a GET request.
|
||||||
func DoGetFallback(c Client, ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, Error) {
|
func DoGetFallback(c Client, ctx context.Context, u *url.URL, args url.Values) (*http.Response, []byte, Warnings, error) {
|
||||||
req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(args.Encode()))
|
req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(args.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewErrorAPI(err, nil)
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
resp, body, err := c.Do(ctx, req)
|
resp, body, warnings, err := c.Do(ctx, req)
|
||||||
if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed {
|
if resp != nil && resp.StatusCode == http.StatusMethodNotAllowed {
|
||||||
u.RawQuery = args.Encode()
|
u.RawQuery = args.Encode()
|
||||||
req, err = http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err = http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewErrorAPI(err, nil)
|
return nil, nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, body, NewErrorAPI(err, nil)
|
return resp, body, warnings, err
|
||||||
}
|
}
|
||||||
return resp, body, nil
|
return resp, body, warnings, nil
|
||||||
}
|
}
|
||||||
return c.Do(ctx, req)
|
return c.Do(ctx, req)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +120,7 @@ func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
|
||||||
return &u
|
return &u
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, Error) {
|
func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, Warnings, error) {
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
}
|
}
|
||||||
|
@ -166,7 +132,7 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response,
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, NewErrorAPI(err, nil)
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var body []byte
|
var body []byte
|
||||||
|
@ -186,5 +152,5 @@ func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response,
|
||||||
case <-done:
|
case <-done:
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, body, NewErrorAPI(err, nil)
|
return resp, body, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ func TestDoGetFallback(t *testing.T) {
|
||||||
client := &httpClient{client: *(server.Client())}
|
client := &httpClient{client: *(server.Client())}
|
||||||
|
|
||||||
// Do a post, and ensure that the post succeeds.
|
// Do a post, and ensure that the post succeeds.
|
||||||
_, b, err := DoGetFallback(client, context.TODO(), u, v)
|
_, b, _, err := DoGetFallback(client, context.TODO(), u, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error doing local request: %v", err)
|
t.Fatalf("Error doing local request: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ func TestDoGetFallback(t *testing.T) {
|
||||||
|
|
||||||
// Do a fallbcak to a get.
|
// Do a fallbcak to a get.
|
||||||
u.Path = "/blockPost"
|
u.Path = "/blockPost"
|
||||||
_, b, err = DoGetFallback(client, context.TODO(), u, v)
|
_, b, _, err = DoGetFallback(client, context.TODO(), u, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error doing local request: %v", err)
|
t.Fatalf("Error doing local request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -125,6 +124,7 @@ const (
|
||||||
epAlertManagers = apiPrefix + "/alertmanagers"
|
epAlertManagers = apiPrefix + "/alertmanagers"
|
||||||
epQuery = apiPrefix + "/query"
|
epQuery = apiPrefix + "/query"
|
||||||
epQueryRange = apiPrefix + "/query_range"
|
epQueryRange = apiPrefix + "/query_range"
|
||||||
|
epLabels = apiPrefix + "/labels"
|
||||||
epLabelValues = apiPrefix + "/label/:name/values"
|
epLabelValues = apiPrefix + "/label/:name/values"
|
||||||
epSeries = apiPrefix + "/series"
|
epSeries = apiPrefix + "/series"
|
||||||
epTargets = apiPrefix + "/targets"
|
epTargets = apiPrefix + "/targets"
|
||||||
|
@ -200,25 +200,12 @@ type Error struct {
|
||||||
Type ErrorType
|
Type ErrorType
|
||||||
Msg string
|
Msg string
|
||||||
Detail string
|
Detail string
|
||||||
warnings []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Error) Error() string {
|
func (e *Error) Error() string {
|
||||||
if e.Type != "" || e.Msg != "" {
|
|
||||||
return fmt.Sprintf("%s: %s", e.Type, e.Msg)
|
return fmt.Sprintf("%s: %s", e.Type, e.Msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Warnings: " + strings.Join(e.warnings, " , ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Error) Err() error {
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Error) Warnings() []string {
|
|
||||||
return w.warnings
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range represents a sliced time range.
|
// Range represents a sliced time range.
|
||||||
type Range struct {
|
type Range struct {
|
||||||
// The boundaries of the time range.
|
// The boundaries of the time range.
|
||||||
|
@ -230,34 +217,36 @@ type Range struct {
|
||||||
// API provides bindings for Prometheus's v1 API.
|
// API provides bindings for Prometheus's v1 API.
|
||||||
type API interface {
|
type API interface {
|
||||||
// Alerts returns a list of all active alerts.
|
// Alerts returns a list of all active alerts.
|
||||||
Alerts(ctx context.Context) (AlertsResult, api.Error)
|
Alerts(ctx context.Context) (AlertsResult, error)
|
||||||
// AlertManagers returns an overview of the current state of the Prometheus alert manager discovery.
|
// AlertManagers returns an overview of the current state of the Prometheus alert manager discovery.
|
||||||
AlertManagers(ctx context.Context) (AlertManagersResult, api.Error)
|
AlertManagers(ctx context.Context) (AlertManagersResult, error)
|
||||||
// CleanTombstones removes the deleted data from disk and cleans up the existing tombstones.
|
// CleanTombstones removes the deleted data from disk and cleans up the existing tombstones.
|
||||||
CleanTombstones(ctx context.Context) api.Error
|
CleanTombstones(ctx context.Context) error
|
||||||
// Config returns the current Prometheus configuration.
|
// Config returns the current Prometheus configuration.
|
||||||
Config(ctx context.Context) (ConfigResult, api.Error)
|
Config(ctx context.Context) (ConfigResult, error)
|
||||||
// DeleteSeries deletes data for a selection of series in a time range.
|
// DeleteSeries deletes data for a selection of series in a time range.
|
||||||
DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) api.Error
|
DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error
|
||||||
// Flags returns the flag values that Prometheus was launched with.
|
// Flags returns the flag values that Prometheus was launched with.
|
||||||
Flags(ctx context.Context) (FlagsResult, api.Error)
|
Flags(ctx context.Context) (FlagsResult, error)
|
||||||
|
// LabelNames returns all the unique label names present in the block in sorted order.
|
||||||
|
LabelNames(ctx context.Context) ([]string, error)
|
||||||
// LabelValues performs a query for the values of the given label.
|
// LabelValues performs a query for the values of the given label.
|
||||||
LabelValues(ctx context.Context, label string) (model.LabelValues, api.Error)
|
LabelValues(ctx context.Context, label string) (model.LabelValues, error)
|
||||||
// Query performs a query for the given time.
|
// Query performs a query for the given time.
|
||||||
Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Error)
|
Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Warnings, error)
|
||||||
// QueryRange performs a query for the given range.
|
// QueryRange performs a query for the given range.
|
||||||
QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Error)
|
QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Warnings, error)
|
||||||
// Series finds series by label matchers.
|
// Series finds series by label matchers.
|
||||||
Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Error)
|
Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Warnings, error)
|
||||||
// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
|
// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
|
||||||
// under the TSDB's data directory and returns the directory as response.
|
// under the TSDB's data directory and returns the directory as response.
|
||||||
Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, api.Error)
|
Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error)
|
||||||
// Rules returns a list of alerting and recording rules that are currently loaded.
|
// Rules returns a list of alerting and recording rules that are currently loaded.
|
||||||
Rules(ctx context.Context) (RulesResult, api.Error)
|
Rules(ctx context.Context) (RulesResult, error)
|
||||||
// Targets returns an overview of the current state of the Prometheus target discovery.
|
// Targets returns an overview of the current state of the Prometheus target discovery.
|
||||||
Targets(ctx context.Context) (TargetsResult, api.Error)
|
Targets(ctx context.Context) (TargetsResult, error)
|
||||||
// TargetsMetadata returns metadata about metrics currently scraped by the target.
|
// TargetsMetadata returns metadata about metrics currently scraped by the target.
|
||||||
TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, api.Error)
|
TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlertsResult contains the result from querying the alerts endpoint.
|
// AlertsResult contains the result from querying the alerts endpoint.
|
||||||
|
@ -534,73 +523,70 @@ type httpAPI struct {
|
||||||
client api.Client
|
client api.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Alerts(ctx context.Context) (AlertsResult, api.Error) {
|
func (h *httpAPI) Alerts(ctx context.Context) (AlertsResult, error) {
|
||||||
u := h.client.URL(epAlerts, nil)
|
u := h.client.URL(epAlerts, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return AlertsResult{}, api.NewErrorAPI(err, nil)
|
return AlertsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return AlertsResult{}, apiErr
|
return AlertsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res AlertsResult
|
var res AlertsResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, api.Error) {
|
func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, error) {
|
||||||
u := h.client.URL(epAlertManagers, nil)
|
u := h.client.URL(epAlertManagers, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return AlertManagersResult{}, api.NewErrorAPI(err, nil)
|
return AlertManagersResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return AlertManagersResult{}, apiErr
|
return AlertManagersResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res AlertManagersResult
|
var res AlertManagersResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) CleanTombstones(ctx context.Context) api.Error {
|
func (h *httpAPI) CleanTombstones(ctx context.Context) error {
|
||||||
u := h.client.URL(epCleanTombstones, nil)
|
u := h.client.URL(epCleanTombstones, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
|
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.NewErrorAPI(err, nil)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, apiErr := h.client.Do(ctx, req)
|
_, _, _, err = h.client.Do(ctx, req)
|
||||||
return apiErr
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Config(ctx context.Context) (ConfigResult, api.Error) {
|
func (h *httpAPI) Config(ctx context.Context) (ConfigResult, error) {
|
||||||
u := h.client.URL(epConfig, nil)
|
u := h.client.URL(epConfig, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ConfigResult{}, api.NewErrorAPI(err, nil)
|
return ConfigResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return ConfigResult{}, apiErr
|
return ConfigResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res ConfigResult
|
var res ConfigResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) api.Error {
|
func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error {
|
||||||
u := h.client.URL(epDeleteSeries, nil)
|
u := h.client.URL(epDeleteSeries, nil)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
|
@ -615,47 +601,59 @@ func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
|
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return api.NewErrorAPI(err, nil)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, apiErr := h.client.Do(ctx, req)
|
_, _, _, err = h.client.Do(ctx, req)
|
||||||
return apiErr
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, api.Error) {
|
func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) {
|
||||||
u := h.client.URL(epFlags, nil)
|
u := h.client.URL(epFlags, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FlagsResult{}, api.NewErrorAPI(err, nil)
|
return FlagsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return FlagsResult{}, apiErr
|
return FlagsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res FlagsResult
|
var res FlagsResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, api.Error) {
|
func (h *httpAPI) LabelNames(ctx context.Context) ([]string, error) {
|
||||||
|
u := h.client.URL(epLabels, nil)
|
||||||
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var labelNames []string
|
||||||
|
return labelNames, json.Unmarshal(body, &labelNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
|
||||||
u := h.client.URL(epLabelValues, map[string]string{"name": label})
|
u := h.client.URL(epLabelValues, map[string]string{"name": label})
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, api.NewErrorAPI(err, nil)
|
return nil, err
|
||||||
}
|
}
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return nil, apiErr
|
return nil, err
|
||||||
}
|
}
|
||||||
var labelValues model.LabelValues
|
var labelValues model.LabelValues
|
||||||
err = json.Unmarshal(body, &labelValues)
|
return labelValues, json.Unmarshal(body, &labelValues)
|
||||||
return labelValues, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Error) {
|
func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Warnings, error) {
|
||||||
u := h.client.URL(epQuery, nil)
|
u := h.client.URL(epQuery, nil)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
|
@ -664,16 +662,16 @@ func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.
|
||||||
q.Set("time", ts.Format(time.RFC3339Nano))
|
q.Set("time", ts.Format(time.RFC3339Nano))
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := api.DoGetFallback(h.client, ctx, u, q)
|
_, body, warnings, err := api.DoGetFallback(h.client, ctx, u, q)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return nil, apiErr
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var qres queryResult
|
var qres queryResult
|
||||||
return model.Value(qres.v), api.NewErrorAPI(json.Unmarshal(body, &qres), nil)
|
return model.Value(qres.v), warnings, json.Unmarshal(body, &qres)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Error) {
|
func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Warnings, error) {
|
||||||
u := h.client.URL(epQueryRange, nil)
|
u := h.client.URL(epQueryRange, nil)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
|
@ -688,17 +686,17 @@ func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.
|
||||||
q.Set("end", end)
|
q.Set("end", end)
|
||||||
q.Set("step", step)
|
q.Set("step", step)
|
||||||
|
|
||||||
_, body, apiErr := api.DoGetFallback(h.client, ctx, u, q)
|
_, body, warnings, err := api.DoGetFallback(h.client, ctx, u, q)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return nil, apiErr
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var qres queryResult
|
var qres queryResult
|
||||||
|
|
||||||
return model.Value(qres.v), api.NewErrorAPI(json.Unmarshal(body, &qres), nil)
|
return model.Value(qres.v), warnings, json.Unmarshal(body, &qres)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Error) {
|
func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Warnings, error) {
|
||||||
u := h.client.URL(epSeries, nil)
|
u := h.client.URL(epSeries, nil)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
|
@ -713,20 +711,19 @@ func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.T
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, api.NewErrorAPI(err, nil)
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, warnings, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return nil, apiErr
|
return nil, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var mset []model.LabelSet
|
var mset []model.LabelSet
|
||||||
err = json.Unmarshal(body, &mset)
|
return mset, warnings, json.Unmarshal(body, &mset)
|
||||||
return mset, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, api.Error) {
|
func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error) {
|
||||||
u := h.client.URL(epSnapshot, nil)
|
u := h.client.URL(epSnapshot, nil)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
|
@ -736,56 +733,53 @@ func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult,
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
|
req, err := http.NewRequest(http.MethodPost, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SnapshotResult{}, api.NewErrorAPI(err, nil)
|
return SnapshotResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return SnapshotResult{}, apiErr
|
return SnapshotResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res SnapshotResult
|
var res SnapshotResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Rules(ctx context.Context) (RulesResult, api.Error) {
|
func (h *httpAPI) Rules(ctx context.Context) (RulesResult, error) {
|
||||||
u := h.client.URL(epRules, nil)
|
u := h.client.URL(epRules, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return RulesResult{}, api.NewErrorAPI(err, nil)
|
return RulesResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return RulesResult{}, apiErr
|
return RulesResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res RulesResult
|
var res RulesResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, api.Error) {
|
func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, error) {
|
||||||
u := h.client.URL(epTargets, nil)
|
u := h.client.URL(epTargets, nil)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TargetsResult{}, api.NewErrorAPI(err, nil)
|
return TargetsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return TargetsResult{}, apiErr
|
return TargetsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res TargetsResult
|
var res TargetsResult
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, api.Error) {
|
func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, error) {
|
||||||
u := h.client.URL(epTargetsMetadata, nil)
|
u := h.client.URL(epTargetsMetadata, nil)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
|
@ -797,17 +791,16 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, api.NewErrorAPI(err, nil)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, body, apiErr := h.client.Do(ctx, req)
|
_, body, _, err := h.client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return nil, apiErr
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []MetricMetadata
|
var res []MetricMetadata
|
||||||
err = json.Unmarshal(body, &res)
|
return res, json.Unmarshal(body, &res)
|
||||||
return res, api.NewErrorAPI(err, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// apiClient wraps a regular client and processes successful API responses.
|
// apiClient wraps a regular client and processes successful API responses.
|
||||||
|
@ -839,19 +832,17 @@ func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
|
||||||
return ErrBadResponse, fmt.Sprintf("bad response code %d", resp.StatusCode)
|
return ErrBadResponse, fmt.Sprintf("bad response code %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Error) {
|
func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Warnings, error) {
|
||||||
resp, body, apiErr := c.Client.Do(ctx, req)
|
resp, body, warnings, err := c.Client.Do(ctx, req)
|
||||||
if apiErr != nil {
|
if err != nil {
|
||||||
return resp, body, apiErr
|
return resp, body, warnings, err
|
||||||
}
|
}
|
||||||
|
|
||||||
code := resp.StatusCode
|
code := resp.StatusCode
|
||||||
|
|
||||||
var err api.Error
|
|
||||||
|
|
||||||
if code/100 != 2 && !apiError(code) {
|
if code/100 != 2 && !apiError(code) {
|
||||||
errorType, errorMsg := errorTypeAndMsgFor(resp)
|
errorType, errorMsg := errorTypeAndMsgFor(resp)
|
||||||
return resp, body, &Error{
|
return resp, body, warnings, &Error{
|
||||||
Type: errorType,
|
Type: errorType,
|
||||||
Msg: errorMsg,
|
Msg: errorMsg,
|
||||||
Detail: string(body),
|
Detail: string(body),
|
||||||
|
@ -862,7 +853,7 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [
|
||||||
|
|
||||||
if http.StatusNoContent != code {
|
if http.StatusNoContent != code {
|
||||||
if jsonErr := json.Unmarshal(body, &result); jsonErr != nil {
|
if jsonErr := json.Unmarshal(body, &result); jsonErr != nil {
|
||||||
return resp, body, &Error{
|
return resp, body, warnings, &Error{
|
||||||
Type: ErrBadResponse,
|
Type: ErrBadResponse,
|
||||||
Msg: jsonErr.Error(),
|
Msg: jsonErr.Error(),
|
||||||
}
|
}
|
||||||
|
@ -873,7 +864,6 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [
|
||||||
err = &Error{
|
err = &Error{
|
||||||
Type: ErrBadResponse,
|
Type: ErrBadResponse,
|
||||||
Msg: "inconsistent body for response code",
|
Msg: "inconsistent body for response code",
|
||||||
warnings: result.Warnings,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,10 +871,9 @@ func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, [
|
||||||
err = &Error{
|
err = &Error{
|
||||||
Type: result.ErrorType,
|
Type: result.ErrorType,
|
||||||
Msg: result.Error,
|
Msg: result.Error,
|
||||||
warnings: result.Warnings,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, []byte(result.Data), err
|
return resp, []byte(result.Data), warnings, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type apiTest struct {
|
type apiTest struct {
|
||||||
do func() (interface{}, api.Error)
|
do func() (interface{}, api.Warnings, error)
|
||||||
inWarnings []string
|
inWarnings []string
|
||||||
inErr error
|
inErr error
|
||||||
inStatusCode int
|
inStatusCode int
|
||||||
|
@ -43,6 +43,7 @@ type apiTest struct {
|
||||||
reqParam url.Values
|
reqParam url.Values
|
||||||
reqMethod string
|
reqMethod string
|
||||||
res interface{}
|
res interface{}
|
||||||
|
warnings api.Warnings
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ func (c *apiTestClient) URL(ep string, args map[string]string) *url.URL {
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Error) {
|
func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Warnings, error) {
|
||||||
|
|
||||||
test := c.curTest
|
test := c.curTest
|
||||||
|
|
||||||
|
@ -88,7 +89,7 @@ func (c *apiTestClient) Do(ctx context.Context, req *http.Request) (*http.Respon
|
||||||
resp.StatusCode = http.StatusOK
|
resp.StatusCode = http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, b, api.NewErrorAPI(test.inErr, test.inWarnings)
|
return resp, b, test.inWarnings, test.inErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIs(t *testing.T) {
|
func TestAPIs(t *testing.T) {
|
||||||
|
@ -101,81 +102,96 @@ func TestAPIs(t *testing.T) {
|
||||||
client: client,
|
client: client,
|
||||||
}
|
}
|
||||||
|
|
||||||
doAlertManagers := func() func() (interface{}, api.Error) {
|
doAlertManagers := func() func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.AlertManagers(context.Background())
|
v, err := promAPI.AlertManagers(context.Background())
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doCleanTombstones := func() func() (interface{}, api.Error) {
|
doCleanTombstones := func() func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return nil, promAPI.CleanTombstones(context.Background())
|
return nil, nil, promAPI.CleanTombstones(context.Background())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doConfig := func() func() (interface{}, api.Error) {
|
doConfig := func() func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Config(context.Background())
|
v, err := promAPI.Config(context.Background())
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doDeleteSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, api.Error) {
|
doDeleteSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return nil, promAPI.DeleteSeries(context.Background(), []string{matcher}, startTime, endTime)
|
return nil, nil, promAPI.DeleteSeries(context.Background(), []string{matcher}, startTime, endTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doFlags := func() func() (interface{}, api.Error) {
|
doFlags := func() func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Flags(context.Background())
|
v, err := promAPI.Flags(context.Background())
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doLabelValues := func(label string) func() (interface{}, api.Error) {
|
doLabelNames := func(label string) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.LabelValues(context.Background(), label)
|
v, err := promAPI.LabelNames(context.Background())
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doQuery := func(q string, ts time.Time) func() (interface{}, api.Error) {
|
doLabelValues := func(label string) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
|
v, err := promAPI.LabelValues(context.Background(), label)
|
||||||
|
return v, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doQuery := func(q string, ts time.Time) func() (interface{}, api.Warnings, error) {
|
||||||
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Query(context.Background(), q, ts)
|
return promAPI.Query(context.Background(), q, ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doQueryRange := func(q string, rng Range) func() (interface{}, api.Error) {
|
doQueryRange := func(q string, rng Range) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.QueryRange(context.Background(), q, rng)
|
return promAPI.QueryRange(context.Background(), q, rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, api.Error) {
|
doSeries := func(matcher string, startTime time.Time, endTime time.Time) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Series(context.Background(), []string{matcher}, startTime, endTime)
|
return promAPI.Series(context.Background(), []string{matcher}, startTime, endTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doSnapshot := func(skipHead bool) func() (interface{}, api.Error) {
|
doSnapshot := func(skipHead bool) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Snapshot(context.Background(), skipHead)
|
v, err := promAPI.Snapshot(context.Background(), skipHead)
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doRules := func() func() (interface{}, api.Error) {
|
doRules := func() func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Rules(context.Background())
|
v, err := promAPI.Rules(context.Background())
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doTargets := func() func() (interface{}, api.Error) {
|
doTargets := func() func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.Targets(context.Background())
|
v, err := promAPI.Targets(context.Background())
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doTargetsMetadata := func(matchTarget string, metric string, limit string) func() (interface{}, api.Error) {
|
doTargetsMetadata := func(matchTarget string, metric string, limit string) func() (interface{}, api.Warnings, error) {
|
||||||
return func() (interface{}, api.Error) {
|
return func() (interface{}, api.Warnings, error) {
|
||||||
return promAPI.TargetsMetadata(context.Background(), matchTarget, metric, limit)
|
v, err := promAPI.TargetsMetadata(context.Background(), matchTarget, metric, limit)
|
||||||
|
return v, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +265,51 @@ func TestAPIs(t *testing.T) {
|
||||||
},
|
},
|
||||||
err: errors.New("client_error: client error: 404"),
|
err: errors.New("client_error: client error: 404"),
|
||||||
},
|
},
|
||||||
|
// Warning only.
|
||||||
|
{
|
||||||
|
do: doQuery("2", testTime),
|
||||||
|
inWarnings: []string{"warning"},
|
||||||
|
inRes: &queryResult{
|
||||||
|
Type: model.ValScalar,
|
||||||
|
Result: &model.Scalar{
|
||||||
|
Value: 2,
|
||||||
|
Timestamp: model.TimeFromUnix(testTime.Unix()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
reqMethod: "POST",
|
||||||
|
reqPath: "/api/v1/query",
|
||||||
|
reqParam: url.Values{
|
||||||
|
"query": []string{"2"},
|
||||||
|
"time": []string{testTime.Format(time.RFC3339Nano)},
|
||||||
|
},
|
||||||
|
res: &model.Scalar{
|
||||||
|
Value: 2,
|
||||||
|
Timestamp: model.TimeFromUnix(testTime.Unix()),
|
||||||
|
},
|
||||||
|
warnings: []string{"warning"},
|
||||||
|
},
|
||||||
|
// Warning + error.
|
||||||
|
{
|
||||||
|
do: doQuery("2", testTime),
|
||||||
|
inWarnings: []string{"warning"},
|
||||||
|
inRes: "some body",
|
||||||
|
inStatusCode: 404,
|
||||||
|
inErr: &Error{
|
||||||
|
Type: ErrClient,
|
||||||
|
Msg: "client error: 404",
|
||||||
|
Detail: "some body",
|
||||||
|
},
|
||||||
|
|
||||||
|
reqMethod: "POST",
|
||||||
|
reqPath: "/api/v1/query",
|
||||||
|
reqParam: url.Values{
|
||||||
|
"query": []string{"2"},
|
||||||
|
"time": []string{testTime.Format(time.RFC3339Nano)},
|
||||||
|
},
|
||||||
|
err: errors.New("client_error: client error: 404"),
|
||||||
|
warnings: []string{"warning"},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
do: doQueryRange("2", Range{
|
do: doQueryRange("2", Range{
|
||||||
|
@ -269,6 +330,22 @@ func TestAPIs(t *testing.T) {
|
||||||
err: fmt.Errorf("some error"),
|
err: fmt.Errorf("some error"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
do: doLabelNames("mylabel"),
|
||||||
|
inRes: []string{"val1", "val2"},
|
||||||
|
reqMethod: "GET",
|
||||||
|
reqPath: "/api/v1/labels",
|
||||||
|
res: []string{"val1", "val2"},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
do: doLabelNames("mylabel"),
|
||||||
|
inErr: fmt.Errorf("some error"),
|
||||||
|
reqMethod: "GET",
|
||||||
|
reqPath: "/api/v1/labels",
|
||||||
|
err: fmt.Errorf("some error"),
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
do: doLabelValues("mylabel"),
|
do: doLabelValues("mylabel"),
|
||||||
inRes: []string{"val1", "val2"},
|
inRes: []string{"val1", "val2"},
|
||||||
|
@ -308,6 +385,32 @@ func TestAPIs(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Series with data + warning.
|
||||||
|
{
|
||||||
|
do: doSeries("up", testTime.Add(-time.Minute), testTime),
|
||||||
|
inRes: []map[string]string{
|
||||||
|
{
|
||||||
|
"__name__": "up",
|
||||||
|
"job": "prometheus",
|
||||||
|
"instance": "localhost:9090"},
|
||||||
|
},
|
||||||
|
inWarnings: []string{"a"},
|
||||||
|
reqMethod: "GET",
|
||||||
|
reqPath: "/api/v1/series",
|
||||||
|
reqParam: url.Values{
|
||||||
|
"match": []string{"up"},
|
||||||
|
"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
|
||||||
|
"end": []string{testTime.Format(time.RFC3339Nano)},
|
||||||
|
},
|
||||||
|
res: []model.LabelSet{
|
||||||
|
{
|
||||||
|
"__name__": "up",
|
||||||
|
"job": "prometheus",
|
||||||
|
"instance": "localhost:9090",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
warnings: []string{"a"},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
do: doSeries("up", testTime.Add(-time.Minute), testTime),
|
do: doSeries("up", testTime.Add(-time.Minute), testTime),
|
||||||
|
@ -321,6 +424,21 @@ func TestAPIs(t *testing.T) {
|
||||||
},
|
},
|
||||||
err: fmt.Errorf("some error"),
|
err: fmt.Errorf("some error"),
|
||||||
},
|
},
|
||||||
|
// Series with error and warning.
|
||||||
|
{
|
||||||
|
do: doSeries("up", testTime.Add(-time.Minute), testTime),
|
||||||
|
inErr: fmt.Errorf("some error"),
|
||||||
|
inWarnings: []string{"a"},
|
||||||
|
reqMethod: "GET",
|
||||||
|
reqPath: "/api/v1/series",
|
||||||
|
reqParam: url.Values{
|
||||||
|
"match": []string{"up"},
|
||||||
|
"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
|
||||||
|
"end": []string{testTime.Format(time.RFC3339Nano)},
|
||||||
|
},
|
||||||
|
err: fmt.Errorf("some error"),
|
||||||
|
warnings: []string{"a"},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
do: doSnapshot(true),
|
do: doSnapshot(true),
|
||||||
|
@ -705,7 +823,11 @@ func TestAPIs(t *testing.T) {
|
||||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||||
client.curTest = test
|
client.curTest = test
|
||||||
|
|
||||||
res, err := test.do()
|
res, warnings, err := test.do()
|
||||||
|
|
||||||
|
if (test.inWarnings == nil) != (warnings == nil) && !reflect.DeepEqual(test.inWarnings, warnings) {
|
||||||
|
t.Fatalf("mismatch in warnings expected=%v actual=%v", test.inWarnings, warnings)
|
||||||
|
}
|
||||||
|
|
||||||
if test.err != nil {
|
if test.err != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -744,13 +866,14 @@ type apiClientTest struct {
|
||||||
response interface{}
|
response interface{}
|
||||||
expectedBody string
|
expectedBody string
|
||||||
expectedErr *Error
|
expectedErr *Error
|
||||||
|
expectedWarnings api.Warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testClient) URL(ep string, args map[string]string) *url.URL {
|
func (c *testClient) URL(ep string, args map[string]string) *url.URL {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Error) {
|
func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, api.Warnings, error) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
c.Fatalf("context was not passed down")
|
c.Fatalf("context was not passed down")
|
||||||
}
|
}
|
||||||
|
@ -777,7 +900,7 @@ func (c *testClient) Do(ctx context.Context, req *http.Request) (*http.Response,
|
||||||
StatusCode: test.code,
|
StatusCode: test.code,
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, b, nil
|
return resp, b, test.expectedWarnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAPIClientDo(t *testing.T) {
|
func TestAPIClientDo(t *testing.T) {
|
||||||
|
@ -898,8 +1021,8 @@ func TestAPIClientDo(t *testing.T) {
|
||||||
expectedErr: &Error{
|
expectedErr: &Error{
|
||||||
Type: ErrBadResponse,
|
Type: ErrBadResponse,
|
||||||
Msg: "inconsistent body for response code",
|
Msg: "inconsistent body for response code",
|
||||||
warnings: []string{"a"},
|
|
||||||
},
|
},
|
||||||
|
expectedWarnings: []string{"a"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,7 +1038,17 @@ func TestAPIClientDo(t *testing.T) {
|
||||||
|
|
||||||
tc.ch <- test
|
tc.ch <- test
|
||||||
|
|
||||||
_, body, err := client.Do(context.Background(), tc.req)
|
_, body, warnings, err := client.Do(context.Background(), tc.req)
|
||||||
|
|
||||||
|
if test.expectedWarnings != nil {
|
||||||
|
if !reflect.DeepEqual(test.expectedWarnings, warnings) {
|
||||||
|
t.Fatalf("mismatch in warnings expected=%v actual=%v", test.expectedWarnings, warnings)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if warnings != nil {
|
||||||
|
t.Fatalf("unexpexted warnings: %v", warnings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if test.expectedErr != nil {
|
if test.expectedErr != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -933,15 +1066,6 @@ func TestAPIClientDo(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(test.expectedErr.Warnings()) != len(err.Warnings()) {
|
|
||||||
t.Fatalf("expected warnings length :%v, but got:%v", len(test.expectedErr.Warnings()), len(err.Warnings()))
|
|
||||||
}
|
|
||||||
|
|
||||||
for x, warning := range test.expectedErr.Warnings() {
|
|
||||||
if warning != err.Warnings()[x] {
|
|
||||||
t.Fatalf("expected warning :%v, but got:%v", warning, err.Warnings()[x])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -10,4 +10,5 @@ require (
|
||||||
github.com/prometheus/common v0.4.1
|
github.com/prometheus/common v0.4.1
|
||||||
github.com/prometheus/procfs v0.0.2
|
github.com/prometheus/procfs v0.0.2
|
||||||
github.com/stretchr/testify v1.3.0 // indirect
|
github.com/stretchr/testify v1.3.0 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5
|
||||||
)
|
)
|
||||||
|
|
1
go.sum
1
go.sum
|
@ -58,6 +58,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5 h1:mzjBh+S5frKOsOBobWIMAbXavqjmgO17k/2puhcFR94=
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -16,8 +16,6 @@ package prometheus
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/prometheus/procfs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type processCollector struct {
|
type processCollector struct {
|
||||||
|
@ -59,20 +57,9 @@ type ProcessCollectorOpts struct {
|
||||||
// collector for the current process with an empty namespace string and no error
|
// collector for the current process with an empty namespace string and no error
|
||||||
// reporting.
|
// reporting.
|
||||||
//
|
//
|
||||||
// Currently, the collector depends on a Linux-style proc filesystem and
|
// The collector only works on operating systems with a Linux-style proc
|
||||||
// therefore only exports metrics for Linux.
|
// filesystem and on Microsoft Windows. On other operating systems, it will not
|
||||||
//
|
// collect any metrics.
|
||||||
// Note: An older version of this function had the following signature:
|
|
||||||
//
|
|
||||||
// NewProcessCollector(pid int, namespace string) Collector
|
|
||||||
//
|
|
||||||
// Most commonly, it was called as
|
|
||||||
//
|
|
||||||
// NewProcessCollector(os.Getpid(), "")
|
|
||||||
//
|
|
||||||
// The following call of the current version is equivalent to the above:
|
|
||||||
//
|
|
||||||
// NewProcessCollector(ProcessCollectorOpts{})
|
|
||||||
func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
||||||
ns := ""
|
ns := ""
|
||||||
if len(opts.Namespace) > 0 {
|
if len(opts.Namespace) > 0 {
|
||||||
|
@ -126,7 +113,7 @@ func NewProcessCollector(opts ProcessCollectorOpts) Collector {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up process metric collection if supported by the runtime.
|
// Set up process metric collection if supported by the runtime.
|
||||||
if _, err := procfs.NewDefaultFS(); err == nil {
|
if canCollectProcess() {
|
||||||
c.collectFn = c.processCollect
|
c.collectFn = c.processCollect
|
||||||
} else {
|
} else {
|
||||||
c.collectFn = func(ch chan<- Metric) {
|
c.collectFn = func(ch chan<- Metric) {
|
||||||
|
@ -153,46 +140,6 @@ func (c *processCollector) Collect(ch chan<- Metric) {
|
||||||
c.collectFn(ch)
|
c.collectFn(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
|
func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) {
|
||||||
if !c.reportErrors {
|
if !c.reportErrors {
|
||||||
return
|
return
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,70 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/common/expfmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWindowsProcessCollector(t *testing.T) {
|
||||||
|
registry := NewRegistry()
|
||||||
|
if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{})); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{
|
||||||
|
PidFn: func() (int, error) { return os.Getpid(), nil },
|
||||||
|
Namespace: "foobar",
|
||||||
|
ReportErrors: true, // No errors expected, just to see if none are reported.
|
||||||
|
})); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mfs, err := registry.Gather()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, mf := range mfs {
|
||||||
|
if _, err := expfmt.MetricFamilyToText(&buf, mf); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, re := range []*regexp.Regexp{
|
||||||
|
regexp.MustCompile("\nprocess_cpu_seconds_total [0-9]"),
|
||||||
|
regexp.MustCompile("\nprocess_max_fds [1-9]"),
|
||||||
|
regexp.MustCompile("\nprocess_open_fds [1-9]"),
|
||||||
|
regexp.MustCompile("\nprocess_virtual_memory_max_bytes (-1|[1-9])"),
|
||||||
|
regexp.MustCompile("\nprocess_virtual_memory_bytes [1-9]"),
|
||||||
|
regexp.MustCompile("\nprocess_resident_memory_bytes [1-9]"),
|
||||||
|
regexp.MustCompile("\nprocess_start_time_seconds [0-9.]{10,}"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_cpu_seconds_total [0-9]"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_max_fds [1-9]"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_open_fds [1-9]"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_virtual_memory_max_bytes (-1|[1-9])"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_virtual_memory_bytes [1-9]"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_resident_memory_bytes [1-9]"),
|
||||||
|
regexp.MustCompile("\nfoobar_process_start_time_seconds [0-9.]{10,}"),
|
||||||
|
} {
|
||||||
|
if !re.Match(buf.Bytes()) {
|
||||||
|
t.Errorf("want body to match %s\n%s", re, buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -325,10 +325,18 @@ func (r *Registry) Register(c Collector) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if existing, exists := r.collectorsByID[collectorID]; exists {
|
if existing, exists := r.collectorsByID[collectorID]; exists {
|
||||||
|
switch e := existing.(type) {
|
||||||
|
case *wrappingCollector:
|
||||||
return AlreadyRegisteredError{
|
return AlreadyRegisteredError{
|
||||||
ExistingCollector: existing,
|
ExistingCollector: e.unwrapRecursively(),
|
||||||
NewCollector: c,
|
NewCollector: c,
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return AlreadyRegisteredError{
|
||||||
|
ExistingCollector: e,
|
||||||
|
NewCollector: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If the collectorID is new, but at least one of the descs existed
|
// If the collectorID is new, but at least one of the descs existed
|
||||||
// before, we are in trouble.
|
// before, we are in trouble.
|
||||||
|
|
|
@ -746,11 +746,11 @@ func BenchmarkHandler(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAlreadyRegistered(t *testing.T) {
|
func TestAlreadyRegistered(t *testing.T) {
|
||||||
reg := prometheus.NewRegistry()
|
|
||||||
original := prometheus.NewCounterVec(
|
original := prometheus.NewCounterVec(
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Help: "help",
|
Help: "help",
|
||||||
|
ConstLabels: prometheus.Labels{"const": "label"},
|
||||||
},
|
},
|
||||||
[]string{"foo", "bar"},
|
[]string{"foo", "bar"},
|
||||||
)
|
)
|
||||||
|
@ -758,26 +758,109 @@ func TestAlreadyRegistered(t *testing.T) {
|
||||||
prometheus.CounterOpts{
|
prometheus.CounterOpts{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Help: "help",
|
Help: "help",
|
||||||
|
ConstLabels: prometheus.Labels{"const": "label"},
|
||||||
},
|
},
|
||||||
[]string{"foo", "bar"},
|
[]string{"foo", "bar"},
|
||||||
)
|
)
|
||||||
|
originalWithoutConstLabel := prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "help",
|
||||||
|
},
|
||||||
|
[]string{"foo", "bar"},
|
||||||
|
)
|
||||||
|
equalButNotSameWithoutConstLabel := prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "test",
|
||||||
|
Help: "help",
|
||||||
|
},
|
||||||
|
[]string{"foo", "bar"},
|
||||||
|
)
|
||||||
|
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
originalCollector prometheus.Collector
|
||||||
|
registerWith func(prometheus.Registerer) prometheus.Registerer
|
||||||
|
newCollector prometheus.Collector
|
||||||
|
reRegisterWith func(prometheus.Registerer) prometheus.Registerer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"RegisterNormallyReregisterNormally",
|
||||||
|
original,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer { return r },
|
||||||
|
equalButNotSame,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer { return r },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RegisterNormallyReregisterWrapped",
|
||||||
|
original,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer { return r },
|
||||||
|
equalButNotSameWithoutConstLabel,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer {
|
||||||
|
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RegisterWrappedReregisterWrapped",
|
||||||
|
originalWithoutConstLabel,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer {
|
||||||
|
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
|
||||||
|
},
|
||||||
|
equalButNotSameWithoutConstLabel,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer {
|
||||||
|
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RegisterWrappedReregisterNormally",
|
||||||
|
originalWithoutConstLabel,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer {
|
||||||
|
return prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r)
|
||||||
|
},
|
||||||
|
equalButNotSame,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer { return r },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RegisterDoublyWrappedReregisterDoublyWrapped",
|
||||||
|
originalWithoutConstLabel,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer {
|
||||||
|
return prometheus.WrapRegistererWithPrefix(
|
||||||
|
"wrap_",
|
||||||
|
prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
equalButNotSameWithoutConstLabel,
|
||||||
|
func(r prometheus.Registerer) prometheus.Registerer {
|
||||||
|
return prometheus.WrapRegistererWithPrefix(
|
||||||
|
"wrap_",
|
||||||
|
prometheus.WrapRegistererWith(prometheus.Labels{"const": "label"}, r),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.name, func(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
if err = reg.Register(original); err != nil {
|
reg := prometheus.NewRegistry()
|
||||||
|
if err = s.registerWith(reg).Register(s.originalCollector); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err = reg.Register(equalButNotSame); err == nil {
|
if err = s.reRegisterWith(reg).Register(s.newCollector); err == nil {
|
||||||
t.Fatal("expected error when registering equal collector")
|
t.Fatal("expected error when registering new collector")
|
||||||
}
|
}
|
||||||
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
|
||||||
if are.ExistingCollector != original {
|
if are.ExistingCollector != s.originalCollector {
|
||||||
t.Error("expected original collector but got something else")
|
t.Error("expected original collector but got something else")
|
||||||
}
|
}
|
||||||
if are.ExistingCollector == equalButNotSame {
|
if are.ExistingCollector == s.newCollector {
|
||||||
t.Error("expected original callector but got new one")
|
t.Error("expected original collector but got new one")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.Error("unexpected error:", err)
|
t.Error("unexpected error:", err)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestHistogramVecRegisterGatherConcurrency is an end-to-end test that
|
// TestHistogramVecRegisterGatherConcurrency is an end-to-end test that
|
||||||
|
|
|
@ -32,6 +32,12 @@ import (
|
||||||
// WrapRegistererWith provides a way to add fixed labels to a subset of
|
// WrapRegistererWith provides a way to add fixed labels to a subset of
|
||||||
// Collectors. It should not be used to add fixed labels to all metrics exposed.
|
// Collectors. It should not be used to add fixed labels to all metrics exposed.
|
||||||
//
|
//
|
||||||
|
// Conflicts between Collectors registered through the original Registerer with
|
||||||
|
// Collectors registered through the wrapping Registerer will still be
|
||||||
|
// detected. Any AlreadyRegisteredError returned by the Register method of
|
||||||
|
// either Registerer will contain the ExistingCollector in the form it was
|
||||||
|
// provided to the respective registry.
|
||||||
|
//
|
||||||
// The Collector example demonstrates a use of WrapRegistererWith.
|
// The Collector example demonstrates a use of WrapRegistererWith.
|
||||||
func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
|
func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
|
||||||
return &wrappingRegisterer{
|
return &wrappingRegisterer{
|
||||||
|
@ -54,6 +60,12 @@ func WrapRegistererWith(labels Labels, reg Registerer) Registerer {
|
||||||
// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
|
// (see NewGoCollector) and the process collector (see NewProcessCollector). (In
|
||||||
// fact, those metrics are already prefixed with “go_” or “process_”,
|
// fact, those metrics are already prefixed with “go_” or “process_”,
|
||||||
// respectively.)
|
// respectively.)
|
||||||
|
//
|
||||||
|
// Conflicts between Collectors registered through the original Registerer with
|
||||||
|
// Collectors registered through the wrapping Registerer will still be
|
||||||
|
// detected. Any AlreadyRegisteredError returned by the Register method of
|
||||||
|
// either Registerer will contain the ExistingCollector in the form it was
|
||||||
|
// provided to the respective registry.
|
||||||
func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
|
func WrapRegistererWithPrefix(prefix string, reg Registerer) Registerer {
|
||||||
return &wrappingRegisterer{
|
return &wrappingRegisterer{
|
||||||
wrappedRegisterer: reg,
|
wrappedRegisterer: reg,
|
||||||
|
@ -123,6 +135,15 @@ func (c *wrappingCollector) Describe(ch chan<- *Desc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *wrappingCollector) unwrapRecursively() Collector {
|
||||||
|
switch wc := c.wrappedCollector.(type) {
|
||||||
|
case *wrappingCollector:
|
||||||
|
return wc.unwrapRecursively()
|
||||||
|
default:
|
||||||
|
return wc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type wrappingMetric struct {
|
type wrappingMetric struct {
|
||||||
wrappedMetric Metric
|
wrappedMetric Metric
|
||||||
prefix string
|
prefix string
|
||||||
|
|
Loading…
Reference in New Issue