Merge pull request #2007 from huanggze/dev-custom
feat: custom monitoring
This commit is contained in:
@@ -3,8 +3,9 @@ package monitoring
|
||||
import "time"
|
||||
|
||||
type Interface interface {
|
||||
GetMetrics(exprs []string, time time.Time) []Metric
|
||||
GetMetricsOverTime(exprs []string, start, end time.Time, step time.Duration) []Metric
|
||||
GetMetric(expr string, time time.Time) Metric
|
||||
GetMetricOverTime(expr string, start, end time.Time, step time.Duration) Metric
|
||||
GetNamedMetrics(metrics []string, time time.Time, opt QueryOption) []Metric
|
||||
GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt QueryOption) []Metric
|
||||
GetMetadata(namespace string) []Metadata
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package prometheus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/prometheus/client_golang/api"
|
||||
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
"github.com/prometheus/common/model"
|
||||
@@ -24,14 +25,35 @@ func NewPrometheus(options *Options) (monitoring.Interface, error) {
|
||||
return prometheus{client: apiv1.NewAPI(client)}, err
|
||||
}
|
||||
|
||||
// TODO(huanggze): reserve for custom monitoring
|
||||
func (p prometheus) GetMetrics(stmts []string, time time.Time) []monitoring.Metric {
|
||||
panic("implement me")
|
||||
func (p prometheus) GetMetric(expr string, ts time.Time) monitoring.Metric {
|
||||
var parsedResp monitoring.Metric
|
||||
|
||||
value, err := p.client.Query(context.Background(), expr, ts)
|
||||
if err != nil {
|
||||
parsedResp.Error = err.Error()
|
||||
} else {
|
||||
parsedResp.MetricData = parseQueryResp(value)
|
||||
}
|
||||
|
||||
return parsedResp
|
||||
}
|
||||
|
||||
// TODO(huanggze): reserve for custom monitoring
|
||||
func (p prometheus) GetMetricsOverTime(stmts []string, start, end time.Time, step time.Duration) []monitoring.Metric {
|
||||
panic("implement me")
|
||||
func (p prometheus) GetMetricOverTime(expr string, start, end time.Time, step time.Duration) monitoring.Metric {
|
||||
timeRange := apiv1.Range{
|
||||
Start: start,
|
||||
End: end,
|
||||
Step: step,
|
||||
}
|
||||
|
||||
value, err := p.client.QueryRange(context.Background(), expr, timeRange)
|
||||
|
||||
var parsedResp monitoring.Metric
|
||||
if err != nil {
|
||||
parsedResp.Error = err.Error()
|
||||
} else {
|
||||
parsedResp.MetricData = parseQueryRangeResp(value)
|
||||
}
|
||||
return parsedResp
|
||||
}
|
||||
|
||||
func (p prometheus) GetNamedMetrics(metrics []string, ts time.Time, o monitoring.QueryOption) []monitoring.Metric {
|
||||
@@ -49,7 +71,7 @@ func (p prometheus) GetNamedMetrics(metrics []string, ts time.Time, o monitoring
|
||||
|
||||
value, err := p.client.Query(context.Background(), makeExpr(metric, *opts), ts)
|
||||
if err != nil {
|
||||
parsedResp.Error = err.(*apiv1.Error).Msg
|
||||
parsedResp.Error = err.Error()
|
||||
} else {
|
||||
parsedResp.MetricData = parseQueryResp(value)
|
||||
}
|
||||
@@ -88,7 +110,7 @@ func (p prometheus) GetNamedMetricsOverTime(metrics []string, start, end time.Ti
|
||||
|
||||
value, err := p.client.QueryRange(context.Background(), makeExpr(metric, *opts), timeRange)
|
||||
if err != nil {
|
||||
parsedResp.Error = err.(*apiv1.Error).Msg
|
||||
parsedResp.Error = err.Error()
|
||||
} else {
|
||||
parsedResp.MetricData = parseQueryRangeResp(value)
|
||||
}
|
||||
@@ -106,6 +128,26 @@ func (p prometheus) GetNamedMetricsOverTime(metrics []string, start, end time.Ti
|
||||
return res
|
||||
}
|
||||
|
||||
func (p prometheus) GetMetadata(namespace string) []monitoring.Metadata {
|
||||
var meta []monitoring.Metadata
|
||||
|
||||
// Filter metrics available to members of this namespace
|
||||
matchTarget := fmt.Sprintf("{namespace=\"%s\"}", namespace)
|
||||
items, err := p.client.TargetsMetadata(context.Background(), matchTarget, "", "")
|
||||
if err != nil {
|
||||
return meta
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
meta = append(meta, monitoring.Metadata{
|
||||
Metric: item.Metric,
|
||||
Type: string(item.Type),
|
||||
Help: item.Help,
|
||||
})
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
func parseQueryRangeResp(value model.Value) monitoring.MetricData {
|
||||
res := monitoring.MetricData{MetricType: monitoring.MetricTypeMatrix}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ func TestGetNamedMetrics(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
expected, err := jsonFromFile(tt.expected)
|
||||
expected := make([]monitoring.Metric, 0)
|
||||
err := jsonFromFile(tt.expected, &expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -53,7 +54,8 @@ func TestGetNamedMetricsOverTime(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
expected, err := jsonFromFile(tt.expected)
|
||||
expected := make([]monitoring.Metric, 0)
|
||||
err := jsonFromFile(tt.expected, &expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -70,6 +72,44 @@ func TestGetNamedMetricsOverTime(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMetadata(t *testing.T) {
|
||||
tests := []struct {
|
||||
fakeResp string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
fakeResp: "metadata-prom.json",
|
||||
expected: "metadata-res.json",
|
||||
},
|
||||
{
|
||||
fakeResp: "metadata-notfound-prom.json",
|
||||
expected: "metadata-notfound-res.json",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
expected := make([]monitoring.Metadata, 0)
|
||||
err := jsonFromFile(tt.expected, &expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(expected) == 0 {
|
||||
expected = nil
|
||||
}
|
||||
|
||||
srv := mockPrometheusService("/api/v1/targets/metadata", tt.fakeResp)
|
||||
defer srv.Close()
|
||||
|
||||
client, _ := NewPrometheus(&Options{Endpoint: srv.URL})
|
||||
result := client.GetMetadata("default")
|
||||
if diff := cmp.Diff(result, expected); diff != "" {
|
||||
t.Fatalf("%T differ (-got, +want): %s", expected, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mockPrometheusService(pattern, fakeResp string) *httptest.Server {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(pattern, func(res http.ResponseWriter, req *http.Request) {
|
||||
@@ -79,17 +119,15 @@ func mockPrometheusService(pattern, fakeResp string) *httptest.Server {
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func jsonFromFile(expectedFile string) ([]monitoring.Metric, error) {
|
||||
expectedJson := []monitoring.Metric{}
|
||||
|
||||
func jsonFromFile(expectedFile string, expectedJsonPtr interface{}) error {
|
||||
json, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s", expectedFile))
|
||||
if err != nil {
|
||||
return expectedJson, err
|
||||
return err
|
||||
}
|
||||
err = jsoniter.Unmarshal(json, &expectedJson)
|
||||
err = jsoniter.Unmarshal(json, expectedJsonPtr)
|
||||
if err != nil {
|
||||
return expectedJson, err
|
||||
return err
|
||||
}
|
||||
|
||||
return expectedJson, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
5
pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-prom.json
vendored
Normal file
5
pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-prom.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"status":"error",
|
||||
"errorType":"not_found",
|
||||
"error":"specified metadata not found"
|
||||
}
|
||||
1
pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-res.json
vendored
Normal file
1
pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-res.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
25
pkg/simple/client/monitoring/prometheus/testdata/metadata-prom.json
vendored
Normal file
25
pkg/simple/client/monitoring/prometheus/testdata/metadata-prom.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"status": "success",
|
||||
"data": [
|
||||
{
|
||||
"target": {
|
||||
"instance": "127.0.0.1:9090",
|
||||
"job": "prometheus"
|
||||
},
|
||||
"metric": "prometheus_treecache_zookeeper_failures_total",
|
||||
"type": "counter",
|
||||
"help": "The total number of ZooKeeper failures.",
|
||||
"unit": ""
|
||||
},
|
||||
{
|
||||
"target": {
|
||||
"instance": "127.0.0.1:9090",
|
||||
"job": "prometheus"
|
||||
},
|
||||
"metric": "prometheus_tsdb_reloads_total",
|
||||
"type": "counter",
|
||||
"help": "Number of times the database reloaded block data from disk.",
|
||||
"unit": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
12
pkg/simple/client/monitoring/prometheus/testdata/metadata-res.json
vendored
Normal file
12
pkg/simple/client/monitoring/prometheus/testdata/metadata-res.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"metric": "prometheus_treecache_zookeeper_failures_total",
|
||||
"type": "counter",
|
||||
"help": "The total number of ZooKeeper failures."
|
||||
},
|
||||
{
|
||||
"metric": "prometheus_tsdb_reloads_total",
|
||||
"type": "counter",
|
||||
"help": "Number of times the database reloaded block data from disk."
|
||||
}
|
||||
]
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"metric_name": "cluster_cpu_utilisation",
|
||||
"error": "inconsistent body for response code"
|
||||
"error": "bad_response: inconsistent body for response code"
|
||||
}
|
||||
]
|
||||
@@ -5,6 +5,12 @@ const (
|
||||
MetricTypeVector = "vector"
|
||||
)
|
||||
|
||||
type Metadata struct {
|
||||
Metric string `json:"metric,omitempty" description:"metric name"`
|
||||
Type string `json:"type,omitempty" description:"metric type"`
|
||||
Help string `json:"help,omitempty" description:"metric description"`
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
MetricName string `json:"metric_name,omitempty" description:"metric name, eg. scheduler_up_sum"`
|
||||
MetricData `json:"data,omitempty" description:"actual metric result"`
|
||||
|
||||
Reference in New Issue
Block a user