console/modules/elastic/api/metrics_util.go

1194 lines
34 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (C) INFINI Labs & INFINI LIMITED.
//
// The INFINI Console is offered under the GNU Affero General Public License v3.0
// and as commercial software.
//
// For commercial licensing, contact us at:
// - Website: infinilabs.com
// - Email: hello@infini.ltd
//
// Open Source licensed under AGPL V3:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package api
import (
"context"
"fmt"
"infini.sh/framework/core/env"
"strings"
"time"
log "github.com/cihub/seelog"
"infini.sh/framework/core/elastic"
"infini.sh/framework/core/global"
"infini.sh/framework/core/util"
"infini.sh/framework/modules/elastic/common"
)
func newMetricItem(metricKey string, order int, group string) *common.MetricItem {
metricItem := common.MetricItem{
Order: order,
Key: metricKey,
Group: group,
}
//axis
metricItem.Axis = []*common.MetricAxis{}
//lines
metricItem.Lines = []*common.MetricLine{}
return &metricItem
}
type GroupMetricItem struct {
Key string
Field string
ID string
IsDerivative bool
Units string
FormatType string
MetricItem *common.MetricItem
Field2 string
Calc func(value, value2 float64) float64
}
type TreeMapNode struct {
Name string `json:"name"`
Value float64 `json:"value,omitempty"`
Children []*TreeMapNode `json:"children,omitempty"`
SubKeys map[string]int `json:"-"`
}
type MetricData map[string][][]interface{}
func generateGroupAggs(nodeMetricItems []GroupMetricItem) map[string]interface{} {
aggs := map[string]interface{}{}
for _, metricItem := range nodeMetricItems {
aggs[metricItem.ID] = util.MapStr{
"max": util.MapStr{
"field": metricItem.Field,
},
}
if metricItem.Field2 != "" {
aggs[metricItem.ID+"_field2"] = util.MapStr{
"max": util.MapStr{
"field": metricItem.Field2,
},
}
}
if metricItem.IsDerivative {
aggs[metricItem.ID+"_deriv"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": metricItem.ID,
},
}
if metricItem.Field2 != "" {
aggs[metricItem.ID+"_deriv_field2"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": metricItem.ID + "_field2",
},
}
}
}
}
return aggs
}
func (h *APIHandler) getMetrics(ctx context.Context, query map[string]interface{}, grpMetricItems []GroupMetricItem, bucketSize int) (map[string]*common.MetricItem, error) {
bucketSizeStr := fmt.Sprintf("%vs", bucketSize)
queryDSL := util.MustToJSONBytes(query)
response, err := elastic.GetClient(global.MustLookupString(elastic.GlobalSystemElasticsearchID)).QueryDSL(ctx, getAllMetricsIndex(), nil, queryDSL)
if err != nil {
return nil, err
}
grpMetricItemsIndex := map[string]int{}
for i, item := range grpMetricItems {
grpMetricItemsIndex[item.ID] = i
}
grpMetricData := map[string]MetricData{}
var minDate, maxDate int64
if response.StatusCode == 200 {
if nodeAgg, ok := response.Aggregations["group_by_level"]; ok {
for _, bucket := range nodeAgg.Buckets {
grpKey := bucket["key"].(string)
for _, metricItem := range grpMetricItems {
metricItem.MetricItem.AddLine(metricItem.Key, grpKey, "", "group1", metricItem.Field, "max", bucketSizeStr, metricItem.Units, metricItem.FormatType, "0.[00]", "0.[00]", false, false)
dataKey := metricItem.ID
if metricItem.IsDerivative {
dataKey = dataKey + "_deriv"
}
if _, ok := grpMetricData[dataKey]; !ok {
grpMetricData[dataKey] = map[string][][]interface{}{}
}
grpMetricData[dataKey][grpKey] = [][]interface{}{}
}
if datesAgg, ok := bucket["dates"].(map[string]interface{}); ok {
if datesBuckets, ok := datesAgg["buckets"].([]interface{}); ok {
for _, dateBucket := range datesBuckets {
if bucketMap, ok := dateBucket.(map[string]interface{}); ok {
v, ok := bucketMap["key"].(float64)
if !ok {
return nil, fmt.Errorf("invalid bucket key type: %T", bucketMap["key"])
}
dateTime := int64(v)
minDate = util.MinInt64(minDate, dateTime)
maxDate = util.MaxInt64(maxDate, dateTime)
for mk1, mv1 := range grpMetricData {
v1, ok := bucketMap[mk1]
if ok {
v2, ok := v1.(map[string]interface{})
if ok {
v3, ok := v2["value"].(float64)
if ok {
metricID := mk1
if strings.HasSuffix(mk1, "_deriv") {
metricID = strings.TrimSuffix(mk1, "_deriv")
if _, ok := bucketMap[mk1+"_field2"]; !ok {
v3 = v3 / float64(bucketSize)
}
}
if field2, ok := bucketMap[mk1+"_field2"]; ok {
if idx, ok := grpMetricItemsIndex[metricID]; ok {
if field2Map, ok := field2.(map[string]interface{}); ok {
v4 := field2Map["value"].(float64)
if v4 == 0 {
v3 = 0
} else {
v3 = grpMetricItems[idx].Calc(v3, v4)
}
}
}
}
if v3 < 0 {
continue
}
points := []interface{}{dateTime, v3}
mv1[grpKey] = append(mv1[grpKey], points)
}
}
}
}
}
}
}
}
}
}
}
result := map[string]*common.MetricItem{}
hitsTotal := response.GetTotal()
for _, metricItem := range grpMetricItems {
for _, line := range metricItem.MetricItem.Lines {
line.TimeRange = common.TimeRange{Min: minDate, Max: maxDate}
dataKey := metricItem.ID
if metricItem.IsDerivative {
dataKey = dataKey + "_deriv"
}
line.Data = grpMetricData[dataKey][line.Metric.Label]
if v, ok := line.Data.([][]interface{}); ok && len(v) > 0 && bucketSize <= 60 {
// remove first metric dot
temp := v[1:]
// // remove first last dot
if len(temp) > 0 {
temp = temp[0 : len(temp)-1]
}
line.Data = temp
}
}
metricItem.MetricItem.Request = string(queryDSL)
metricItem.MetricItem.HitsTotal = hitsTotal
result[metricItem.Key] = metricItem.MetricItem
}
return result, nil
}
func GetMinBucketSize() int {
metricsCfg := struct {
MinBucketSizeInSeconds int `config:"min_bucket_size_in_seconds"`
}{
MinBucketSizeInSeconds: 20,
}
_, _ = env.ParseConfig("insight", &metricsCfg)
if metricsCfg.MinBucketSizeInSeconds < 20 {
metricsCfg.MinBucketSizeInSeconds = 20
}
return metricsCfg.MinBucketSizeInSeconds
}
func GetMetricRangeAndBucketSize(minStr string, maxStr string, bucketSize int, metricCount int) (int, int64, int64, error) {
var min, max int64
var rangeFrom, rangeTo time.Time
var err error
var useMinMax = bucketSize == 0
now := time.Now()
if minStr == "" {
rangeFrom = now.Add(-time.Second * time.Duration(bucketSize*metricCount+1))
} else {
//try 2021-08-21T14:06:04.818Z
rangeFrom, err = util.ParseStandardTime(minStr)
if err != nil {
//try 1629637500000
v, err := util.ToInt64(minStr)
if err != nil {
log.Error("invalid timestamp:", minStr, err)
rangeFrom = now.Add(-time.Second * time.Duration(bucketSize*metricCount+1))
} else {
rangeFrom = util.FromUnixTimestamp(v / 1000)
}
}
}
if maxStr == "" {
rangeTo = now.Add(-time.Second * time.Duration(int(1*(float64(bucketSize)))))
} else {
rangeTo, err = util.ParseStandardTime(maxStr)
if err != nil {
v, err := util.ToInt64(maxStr)
if err != nil {
log.Error("invalid timestamp:", maxStr, err)
rangeTo = now.Add(-time.Second * time.Duration(int(1*(float64(bucketSize)))))
} else {
rangeTo = util.FromUnixTimestamp(int64(v) / 1000)
}
}
}
min = rangeFrom.UnixNano() / 1e6
max = rangeTo.UnixNano() / 1e6
hours := rangeTo.Sub(rangeFrom).Hours()
if useMinMax {
if hours <= 0.25 {
bucketSize = GetMinBucketSize()
} else if hours <= 0.5 {
bucketSize = 30
} else if hours <= 2 {
bucketSize = 60
} else if hours < 3 {
bucketSize = 90
} else if hours < 6 {
bucketSize = 120
} else if hours < 12 {
bucketSize = 60 * 3
} else if hours < 25 { //1day
bucketSize = 60 * 5 * 2
} else if hours <= 7*24+1 { //7days
bucketSize = 60 * 15 * 2
} else if hours <= 15*24+1 { //15days
bucketSize = 60 * 30 * 2
} else if hours < 30*24+1 { //<30 days
bucketSize = 60 * 60 //hourly
} else if hours <= 30*24+1 { //<30days
bucketSize = 12 * 60 * 60 //half daily
} else if hours >= 30*24+1 { //>30days
bucketSize = 60 * 60 * 24 //daily bucket
}
}
return bucketSize, min, max, nil
}
// 获取单个指标,可以包含多条曲线
func (h *APIHandler) getSingleMetrics(ctx context.Context, metricItems []*common.MetricItem, query map[string]interface{}, bucketSize int) (map[string]*common.MetricItem, error) {
metricData := map[string][][]interface{}{}
aggs := map[string]interface{}{}
metricItemsMap := map[string]*common.MetricLine{}
for _, metricItem := range metricItems {
for _, line := range metricItem.Lines {
metricItemsMap[line.Metric.GetDataKey()] = line
metricData[line.Metric.GetDataKey()] = [][]interface{}{}
aggs[line.Metric.ID] = util.MapStr{
line.Metric.MetricAgg: util.MapStr{
"field": line.Metric.Field,
},
}
if line.Metric.Field2 != "" {
aggs[line.Metric.ID+"_field2"] = util.MapStr{
line.Metric.MetricAgg: util.MapStr{
"field": line.Metric.Field2,
},
}
}
if line.Metric.IsDerivative {
//add which metric keys to extract
aggs[line.Metric.ID+"_deriv"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID,
},
}
if line.Metric.Field2 != "" {
aggs[line.Metric.ID+"_deriv_field2"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID + "_field2",
},
}
}
}
}
}
bucketSizeStr := fmt.Sprintf("%vs", bucketSize)
clusterID := global.MustLookupString(elastic.GlobalSystemElasticsearchID)
intervalField, err := getDateHistogramIntervalField(clusterID, bucketSizeStr)
if err != nil {
return nil, err
}
query["size"] = 0
query["aggs"] = util.MapStr{
"dates": util.MapStr{
"date_histogram": util.MapStr{
"field": "timestamp",
intervalField: bucketSizeStr,
},
"aggs": aggs,
},
}
queryDSL := util.MustToJSONBytes(query)
response, err := elastic.GetClient(clusterID).QueryDSL(ctx, getAllMetricsIndex(), nil, queryDSL)
if err != nil {
return nil, err
}
var minDate, maxDate int64
if response.StatusCode == 200 {
for _, v := range response.Aggregations {
for _, bucket := range v.Buckets {
v, ok := bucket["key"].(float64)
if !ok {
panic("invalid bucket key")
}
dateTime := (int64(v))
minDate = util.MinInt64(minDate, dateTime)
maxDate = util.MaxInt64(maxDate, dateTime)
for mk1, mv1 := range metricData {
v1, ok := bucket[mk1]
if ok {
v2, ok := v1.(map[string]interface{})
if ok {
v3, ok := v2["value"].(float64)
if ok {
if strings.HasSuffix(mk1, "_deriv") {
if _, ok := bucket[mk1+"_field2"]; !ok {
v3 = v3 / float64(bucketSize)
}
}
if field2, ok := bucket[mk1+"_field2"]; ok {
if line, ok := metricItemsMap[mk1]; ok {
if field2Map, ok := field2.(map[string]interface{}); ok {
v4 := field2Map["value"].(float64)
if v4 == 0 {
v3 = 0
} else {
v3 = line.Metric.Calc(v3, v4)
}
}
}
}
if v3 < 0 {
continue
}
points := []interface{}{dateTime, v3}
metricData[mk1] = append(mv1, points)
}
}
}
}
}
}
}
result := map[string]*common.MetricItem{}
for _, metricItem := range metricItems {
for _, line := range metricItem.Lines {
line.TimeRange = common.TimeRange{Min: minDate, Max: maxDate}
line.Data = metricData[line.Metric.GetDataKey()]
if v, ok := line.Data.([][]interface{}); ok && len(v) > 0 && bucketSize <= 60 {
// remove first metric dot
temp := v[1:]
// // remove first last dot
if len(temp) > 0 {
temp = temp[0 : len(temp)-1]
}
line.Data = temp
}
}
metricItem.Request = string(queryDSL)
metricItem.HitsTotal = response.GetTotal()
result[metricItem.Key] = metricItem
}
return result, nil
}
//func (h *APIHandler) executeQuery(query map[string]interface{}, bucketItems *[]common.BucketItem, bucketSize int) map[string]*common.MetricItem {
// response, err := elastic.GetClient(h.Config.Elasticsearch).SearchWithRawQueryDSL(getAllMetricsIndex(), util.MustToJSONBytes(query))
//
//}
func (h *APIHandler) getBucketMetrics(query map[string]interface{}, bucketItems *[]common.BucketItem, bucketSize int) map[string]*common.MetricItem {
//bucketSizeStr := fmt.Sprintf("%vs", bucketSize)
response, err := elastic.GetClient(global.MustLookupString(elastic.GlobalSystemElasticsearchID)).SearchWithRawQueryDSL(getAllMetricsIndex(), util.MustToJSONBytes(query))
if err != nil {
log.Error(err)
panic(err)
}
//grpMetricItemsIndex := map[string]int{}
for _, item := range *bucketItems {
//grpMetricItemsIndex[item.Key] = i
agg, ok := response.Aggregations[item.Key]
if ok {
fmt.Println(len(agg.Buckets))
}
}
//grpMetricData := map[string]MetricData{}
//var minDate, maxDate int64
//if response.StatusCode == 200 {
// if nodeAgg, ok := response.Aggregations["group_by_level"]; ok {
// for _, bucket := range nodeAgg.Buckets {
// grpKey := bucket["key"].(string)
// for _, metricItem := range *bucketItems {
// metricItem.MetricItem.AddLine(metricItem.Key, grpKey, "", "group1", metricItem.Field, "max", bucketSizeStr, metricItem.Units, metricItem.FormatType, "0.[00]", "0.[00]", false, false)
// dataKey := metricItem.Key
// if metricItem.IsDerivative {
// dataKey = dataKey + "_deriv"
// }
// if _, ok := grpMetricData[dataKey]; !ok {
// grpMetricData[dataKey] = map[string][][]interface{}{}
// }
// grpMetricData[dataKey][grpKey] = [][]interface{}{}
// }
// if datesAgg, ok := bucket["dates"].(map[string]interface{}); ok {
// if datesBuckets, ok := datesAgg["buckets"].([]interface{}); ok {
// for _, dateBucket := range datesBuckets {
// if bucketMap, ok := dateBucket.(map[string]interface{}); ok {
// v, ok := bucketMap["key"].(float64)
// if !ok {
// panic("invalid bucket key")
// }
// dateTime := (int64(v))
// minDate = util.MinInt64(minDate, dateTime)
// maxDate = util.MaxInt64(maxDate, dateTime)
//
// for mk1, mv1 := range grpMetricData {
// v1, ok := bucketMap[mk1]
// if ok {
// v2, ok := v1.(map[string]interface{})
// if ok {
// v3, ok := v2["value"].(float64)
// if ok {
// if strings.HasSuffix(mk1, "_deriv") {
// v3 = v3 / float64(bucketSize)
// }
// if field2, ok := bucketMap[mk1+"_field2"]; ok {
// if idx, ok := grpMetricItemsIndex[mk1]; ok {
// if field2Map, ok := field2.(map[string]interface{}); ok {
// v3 = grpMetricItems[idx].Calc(v3, field2Map["value"].(float64))
// }
// }
// }
// if v3 < 0 {
// continue
// }
// points := []interface{}{dateTime, v3}
// mv1[grpKey] = append(mv1[grpKey], points)
// }
// }
// }
// }
// }
// }
// }
//
// }
// }
// }
//}
//
//result := map[string]*common.MetricItem{}
//
//for _, metricItem := range grpMetricItems {
// for _, line := range metricItem.MetricItem.Lines {
// line.TimeRange = common.TimeRange{Min: minDate, Max: maxDate}
// dataKey := metricItem.ID
// if metricItem.IsDerivative {
// dataKey = dataKey + "_deriv"
// }
// line.Data = grpMetricData[dataKey][line.ElasticsearchMetric.Label]
// }
// result[metricItem.Key] = metricItem.MetricItem
//}
return nil
}
func ConvertMetricItemsToAggQuery(metricItems []*common.MetricItem) map[string]interface{} {
aggs := map[string]interface{}{}
for _, metricItem := range metricItems {
for _, line := range metricItem.Lines {
aggs[line.Metric.ID] = util.MapStr{
"max": util.MapStr{
"field": line.Metric.Field,
},
}
if line.Metric.IsDerivative {
//add which metric keys to extract
aggs[line.Metric.ID+"_deriv"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID,
},
}
}
}
}
return aggs
}
func ConvertBucketItemsToAggQuery(bucketItems []*common.BucketItem, metricItems []*common.MetricItem) util.MapStr {
aggs := util.MapStr{}
var currentAgg = util.MapStr{}
for _, bucketItem := range bucketItems {
bucketAgg := util.MapStr{}
switch bucketItem.Type {
case "terms":
bucketAgg = util.MapStr{
"terms": bucketItem.Parameters,
}
break
case "date_histogram":
bucketAgg = util.MapStr{
"date_histogram": bucketItem.Parameters,
}
break
case "date_range":
bucketAgg = util.MapStr{
"date_range": bucketItem.Parameters,
}
break
}
//if bucketItem.Buckets!=nil&&len(bucketItem.Buckets)>0{
nestedAggs := ConvertBucketItemsToAggQuery(bucketItem.Buckets, bucketItem.Metrics)
if len(nestedAggs) > 0 {
util.MergeFields(bucketAgg, nestedAggs, true)
}
//}
currentAgg[bucketItem.Key] = bucketAgg
}
if metricItems != nil && len(metricItems) > 0 {
metricAggs := ConvertMetricItemsToAggQuery(metricItems)
util.MergeFields(currentAgg, metricAggs, true)
}
aggs = util.MapStr{
"aggs": currentAgg,
}
return aggs
}
type BucketBase map[string]interface{}
func (receiver BucketBase) GetChildBucket(name string) (map[string]interface{}, bool) {
bks, ok := receiver[name]
if ok {
bks2, ok := bks.(map[string]interface{})
return bks2, ok
}
return nil, false
}
type Bucket struct {
BucketBase //子 buckets
KeyAsString string `json:"key_as_string,omitempty"`
Key interface{} `json:"key,omitempty"`
DocCount int64 `json:"doc_count,omitempty"`
DocCountErrorUpperBound int64 `json:"doc_count_error_upper_bound,omitempty"`
SumOtherDocCount int64 `json:"sum_other_doc_count,omitempty"`
Buckets []Bucket `json:"buckets,omitempty"` //本 buckets
}
type SearchResponse struct {
Took int `json:"took"`
TimedOut bool `json:"timed_out"`
Hits struct {
Total interface{} `json:"total"`
MaxScore float32 `json:"max_score"`
} `json:"hits"`
Aggregations util.MapStr `json:"aggregations,omitempty"`
}
func ParseAggregationBucketResult(bucketSize int, aggsData util.MapStr, groupKey, resultLabelKey, resultValueKey string, resultItemHandle func()) MetricData {
metricData := MetricData{}
for k, v := range aggsData {
if k == groupKey {
//start to collect metric for each bucket
objcs, ok := v.(map[string]interface{})
if ok {
bks, ok := objcs["buckets"].([]interface{})
if ok {
for _, bk := range bks {
//check each bucket, collecting metrics
bkMap, ok := bk.(map[string]interface{})
if ok {
groupKeyValue, ok := bkMap["key"]
if ok {
}
bkHitMap, ok := bkMap[resultLabelKey]
if ok {
//hit label, 说明匹配到时间范围了
labelMap, ok := bkHitMap.(map[string]interface{})
if ok {
labelBks, ok := labelMap["buckets"]
if ok {
labelBksMap, ok := labelBks.([]interface{})
if ok {
for _, labelItem := range labelBksMap {
metrics, ok := labelItem.(map[string]interface{})
labelKeyValue, ok := metrics["to"] //TODO config
if !ok {
labelKeyValue, ok = metrics["from"] //TODO config
}
if !ok {
labelKeyValue, ok = metrics["key"] //TODO config
}
metric, ok := metrics[resultValueKey]
if ok {
metricMap, ok := metric.(map[string]interface{})
if ok {
t := "bucket" //metric, bucket
if t == "metric" {
metricValue, ok := metricMap["value"]
if ok {
saveMetric(&metricData, groupKeyValue.(string), labelKeyValue, metricValue, bucketSize)
continue
}
} else {
metricValue, ok := metricMap["buckets"]
if ok {
buckets, ok := metricValue.([]interface{})
if ok {
var result string = "unavailable"
for _, v := range buckets {
x, ok := v.(map[string]interface{})
if ok {
if x["key"] == "red" {
result = "red"
break
}
if x["key"] == "yellow" {
result = "yellow"
} else {
if result != "yellow" {
result = x["key"].(string)
}
}
}
}
v, ok := (metricData)[groupKeyValue.(string)]
if !ok {
v = [][]interface{}{}
}
v2 := []interface{}{}
v2 = append(v2, labelKeyValue)
v2 = append(v2, result)
v = append(v, v2)
(metricData)[groupKeyValue.(string)] = v
}
continue
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
return metricData
}
func ParseAggregationResult(bucketSize int, aggsData util.MapStr, groupKey, metricLabelKey, metricValueKey string) MetricData {
metricData := MetricData{}
//group bucket key: key1, 获取 key 的 buckets 作为分组的内容 map[group][]{LabelMetricValue}
//metric Label Key: key2, 获取其 key 作为 时间指标
//metric Value Key: c7qgjrqi4h92sqdaa9b0, 获取其 value 作为 point 内容
//groupKey:="key1"
//metricLabelKey:="key2"
//metricValueKey:="c7qi5hii4h935v9bs920"
//fmt.Println(groupKey," => ",metricLabelKey," => ",metricValueKey)
for k, v := range aggsData {
//fmt.Println("k:",k)
//fmt.Println("v:",v)
if k == groupKey {
//fmt.Println("hit group key")
//start to collect metric for each bucket
objcs, ok := v.(map[string]interface{})
if ok {
bks, ok := objcs["buckets"].([]interface{})
if ok {
for _, bk := range bks {
//check each bucket, collecting metrics
//fmt.Println("check bucket:",bk)
bkMap, ok := bk.(map[string]interface{})
if ok {
groupKeyValue, ok := bkMap["key"]
if ok {
//fmt.Println("collecting bucket::",groupKeyValue)
}
bkHitMap, ok := bkMap[metricLabelKey]
if ok {
//hit label, 说明匹配到时间范围了
labelMap, ok := bkHitMap.(map[string]interface{})
if ok {
//fmt.Println("bkHitMap",bkHitMap)
labelBks, ok := labelMap["buckets"]
if ok {
labelBksMap, ok := labelBks.([]interface{})
//fmt.Println("get label buckets",ok)
if ok {
//fmt.Println("get label buckets",ok)
for _, labelItem := range labelBksMap {
metrics, ok := labelItem.(map[string]interface{})
//fmt.Println(labelItem)
labelKeyValue, ok := metrics["key"]
if ok {
//fmt.Println("collecting metric label::",int64(labelKeyValue.(float64)))
}
metric, ok := metrics[metricValueKey]
if ok {
metricMap, ok := metric.(map[string]interface{})
if ok {
metricValue, ok := metricMap["value"]
if ok {
//fmt.Println("collecting metric value::",metricValue.(float64))
saveMetric(&metricData, groupKeyValue.(string), labelKeyValue, metricValue, bucketSize)
continue
}
}
}
}
}
}
}
}
}
}
}
}
}
}
//for k,v:=range bucketItems{
// fmt.Println("k:",k)
// fmt.Println("v:",v)
// aggObect:=aggsData[v.Key]
// fmt.Println("",aggObect)
// //fmt.Println(len(aggObect.Buckets))
// //for _,bucket:=range aggObect.Buckets{
// // fmt.Println(bucket.Key)
// // fmt.Println(bucket.GetChildBucket("key2"))
// // //children,ok:=bucket.GetChildBucket()
// // //if ok{
// // //
// // //}
// //}
//}
return metricData
}
func saveMetric(metricData *MetricData, group string, label, value interface{}, bucketSize int) {
if value == nil {
return
}
v3, ok := value.(float64)
if ok {
value = v3 / float64(bucketSize)
}
v, ok := (*metricData)[group]
if !ok {
v = [][]interface{}{}
}
v2 := []interface{}{}
v2 = append(v2, label)
v2 = append(v2, value)
v = append(v, v2)
(*metricData)[group] = v
//fmt.Printf("save:%v, %v=%v\n",group,label,value)
}
func parseGroupMetricData(buckets []elastic.BucketBase, isPercent bool) ([]interface{}, error) {
metricData := []interface{}{}
var minDate, maxDate int64
for _, bucket := range buckets {
v, ok := bucket["key"].(float64)
if !ok {
log.Error("invalid bucket key")
return nil, fmt.Errorf("invalid bucket key")
}
dateTime := int64(v)
minDate = util.MinInt64(minDate, dateTime)
maxDate = util.MaxInt64(maxDate, dateTime)
totalCount := bucket["doc_count"].(float64)
if grpStatus, ok := bucket["groups"].(map[string]interface{}); ok {
if statusBks, ok := grpStatus["buckets"].([]interface{}); ok {
for _, statusBk := range statusBks {
if bkMap, ok := statusBk.(map[string]interface{}); ok {
statusKey := bkMap["key"].(string)
count := bkMap["doc_count"].(float64)
if isPercent {
metricData = append(metricData, map[string]interface{}{
"x": dateTime,
"y": count / totalCount * 100,
"g": statusKey,
})
} else {
metricData = append(metricData, map[string]interface{}{
"x": dateTime,
"y": count,
"g": statusKey,
})
}
}
}
}
}
}
return metricData, nil
}
func (h *APIHandler) getSingleIndexMetricsByNodeStats(ctx context.Context, metricItems []*common.MetricItem, query map[string]interface{}, bucketSize int) (map[string]*common.MetricItem, error) {
metricData := map[string][][]interface{}{}
aggs := util.MapStr{}
metricItemsMap := map[string]*common.MetricLine{}
sumAggs := util.MapStr{}
for _, metricItem := range metricItems {
for _, line := range metricItem.Lines {
dk := line.Metric.GetDataKey()
metricItemsMap[dk] = line
metricData[dk] = [][]interface{}{}
leafAgg := util.MapStr{
line.Metric.MetricAgg: util.MapStr{
"field": line.Metric.Field,
},
}
var sumBucketPath = "term_node>" + line.Metric.ID
aggs[line.Metric.ID] = leafAgg
sumAggs[line.Metric.ID] = util.MapStr{
"sum_bucket": util.MapStr{
"buckets_path": sumBucketPath,
},
}
if line.Metric.Field2 != "" {
leafAgg2 := util.MapStr{
line.Metric.MetricAgg: util.MapStr{
"field": line.Metric.Field2,
},
}
aggs[line.Metric.ID+"_field2"] = leafAgg2
sumAggs[line.Metric.ID+"_field2"] = util.MapStr{
"sum_bucket": util.MapStr{
"buckets_path": sumBucketPath + "_field2",
},
}
}
if line.Metric.IsDerivative {
//add which metric keys to extract
sumAggs[line.Metric.ID+"_deriv"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID,
},
}
if line.Metric.Field2 != "" {
sumAggs[line.Metric.ID+"_deriv_field2"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID + "_field2",
},
}
}
}
}
}
sumAggs["term_node"] = util.MapStr{
"terms": util.MapStr{
"field": "metadata.labels.node_id",
"size": 1000,
},
"aggs": aggs,
}
bucketSizeStr := fmt.Sprintf("%vs", bucketSize)
clusterID := global.MustLookupString(elastic.GlobalSystemElasticsearchID)
intervalField, err := getDateHistogramIntervalField(clusterID, bucketSizeStr)
if err != nil {
return nil, err
}
query["size"] = 0
query["aggs"] = util.MapStr{
"dates": util.MapStr{
"date_histogram": util.MapStr{
"field": "timestamp",
intervalField: bucketSizeStr,
},
"aggs": sumAggs,
},
}
return parseSingleIndexMetrics(ctx, clusterID, metricItems, query, bucketSize, metricData, metricItemsMap)
}
func (h *APIHandler) getSingleIndexMetrics(ctx context.Context, metricItems []*common.MetricItem, query map[string]interface{}, bucketSize int) (map[string]*common.MetricItem, error) {
metricData := map[string][][]interface{}{}
aggs := util.MapStr{}
metricItemsMap := map[string]*common.MetricLine{}
sumAggs := util.MapStr{}
for _, metricItem := range metricItems {
for _, line := range metricItem.Lines {
dk := line.Metric.GetDataKey()
metricItemsMap[dk] = line
metricData[dk] = [][]interface{}{}
leafAgg := util.MapStr{
line.Metric.MetricAgg: util.MapStr{
"field": line.Metric.Field,
},
}
var sumBucketPath = "term_shard>" + line.Metric.ID
aggs[line.Metric.ID] = leafAgg
sumAggs[line.Metric.ID] = util.MapStr{
"sum_bucket": util.MapStr{
"buckets_path": sumBucketPath,
},
}
if line.Metric.Field2 != "" {
leafAgg2 := util.MapStr{
line.Metric.MetricAgg: util.MapStr{
"field": line.Metric.Field2,
},
}
aggs[line.Metric.ID+"_field2"] = leafAgg2
sumAggs[line.Metric.ID+"_field2"] = util.MapStr{
"sum_bucket": util.MapStr{
"buckets_path": sumBucketPath + "_field2",
},
}
}
if line.Metric.IsDerivative {
//add which metric keys to extract
sumAggs[line.Metric.ID+"_deriv"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID,
},
}
if line.Metric.Field2 != "" {
sumAggs[line.Metric.ID+"_deriv_field2"] = util.MapStr{
"derivative": util.MapStr{
"buckets_path": line.Metric.ID + "_field2",
},
}
}
}
}
}
sumAggs["term_shard"] = util.MapStr{
"terms": util.MapStr{
"field": "metadata.labels.shard_id",
"size": 100000,
},
"aggs": aggs,
}
bucketSizeStr := fmt.Sprintf("%vs", bucketSize)
clusterID := global.MustLookupString(elastic.GlobalSystemElasticsearchID)
intervalField, err := getDateHistogramIntervalField(clusterID, bucketSizeStr)
if err != nil {
return nil, err
}
if len(metricItems) > 0 && len(metricItems[0].Lines) > 0 && metricItems[0].Lines[0].Metric.OnlyPrimary {
query["query"] = util.MapStr{
"bool": util.MapStr{
"must": []util.MapStr{
query["query"].(util.MapStr),
{"term": util.MapStr{"payload.elasticsearch.shard_stats.routing.primary": true}},
},
},
}
}
query["size"] = 0
query["aggs"] = util.MapStr{
"dates": util.MapStr{
"date_histogram": util.MapStr{
"field": "timestamp",
intervalField: bucketSizeStr,
},
"aggs": sumAggs,
},
}
return parseSingleIndexMetrics(ctx, clusterID, metricItems, query, bucketSize, metricData, metricItemsMap)
}
func parseSingleIndexMetrics(ctx context.Context, clusterID string, metricItems []*common.MetricItem, query map[string]interface{}, bucketSize int, metricData map[string][][]interface{}, metricItemsMap map[string]*common.MetricLine) (map[string]*common.MetricItem, error) {
queryDSL := util.MustToJSONBytes(query)
response, err := elastic.GetClient(clusterID).QueryDSL(ctx, getAllMetricsIndex(), nil, util.MustToJSONBytes(query))
if err != nil {
return nil, err
}
var minDate, maxDate int64
if response.StatusCode == 200 {
for _, v := range response.Aggregations {
for _, bucket := range v.Buckets {
v, ok := bucket["key"].(float64)
if !ok {
return nil, fmt.Errorf("invalid bucket key type: %T", bucket["key"])
}
dateTime := int64(v)
minDate = util.MinInt64(minDate, dateTime)
maxDate = util.MaxInt64(maxDate, dateTime)
for mk1, mv1 := range metricData {
v1, ok := bucket[mk1]
if ok {
v2, ok := v1.(map[string]interface{})
if ok {
v3, ok := v2["value"].(float64)
if ok {
if strings.HasSuffix(mk1, "_deriv") {
if _, ok := bucket[mk1+"_field2"]; !ok {
v3 = v3 / float64(bucketSize)
}
}
if field2, ok := bucket[mk1+"_field2"]; ok {
if line, ok := metricItemsMap[mk1]; ok {
if field2Map, ok := field2.(map[string]interface{}); ok {
v4 := field2Map["value"].(float64)
if v4 == 0 {
v3 = 0
} else {
v3 = line.Metric.Calc(v3, v4)
}
}
}
}
if v3 < 0 {
continue
}
points := []interface{}{dateTime, v3}
metricData[mk1] = append(mv1, points)
}
}
}
}
}
}
}
result := map[string]*common.MetricItem{}
for _, metricItem := range metricItems {
for _, line := range metricItem.Lines {
line.TimeRange = common.TimeRange{Min: minDate, Max: maxDate}
line.Data = metricData[line.Metric.GetDataKey()]
if v, ok := line.Data.([][]interface{}); ok && len(v) > 0 && bucketSize <= 60 {
// remove first metric dot
temp := v[1:]
// // remove first last dot
if len(temp) > 0 {
temp = temp[0 : len(temp)-1]
}
line.Data = temp
}
}
metricItem.Request = string(queryDSL)
metricItem.HitsTotal = response.GetTotal()
result[metricItem.Key] = metricItem
}
return result, nil
}