在使用prometheus的query_range接口查询metrics的时候,prometheus返回的数据格式如下
{
"status" : "success",
"data" : {
"resultType" : "matrix",
"result" : [
{
"metric" : {
"__name__" : "up",
"job" : "prometheus",
"instance" : "localhost:9090"
},
"values" : [
[ 1435781430.781, "1" ],
[ 1435781445.781, "1" ],
[ 1435781460.781, "1" ]
]
},
{
"metric" : {
"__name__" : "up",
"job" : "node",
"instance" : "localhost:9091"
},
"values" : [
[ 1435781430.781, "0" ],
[ 1435781445.781, "0" ],
[ 1435781460.781, "1" ]
]
}
]
}
}
其他字段的处理都很好办,但是到了values字段的时候就有点难搞了。
首先定义结构体接收原始数据。
我把values字段定义成了[][]interface,方便后续的处理。
type RawMetrics struct {
Status string `json:"status"`
Data Data `json:"data"`
}
type Data struct {
ResultType string `json:"resultType"`
Result []Result `json:"result"`
}
type Result struct {
Metric Metric `json:"metric"`
Values [][]interface{} `json:"values"`
}
type Metric struct {
Name string `json:"__name__"`
Endpoint string `json:"endpoint"`
Instance string `json:"instance"`
Job string `json:"job"`
Namespace string `json:"namespace"`
Pod string `json:"pod"`
Service string `json:"service"`
}
单元测试
func TestQueryMetrics(t *testing.T) {
url := "http://10.10.13.27:30900/api/v1/query_range?query=up&step=1&start=1584928541&end=1584928542"
resp, err := http.Get(url)
assert.Equal(t, nil, err)
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
assert.Equal(t, nil, err)
assert.Equal(t, resp.StatusCode, http.StatusOK)
var dst RawMetrics
err = ParseJsonFromBytes(data, &dst)
assert.Equal(t, nil, err)
}
func ParseJsonFromBytes(b []byte, result interface{}) error {
return jsoniter.Unmarshal(b, result)
}
将原始数据转换成需要的字段,其实主要是对values字段的处理。当需要得到values字段里面的每个数据时,就需要使用断言进行处理。
type Metrics struct {
Name string `json:"name"`
Endpoint string `json:"endpoint"`
Instance string `json:"instance"`
Job string `json:"job"`
Namespace string `json:"namespace"`
Pod string `json:"pod"`
Service string `json:"service"`
Values []Value `json:"values"`
}
type Value struct {
Timestamp float64 `json:"timestamp"`
Value string `json:"value"`
}
完整代码
func TestQueryMetrics(t *testing.T) {
url := "http://10.10.13.27:30900/api/v1/query_range?query=up&step=1&start=1584928541&end=1584928542"
resp, err := http.Get(url)
assert.Equal(t, nil, err)
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
assert.Equal(t, nil, err)
assert.Equal(t, resp.StatusCode, http.StatusOK)
var dst RawMetrics
err = ParseJsonFromBytes(data, &dst)
assert.Equal(t, nil, err)
// 以上是获取原始数据的操作
// 对原始数据进行转换
var metrics []Metrics
for _, result := range dst.Data.Result {
var metric Metrics
metric.Name = result.Metric.Name
metric.Namespace = result.Metric.Namespace
metric.Pod = result.Metric.Pod
metric.Endpoint = result.Metric.Endpoint
metric.Job = result.Metric.Job
metric.Instance = result.Metric.Instance
metric.Service = result.Metric.Service
for _, rawValue := range result.Values {
// 注意value必须在这里定义,否则后面会出现timestamp为0时value不为空的情况
var value Value
for _, raw := range rawValue {
switch raw.(type) {
case float64:
ts := raw.(float64)
value.Timestamp = ts
case string:
v := raw.(string)
value.Value = v
}
}
metric.Values = append(metric.Values, value)
}
metrics = append(metrics, metric)
}
fmt.Println("metrics >> ",Obj2Json(metrics))
}
如果var value Value位置放的不对,放在了for _, raw := range rawValue里面就会出现: