Skip to content

Commit 5486764

Browse files
authored
Merge pull request #861 from yeya24/add-exemplars
Add exemplars API support
2 parents 0400fc4 + 7a147c1 commit 5486764

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

api/prometheus/v1/api.go

+37
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ const (
123123
epAlertManagers = apiPrefix + "/alertmanagers"
124124
epQuery = apiPrefix + "/query"
125125
epQueryRange = apiPrefix + "/query_range"
126+
epQueryExemplars = apiPrefix + "/query_exemplars"
126127
epLabels = apiPrefix + "/labels"
127128
epLabelValues = apiPrefix + "/label/:name/values"
128129
epSeries = apiPrefix + "/series"
@@ -239,6 +240,8 @@ type API interface {
239240
Query(ctx context.Context, query string, ts time.Time) (model.Value, Warnings, error)
240241
// QueryRange performs a query for the given range.
241242
QueryRange(ctx context.Context, query string, r Range) (model.Value, Warnings, error)
243+
// QueryExemplars performs a query for exemplars by the given query and time range.
244+
QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]ExemplarQueryResult, error)
242245
// Buildinfo returns various build information properties about the Prometheus server
243246
Buildinfo(ctx context.Context) (BuildinfoResult, error)
244247
// Runtimeinfo returns the various runtime information properties about the Prometheus server.
@@ -588,6 +591,18 @@ func (qr *queryResult) UnmarshalJSON(b []byte) error {
588591
return err
589592
}
590593

594+
// Exemplar is additional information associated with a time series.
595+
type Exemplar struct {
596+
Labels model.LabelSet `json:"labels"`
597+
Value model.SampleValue `json:"value"`
598+
Timestamp model.Time `json:"timestamp"`
599+
}
600+
601+
type ExemplarQueryResult struct {
602+
SeriesLabels model.LabelSet `json:"seriesLabels"`
603+
Exemplars []Exemplar `json:"exemplars"`
604+
}
605+
591606
// NewAPI returns a new API for the client.
592607
//
593608
// It is safe to use the returned API from multiple goroutines.
@@ -967,7 +982,29 @@ func (h *httpAPI) TSDB(ctx context.Context) (TSDBResult, error) {
967982

968983
var res TSDBResult
969984
return res, json.Unmarshal(body, &res)
985+
}
986+
987+
func (h *httpAPI) QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]ExemplarQueryResult, error) {
988+
u := h.client.URL(epQueryExemplars, nil)
989+
q := u.Query()
990+
991+
q.Set("query", query)
992+
q.Set("start", formatTime(startTime))
993+
q.Set("end", formatTime(endTime))
994+
u.RawQuery = q.Encode()
970995

996+
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
997+
if err != nil {
998+
return nil, err
999+
}
1000+
1001+
_, body, _, err := h.client.Do(ctx, req)
1002+
if err != nil {
1003+
return nil, err
1004+
}
1005+
1006+
var res []ExemplarQueryResult
1007+
return res, json.Unmarshal(body, &res)
9711008
}
9721009

9731010
// Warnings is an array of non critical errors

api/prometheus/v1/api_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,13 @@ func TestAPIs(t *testing.T) {
230230
}
231231
}
232232

233+
doQueryExemplars := func(query string, startTime time.Time, endTime time.Time) func() (interface{}, Warnings, error) {
234+
return func() (interface{}, Warnings, error) {
235+
v, err := promAPI.QueryExemplars(context.Background(), query, startTime, endTime)
236+
return v, nil, err
237+
}
238+
}
239+
233240
queryTests := []apiTest{
234241
{
235242
do: doQuery("2", testTime),
@@ -1190,6 +1197,66 @@ func TestAPIs(t *testing.T) {
11901197
},
11911198
},
11921199
},
1200+
1201+
{
1202+
do: doQueryExemplars("tns_request_duration_seconds_bucket", testTime.Add(-1*time.Minute), testTime),
1203+
reqMethod: "GET",
1204+
reqPath: "/api/v1/query_exemplars",
1205+
inErr: fmt.Errorf("some error"),
1206+
err: fmt.Errorf("some error"),
1207+
},
1208+
1209+
{
1210+
do: doQueryExemplars("tns_request_duration_seconds_bucket", testTime.Add(-1*time.Minute), testTime),
1211+
reqMethod: "GET",
1212+
reqPath: "/api/v1/query_exemplars",
1213+
inRes: []interface{}{
1214+
map[string]interface{}{
1215+
"seriesLabels": map[string]interface{}{
1216+
"__name__": "tns_request_duration_seconds_bucket",
1217+
"instance": "app:80",
1218+
"job": "tns/app",
1219+
},
1220+
"exemplars": []interface{}{
1221+
map[string]interface{}{
1222+
"labels": map[string]interface{}{
1223+
"traceID": "19fd8c8a33975a23",
1224+
},
1225+
"value": "0.003863295",
1226+
"timestamp": model.TimeFromUnixNano(testTime.UnixNano()),
1227+
},
1228+
map[string]interface{}{
1229+
"labels": map[string]interface{}{
1230+
"traceID": "67f743f07cc786b0",
1231+
},
1232+
"value": "0.001535405",
1233+
"timestamp": model.TimeFromUnixNano(testTime.UnixNano()),
1234+
},
1235+
},
1236+
},
1237+
},
1238+
res: []ExemplarQueryResult{
1239+
{
1240+
SeriesLabels: model.LabelSet{
1241+
"__name__": "tns_request_duration_seconds_bucket",
1242+
"instance": "app:80",
1243+
"job": "tns/app",
1244+
},
1245+
Exemplars: []Exemplar{
1246+
{
1247+
Labels: model.LabelSet{"traceID": "19fd8c8a33975a23"},
1248+
Value: 0.003863295,
1249+
Timestamp: model.TimeFromUnixNano(testTime.UnixNano()),
1250+
},
1251+
{
1252+
Labels: model.LabelSet{"traceID": "67f743f07cc786b0"},
1253+
Value: 0.001535405,
1254+
Timestamp: model.TimeFromUnixNano(testTime.UnixNano()),
1255+
},
1256+
},
1257+
},
1258+
},
1259+
},
11931260
}
11941261

11951262
var tests []apiTest

0 commit comments

Comments
 (0)