在使用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裏面就會出現: