diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index 06319a89f..fc375691d 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -130,6 +130,7 @@ const ( epSeries = apiPrefix + "/series" epTargets = apiPrefix + "/targets" epTargetsMetadata = apiPrefix + "/targets/metadata" + epMetricsMetadata = apiPrefix + "/metadata" epRules = apiPrefix + "/rules" epSnapshot = apiPrefix + "/admin/tsdb/snapshot" epDeleteSeries = apiPrefix + "/admin/tsdb/delete_series" @@ -248,6 +249,8 @@ type API interface { Targets(ctx context.Context) (TargetsResult, error) // TargetsMetadata returns metadata about metrics currently scraped by the target. TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, error) + // MetricMetadata returns metadata about metrics currently scraped by the metric name. + MetricsMetadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) } // AlertsResult contains the result from querying the alerts endpoint. @@ -357,7 +360,7 @@ type DroppedTarget struct { DiscoveredLabels map[string]string `json:"discoveredLabels"` } -// MetricMetadata models the metadata of a metric. +// MetricMetadata models the metadata of a metric with its scrape target and name. type MetricMetadata struct { Target map[string]string `json:"target"` Metric string `json:"metric,omitempty"` @@ -366,6 +369,13 @@ type MetricMetadata struct { Unit string `json:"unit"` } +// Metadata models the metadata of a metric. +type Metadata struct { + Type MetricType `json:"type"` + Help string `json:"help"` + Unit string `json:"unit"` +} + // queryResult contains result data for a query. type queryResult struct { Type model.ValueType `json:"resultType"` @@ -802,6 +812,29 @@ func (h *httpAPI) TargetsMetadata(ctx context.Context, matchTarget string, metri return res, json.Unmarshal(body, &res) } +func (h *httpAPI) MetricsMetadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) { + u := h.client.URL(epMetricsMetadata, nil) + q := u.Query() + + q.Set("metric", metric) + q.Set("limit", limit) + + u.RawQuery = q.Encode() + + 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 res map[string][]Metadata + return res, json.Unmarshal(body, &res) +} + // Warnings is an array of non critical errors type Warnings []string diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index b43318e65..21ffa7b9d 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -202,6 +202,13 @@ func TestAPIs(t *testing.T) { } } + doMetricsMetadata := func(metring string, limit string) func() (interface{}, Warnings, error) { + return func() (interface{}, Warnings, error) { + v, err := promAPI.MetricsMetadata(context.Background(), metring, limit) + return v, nil, err + } + } + queryTests := []apiTest{ { do: doQuery("2", testTime), @@ -857,6 +864,46 @@ func TestAPIs(t *testing.T) { }, err: fmt.Errorf("some error"), }, + + { + do: doMetricsMetadata("go_goroutines", "1"), + inRes: map[string]interface{}{ + "go_goroutines": []map[string]interface{}{ + { + "type": "gauge", + "help": "Number of goroutines that currently exist.", + "unit": "", + }, + }, + }, + reqMethod: "GET", + reqPath: "/api/v1/metadata", + reqParam: url.Values{ + "metric": []string{"go_goroutines"}, + "limit": []string{"1"}, + }, + res: map[string][]Metadata{ + "go_goroutines": []Metadata{ + { + Type: "gauge", + Help: "Number of goroutines that currently exist.", + Unit: "", + }, + }, + }, + }, + + { + do: doMetricsMetadata("", "1"), + inErr: fmt.Errorf("some error"), + reqMethod: "GET", + reqPath: "/api/v1/metadata", + reqParam: url.Values{ + "metric": []string{""}, + "limit": []string{"1"}, + }, + err: fmt.Errorf("some error"), + }, } var tests []apiTest diff --git a/test.go b/test.go new file mode 100644 index 000000000..9030be533 --- /dev/null +++ b/test.go @@ -0,0 +1,41 @@ +package main + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/prometheus/client_golang/api" + v1 "github.com/prometheus/client_golang/api/prometheus/v1" +) + +func main() { + client, err := api.NewClient(api.Config{ + Address: "http://localhost:9091", + }) + if err != nil { + fmt.Printf("Error creating client: %v\n", err) + os.Exit(1) + } + + v1api := v1.NewAPI(client) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + metadata, err := v1api.MetricsMetadata(ctx, "", "") + if err != nil { + fmt.Printf("Error querying Prometheus: %v\n", err) + os.Exit(1) + } + fmt.Println("Result:") + for metric, metadata := range metadata { + fmt.Println(metric) + for _, m := range metadata { + fmt.Println(m.Type) + fmt.Println(m.Help) + fmt.Println(m.Unit) + fmt.Println("\n\n") + } + } + +}