Added spans for pyroscope
This commit is contained in:
@@ -12,7 +12,10 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -36,10 +39,11 @@ type PyroscopeDatasource struct {
|
||||
client ProfilingClient
|
||||
settings backend.DataSourceInstanceSettings
|
||||
ac accesscontrol.AccessControl
|
||||
tracer tracing.Tracer
|
||||
}
|
||||
|
||||
// NewPyroscopeDatasource creates a new datasource instance.
|
||||
func NewPyroscopeDatasource(httpClientProvider httpclient.Provider, settings backend.DataSourceInstanceSettings, ac accesscontrol.AccessControl) (instancemgmt.Instance, error) {
|
||||
func NewPyroscopeDatasource(httpClientProvider httpclient.Provider, settings backend.DataSourceInstanceSettings, ac accesscontrol.AccessControl, tracer tracing.Tracer) (instancemgmt.Instance, error) {
|
||||
opt, err := settings.HTTPClientOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -51,13 +55,16 @@ func NewPyroscopeDatasource(httpClientProvider httpclient.Provider, settings bac
|
||||
|
||||
return &PyroscopeDatasource{
|
||||
httpClient: httpClient,
|
||||
client: NewPyroscopeClient(httpClient, settings.URL),
|
||||
client: NewPyroscopeClient(httpClient, settings.URL, tracer),
|
||||
settings: settings,
|
||||
ac: ac,
|
||||
tracer: tracer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *PyroscopeDatasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
ctx, span := d.tracer.Start(ctx, "datasource.pyroscope.CallResource", trace.WithAttributes(attribute.String("path", req.Path), attribute.String("method", req.Method)))
|
||||
defer span.End()
|
||||
logger.Debug("CallResource", "Path", req.Path, "Method", req.Method, "Body", req.Body)
|
||||
if req.Path == "profileTypes" {
|
||||
return d.profileTypes(ctx, req, sender)
|
||||
|
||||
@@ -7,8 +7,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/bufbuild/connect-go"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
querierv1 "github.com/grafana/phlare/api/gen/proto/go/querier/v1"
|
||||
"github.com/grafana/phlare/api/gen/proto/go/querier/v1/querierv1connect"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
type ProfileType struct {
|
||||
@@ -56,17 +60,23 @@ type SeriesResponse struct {
|
||||
|
||||
type PyroscopeClient struct {
|
||||
connectClient querierv1connect.QuerierServiceClient
|
||||
tracer tracing.Tracer
|
||||
}
|
||||
|
||||
func NewPyroscopeClient(httpClient *http.Client, url string) *PyroscopeClient {
|
||||
func NewPyroscopeClient(httpClient *http.Client, url string, tracer tracing.Tracer) *PyroscopeClient {
|
||||
return &PyroscopeClient{
|
||||
connectClient: querierv1connect.NewQuerierServiceClient(httpClient, url),
|
||||
tracer: tracer,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) ProfileTypes(ctx context.Context) ([]*ProfileType, error) {
|
||||
ctx, span := c.tracer.Start(ctx, "datasource.pyroscope.ProfileTypes")
|
||||
defer span.End()
|
||||
res, err := c.connectClient.ProfileTypes(ctx, connect.NewRequest(&querierv1.ProfileTypesRequest{}))
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if res.Msg.ProfileTypes == nil {
|
||||
@@ -85,6 +95,8 @@ func (c *PyroscopeClient) ProfileTypes(ctx context.Context) ([]*ProfileType, err
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, groupBy []string, step float64) (*SeriesResponse, error) {
|
||||
ctx, span := c.tracer.Start(ctx, "datasource.pyroscope.GetSeries", trace.WithAttributes(attribute.String("profileTypeID", profileTypeID), attribute.String("labelSelector", labelSelector)))
|
||||
defer span.End()
|
||||
req := connect.NewRequest(&querierv1.SelectSeriesRequest{
|
||||
ProfileTypeID: profileTypeID,
|
||||
LabelSelector: labelSelector,
|
||||
@@ -96,6 +108,8 @@ func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, l
|
||||
|
||||
resp, err := c.connectClient.SelectSeries(ctx, req)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -134,6 +148,8 @@ func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, l
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) GetProfile(ctx context.Context, profileTypeID, labelSelector string, start, end int64, maxNodes *int64) (*ProfileResponse, error) {
|
||||
ctx, span := c.tracer.Start(ctx, "datasource.pyroscope.GetProfile", trace.WithAttributes(attribute.String("profileTypeID", profileTypeID), attribute.String("labelSelector", labelSelector)))
|
||||
defer span.End()
|
||||
req := &connect.Request[querierv1.SelectMergeStacktracesRequest]{
|
||||
Msg: &querierv1.SelectMergeStacktracesRequest{
|
||||
ProfileTypeID: profileTypeID,
|
||||
@@ -146,6 +162,8 @@ func (c *PyroscopeClient) GetProfile(ctx context.Context, profileTypeID, labelSe
|
||||
|
||||
resp, err := c.connectClient.SelectMergeStacktraces(ctx, req)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -185,8 +203,12 @@ func getUnits(profileTypeID string) string {
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) LabelNames(ctx context.Context) ([]string, error) {
|
||||
ctx, span := c.tracer.Start(ctx, "datasource.pyroscope.LabelNames")
|
||||
defer span.End()
|
||||
resp, err := c.connectClient.LabelNames(ctx, connect.NewRequest(&querierv1.LabelNamesRequest{}))
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, fmt.Errorf("error seding LabelNames request %v", err)
|
||||
}
|
||||
|
||||
@@ -201,8 +223,12 @@ func (c *PyroscopeClient) LabelNames(ctx context.Context) ([]string, error) {
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) LabelValues(ctx context.Context, label string) ([]string, error) {
|
||||
ctx, span := c.tracer.Start(ctx, "datasource.pyroscope.LabelValues")
|
||||
defer span.End()
|
||||
resp, err := c.connectClient.LabelValues(ctx, connect.NewRequest(&querierv1.LabelValuesRequest{Name: label}))
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return resp.Msg.Names, nil
|
||||
|
||||
@@ -14,6 +14,9 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/live"
|
||||
"github.com/grafana/grafana/pkg/tsdb/grafana-pyroscope-datasource/kinds/dataquery"
|
||||
"github.com/xlab/treeprint"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
@@ -34,11 +37,16 @@ const (
|
||||
|
||||
// query processes single Pyroscope query transforming the response to data.Frame packaged in DataResponse
|
||||
func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginContext, query backend.DataQuery) backend.DataResponse {
|
||||
ctx, span := d.tracer.Start(ctx, "datasource.pyroscope.query", trace.WithAttributes(attribute.String("query_type", query.QueryType)))
|
||||
defer span.End()
|
||||
|
||||
var qm queryModel
|
||||
response := backend.DataResponse{}
|
||||
|
||||
err := json.Unmarshal(query.JSON, &qm)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
response.Error = fmt.Errorf("error unmarshaling query model: %v", err)
|
||||
return response
|
||||
}
|
||||
@@ -50,6 +58,8 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
var dsJson dsJsonModel
|
||||
err = json.Unmarshal(pCtx.DataSourceInstanceSettings.JSONData, &dsJson)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return fmt.Errorf("error unmarshaling datasource json model: %v", err)
|
||||
}
|
||||
|
||||
@@ -72,6 +82,8 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
math.Max(query.Interval.Seconds(), parsedInterval.Seconds()),
|
||||
)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
logger.Error("Querying SelectSeries()", "err", err)
|
||||
return err
|
||||
}
|
||||
@@ -88,6 +100,8 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
logger.Debug("Calling GetProfile", "queryModel", qm)
|
||||
prof, err := d.client.GetProfile(gCtx, qm.ProfileTypeId, qm.LabelSelector, query.TimeRange.From.UnixMilli(), query.TimeRange.To.UnixMilli(), qm.MaxNodes)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
logger.Error("Error GetProfile()", "err", err)
|
||||
return err
|
||||
}
|
||||
@@ -120,6 +134,8 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
}
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
response.Error = g.Wait()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
)
|
||||
|
||||
@@ -42,15 +43,15 @@ func (s *Service) getInstance(ctx context.Context, pluginCtx backend.PluginConte
|
||||
return in, nil
|
||||
}
|
||||
|
||||
func ProvideService(httpClientProvider httpclient.Provider, ac accesscontrol.AccessControl) *Service {
|
||||
func ProvideService(httpClientProvider httpclient.Provider, ac accesscontrol.AccessControl, tracer tracing.Tracer) *Service {
|
||||
return &Service{
|
||||
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider, ac)),
|
||||
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider, ac, tracer)),
|
||||
}
|
||||
}
|
||||
|
||||
func newInstanceSettings(httpClientProvider httpclient.Provider, ac accesscontrol.AccessControl) datasource.InstanceFactoryFunc {
|
||||
func newInstanceSettings(httpClientProvider httpclient.Provider, ac accesscontrol.AccessControl, tracer tracing.Tracer) datasource.InstanceFactoryFunc {
|
||||
return func(_ context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
|
||||
return NewPyroscopeDatasource(httpClientProvider, settings, ac)
|
||||
return NewPyroscopeDatasource(httpClientProvider, settings, ac, tracer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ func (d *ParcaDatasource) callProfileTypes(ctx context.Context, req *backend.Cal
|
||||
defer span.End()
|
||||
res, err := d.client.ProfileTypes(ctx, connect.NewRequest(&v1alpha1.ProfileTypesRequest{}))
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user