Skip to content

Commit cc1d8af

Browse files
authored
feat(datasource): add prometheus metric metadata endpoints (#264)
为「数据分析-指标查询」的指标浏览器提供元数据查询能力, 降低手写 PromQL 的门槛: - metricNames: 转发 /api/v1/label/__name__/values, 返回全部指标名 - labels: 转发 /api/v1/labels, 返回 label 名(可选 match[] 限缩到某指标) - labelValues: 转发 /api/v1/label/{name}/values, 返回 label 取值(可选 match[]) 统一走 promMetadataGet, 复用数据源鉴权头(Bearer/Basic)与出站 SSRF 校验。
1 parent be3c46b commit cc1d8af

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

‎api/datasource.go‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ func (datasourceController datasourceController) API(gin *gin.RouterGroup) {
6262
c.POST("searchLogs", datasourceController.SearchLogs)
6363
c.GET("query", datasourceController.PromQuery)
6464
c.GET("queryRange", datasourceController.PromQueryRange)
65+
c.GET("metricNames", datasourceController.PromMetricNames)
66+
c.GET("labels", datasourceController.PromLabels)
67+
c.GET("labelValues", datasourceController.PromLabelValues)
6568
}
6669

6770
}
@@ -231,6 +234,72 @@ func (datasourceController datasourceController) PromQueryRange(ctx *gin.Context
231234
})
232235
}
233236

237+
// promMetadataGet 转发 Prometheus 元数据查询(/api/v1/label/*), 供「指标浏览器」使用。
238+
// 复用数据源鉴权头(Bearer/Basic)与出站 SSRF 校验。
239+
func promMetadataGet(datasourceId, pathSuffix string, params url.Values) (interface{}, error) {
240+
if datasourceId == "" {
241+
return nil, fmt.Errorf("datasourceId 不能为空")
242+
}
243+
source, err := ctx2.DO().DB.Datasource().Get(datasourceId)
244+
if err != nil {
245+
return nil, err
246+
}
247+
if !source.GetEnabled() {
248+
return nil, fmt.Errorf("数据源「%s」已被禁用!", source.Name)
249+
}
250+
251+
fullURL := fmt.Sprintf("%s%s", source.HTTP.URL, pathSuffix)
252+
if len(params) > 0 {
253+
fullURL += "?" + params.Encode()
254+
}
255+
256+
headers := tools.CreateBasicAuthHeader(source.Auth.User, source.Auth.Pass)
257+
headers = tools.MergeHeaders(headers, source.HTTP.Headers)
258+
get, err := tools.Get(headers, fullURL, 10)
259+
if err != nil {
260+
return nil, err
261+
}
262+
263+
var res map[string]interface{}
264+
if err := tools.ParseReaderBody(get.Body, &res); err != nil {
265+
return nil, err
266+
}
267+
return res["data"], nil
268+
}
269+
270+
// PromMetricNames 返回数据源全部指标名(基于 __name__ label)。
271+
func (datasourceController datasourceController) PromMetricNames(ctx *gin.Context) {
272+
Service(ctx, func() (interface{}, interface{}) {
273+
return promMetadataGet(ctx.Query("datasourceId"), "/api/v1/label/__name__/values", nil)
274+
})
275+
}
276+
277+
// PromLabels 返回数据源 label 名(可选 match[] 限缩到某指标)。
278+
func (datasourceController datasourceController) PromLabels(ctx *gin.Context) {
279+
Service(ctx, func() (interface{}, interface{}) {
280+
params := url.Values{}
281+
if m := ctx.Query("match"); m != "" {
282+
params.Add("match[]", m)
283+
}
284+
return promMetadataGet(ctx.Query("datasourceId"), "/api/v1/labels", params)
285+
})
286+
}
287+
288+
// PromLabelValues 返回指定 label 的取值(可选 match[] 限缩到某指标)。
289+
func (datasourceController datasourceController) PromLabelValues(ctx *gin.Context) {
290+
Service(ctx, func() (interface{}, interface{}) {
291+
name := ctx.Query("name")
292+
if name == "" {
293+
return nil, fmt.Errorf("label name 不能为空")
294+
}
295+
params := url.Values{}
296+
if m := ctx.Query("match"); m != "" {
297+
params.Add("match[]", m)
298+
}
299+
return promMetadataGet(ctx.Query("datasourceId"), "/api/v1/label/"+name+"/values", params)
300+
})
301+
}
302+
234303
func (datasourceController datasourceController) Ping(ctx *gin.Context) {
235304
r := new(types.RequestDatasourceCreate)
236305
BindJson(ctx, r)

0 commit comments

Comments
 (0)