Skip to content

Commit

Permalink
Series Graph (#201)
Browse files Browse the repository at this point in the history
* Series graph

* Tests

* Rename index

* Add comment and update label value
  • Loading branch information
joey-grafana authored Sep 13, 2022
1 parent ed195eb commit 2eb8e81
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 14 deletions.
48 changes: 41 additions & 7 deletions grafana/fire-datasource/pkg/plugin/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"time"

"github.com/bufbuild/connect-go"
querierv1 "github.com/grafana/fire/pkg/gen/querier/v1"
Expand Down Expand Up @@ -51,11 +53,6 @@ func (d *FireDatasource) query(ctx context.Context, pCtx backend.PluginContext,
frame.SetMeta(&data.FrameMeta{Channel: channel.String()})
}

// add the frames to the response.
response.Frames = append(response.Frames, frame)

log.DefaultLogger.Debug("Querying SelectSeries()", "queryModel", qm)

seriesResp, err := d.client.SelectSeries(ctx, connect.NewRequest(&querierv1.SelectSeriesRequest{
ProfileTypeID: qm.ProfileTypeID,
LabelSelector: qm.LabelSelector,
Expand All @@ -69,8 +66,11 @@ func (d *FireDatasource) query(ctx context.Context, pCtx backend.PluginContext,
response.Error = err
return response
}
// todo remove me and add the series to the frame.
log.DefaultLogger.Debug("Series", seriesResp.Msg.Series)

// add the frames to the response.
response.Frames = append(response.Frames, seriesToDataFrame(seriesResp, qm.ProfileTypeID))
response.Frames = append(response.Frames, frame)

return response
}

Expand Down Expand Up @@ -242,3 +242,37 @@ func walkTree(tree *ProfileTree, fn func(tree *ProfileTree)) {
}
}
}

func seriesToDataFrame(seriesResp *connect.Response[querierv1.SelectSeriesResponse], profileTypeID string) *data.Frame {
frame := data.NewFrame("series")
frame.Meta = &data.FrameMeta{PreferredVisualization: "graph"}

fields := data.Fields{}
timeField := data.NewField("time", nil, []time.Time{})
fields = append(fields, timeField)

for index, series := range seriesResp.Msg.Series {
label := ""
if len(series.Labels) > 0 {
label = series.Labels[0].Name
} else {
parts := strings.Split(profileTypeID, ":")
if len(parts) == 5 {
label = parts[1] // sample type e.g. cpu, goroutine, alloc_objects
}
}
valueField := data.NewField(label, nil, []float64{})

for _, point := range series.Points {
if index == 0 {
timeField.Append(time.UnixMilli(point.T))
}
valueField.Append(point.V)
}

fields = append(fields, valueField)
}

frame.Fields = fields
return frame
}
36 changes: 29 additions & 7 deletions grafana/fire-datasource/pkg/plugin/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/stretchr/testify/require"

commonv1 "github.com/grafana/fire/pkg/gen/common/v1"
querierv1 "github.com/grafana/fire/pkg/gen/querier/v1"
)

Expand Down Expand Up @@ -39,7 +40,7 @@ func Test_query(t *testing.T) {
}

// This is where the tests for the datasource backend live.
func Test_responseToDataFrames(t *testing.T) {
func Test_profileToDataFrame(t *testing.T) {
resp := &connect.Response[querierv1.SelectMergeStacktracesResponse]{
Msg: &querierv1.SelectMergeStacktracesResponse{
Flamegraph: &querierv1.FlameGraph{
Expand Down Expand Up @@ -116,23 +117,44 @@ func Test_treeToNestedDataFrame(t *testing.T) {
},
}

frame := treeToNestedSetDataFrame(tree)
frame := treeToNestedSetDataFrame(tree, "memory:alloc_objects:count:space:bytes")
require.Equal(t,
[]*data.Field{
data.NewField("level", nil, []int64{0, 1, 1, 2}),
data.NewField("value", nil, []int64{100, 40, 30, 15}),
data.NewField("label", nil, []string{"root", "func1", "func2", "func1:func3"}),
}, frame.Fields)
require.Equal(t, "memory:alloc_objects:count:space:bytes", frame.Meta.Custom.(CustomMeta).ProfileTypeID)

}

type FakeClient struct {
Req *connect.Request[querierv1.SelectMergeStacktracesRequest]
func Test_seriesToDataFrame(t *testing.T) {
resp := &connect.Response[querierv1.SelectSeriesResponse]{
Msg: &querierv1.SelectSeriesResponse{
Series: []*querierv1.Series{
{Labels: []*commonv1.LabelPair{}, Points: []*querierv1.Point{{T: int64(1000), V: 30}, {T: int64(2000), V: 10}}},
},
},
}
frame := seriesToDataFrame(resp, "process_cpu:samples:count:cpu:nanoseconds")
require.Equal(t, 2, len(frame.Fields))
require.Equal(t, data.NewField("time", nil, []time.Time{time.UnixMilli(1000), time.UnixMilli(2000)}), frame.Fields[0])
require.Equal(t, data.NewField("samples", nil, []float64{30, 10}), frame.Fields[1])

// with a label pair, the value field should name itself with a label pair name and not the profile type
resp = &connect.Response[querierv1.SelectSeriesResponse]{
Msg: &querierv1.SelectSeriesResponse{
Series: []*querierv1.Series{
{Labels: []*commonv1.LabelPair{{Name: "app", Value: "bar"}}, Points: []*querierv1.Point{{T: int64(1000), V: 30}, {T: int64(2000), V: 10}}},
},
},
}
frame = seriesToDataFrame(resp, "process_cpu:samples:count:cpu:nanoseconds")
require.Equal(t, data.NewField("app", nil, []float64{30, 10}), frame.Fields[1])
}

func (f FakeClient) LabelNames(ctx context.Context, c *connect.Request[querierv1.LabelNamesRequest]) (*connect.Response[querierv1.LabelNamesResponse], error) {
//TODO implement me
panic("implement me")
type FakeClient struct {
Req *connect.Request[querierv1.SelectMergeStacktracesRequest]
}

func (f FakeClient) ProfileTypes(ctx context.Context, c *connect.Request[querierv1.ProfileTypesRequest]) (*connect.Response[querierv1.ProfileTypesResponse], error) {
Expand Down

0 comments on commit 2eb8e81

Please sign in to comment.