155 lines
4.2 KiB
Go
155 lines
4.2 KiB
Go
package traceql
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/grafana/grafana/pkg/tsdb/tempo/kinds/dataquery"
|
|
"github.com/grafana/tempo/pkg/tempopb"
|
|
v1 "github.com/grafana/tempo/pkg/tempopb/common/v1"
|
|
)
|
|
|
|
func TransformMetricsResponse(query string, resp tempopb.QueryRangeResponse) []*data.Frame {
|
|
// prealloc frames
|
|
frames := make([]*data.Frame, len(resp.Series))
|
|
var exemplarFrames []*data.Frame
|
|
|
|
for i, series := range resp.Series {
|
|
name, labels := transformLabelsAndGetName(series.Labels)
|
|
|
|
valueField := data.NewField(name, labels, []float64{})
|
|
valueField.Config = &data.FieldConfig{
|
|
DisplayName: name,
|
|
}
|
|
|
|
timeField := data.NewField("time", nil, []time.Time{})
|
|
|
|
frame := &data.Frame{
|
|
RefID: name,
|
|
Name: name,
|
|
Fields: []*data.Field{
|
|
timeField,
|
|
valueField,
|
|
},
|
|
Meta: &data.FrameMeta{
|
|
PreferredVisualization: data.VisTypeGraph,
|
|
Type: data.FrameTypeTimeSeriesMulti,
|
|
},
|
|
}
|
|
|
|
isHistogram := isHistogramQuery(query)
|
|
if isHistogram {
|
|
frame.Meta.PreferredVisualizationPluginID = "heatmap"
|
|
}
|
|
|
|
for _, sample := range series.Samples {
|
|
frame.AppendRow(time.UnixMilli(sample.GetTimestampMs()), sample.GetValue())
|
|
}
|
|
|
|
if len(series.Exemplars) > 0 {
|
|
exFrame := transformExemplarToFrame(name, series)
|
|
exemplarFrames = append(exemplarFrames, exFrame)
|
|
}
|
|
|
|
frames[i] = frame
|
|
}
|
|
return append(frames, exemplarFrames...)
|
|
}
|
|
|
|
func TransformInstantMetricsResponse(query *dataquery.TempoQuery, resp tempopb.QueryInstantResponse) []*data.Frame {
|
|
frames := make([]*data.Frame, len(resp.Series))
|
|
|
|
for i, series := range resp.Series {
|
|
name, labels := transformLabelsAndGetName(series.Labels)
|
|
|
|
labelKeys := make([]string, 0, len(labels))
|
|
labelFields := make([]*data.Field, 0, len(labels))
|
|
for key := range labels {
|
|
labelKeys = append(labelKeys, key)
|
|
labelFields = append(labelFields, data.NewField(key, nil, []string{}))
|
|
}
|
|
|
|
timeField := data.NewField("time", nil, []time.Time{})
|
|
valueField := data.NewField("value", labels, []float64{})
|
|
valueField.Config = &data.FieldConfig{
|
|
DisplayName: name,
|
|
}
|
|
|
|
frame := &data.Frame{
|
|
RefID: name,
|
|
Name: name,
|
|
Fields: append([]*data.Field{timeField}, append(labelFields, valueField)...),
|
|
Meta: &data.FrameMeta{
|
|
PreferredVisualization: data.VisTypeTable,
|
|
},
|
|
}
|
|
|
|
labelValues := make([]interface{}, len(labels))
|
|
for idx, key := range labelKeys {
|
|
labelValues[idx] = strings.Trim(labels[key], "\"")
|
|
}
|
|
row := append([]interface{}{time.Now()}, append(labelValues, series.GetValue())...)
|
|
frame.AppendRow(row...)
|
|
|
|
frames[i] = frame
|
|
}
|
|
return frames
|
|
}
|
|
|
|
func metricsValueToString(value *v1.AnyValue) (string, string) {
|
|
switch value.GetValue().(type) {
|
|
case *v1.AnyValue_DoubleValue:
|
|
res := strconv.FormatFloat(value.GetDoubleValue(), 'f', -1, 64)
|
|
return res, res
|
|
case *v1.AnyValue_IntValue:
|
|
res := strconv.FormatInt(value.GetIntValue(), 10)
|
|
return res, res
|
|
case *v1.AnyValue_StringValue:
|
|
// return the value wrapped in quotes since it's accurate and "1" is different from 1
|
|
// the second value is returned without quotes for display purposes
|
|
return fmt.Sprintf("\"%s\"", value.GetStringValue()), value.GetStringValue()
|
|
case *v1.AnyValue_BoolValue:
|
|
res := strconv.FormatBool(value.GetBoolValue())
|
|
return res, res
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func transformLabelsAndGetName(seriesLabels []v1.KeyValue) (string, data.Labels) {
|
|
labels := make(data.Labels)
|
|
for _, label := range seriesLabels {
|
|
labels[label.GetKey()], _ = metricsValueToString(label.GetValue())
|
|
}
|
|
name := ""
|
|
if len(seriesLabels) > 0 {
|
|
if len(seriesLabels) == 1 {
|
|
_, name = metricsValueToString(seriesLabels[0].GetValue())
|
|
} else {
|
|
keys := make([]string, 0, len(labels))
|
|
|
|
for k := range labels {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
var labelStrings []string
|
|
for _, key := range keys {
|
|
labelStrings = append(labelStrings, fmt.Sprintf("%s=%s", key, labels[key]))
|
|
}
|
|
|
|
name = fmt.Sprintf("{%s}", strings.Join(labelStrings, ", "))
|
|
}
|
|
}
|
|
return name, labels
|
|
}
|
|
|
|
func isHistogramQuery(query string) bool {
|
|
match, _ := regexp.MatchString("\\|\\s*(histogram_over_time)\\s*\\(", query)
|
|
return match
|
|
}
|