diff --git a/pkg/tsdb/grafana-pyroscope-datasource/instance.go b/pkg/tsdb/grafana-pyroscope-datasource/instance.go index 5ffd40e1ef2..d02e0f9cc89 100644 --- a/pkg/tsdb/grafana-pyroscope-datasource/instance.go +++ b/pkg/tsdb/grafana-pyroscope-datasource/instance.go @@ -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) diff --git a/pkg/tsdb/grafana-pyroscope-datasource/pyroscopeClient.go b/pkg/tsdb/grafana-pyroscope-datasource/pyroscopeClient.go index b63879f2c58..b483d2b40ce 100644 --- a/pkg/tsdb/grafana-pyroscope-datasource/pyroscopeClient.go +++ b/pkg/tsdb/grafana-pyroscope-datasource/pyroscopeClient.go @@ -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 diff --git a/pkg/tsdb/grafana-pyroscope-datasource/query.go b/pkg/tsdb/grafana-pyroscope-datasource/query.go index 0bbd30a6e4c..477e115665a 100644 --- a/pkg/tsdb/grafana-pyroscope-datasource/query.go +++ b/pkg/tsdb/grafana-pyroscope-datasource/query.go @@ -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() } diff --git a/pkg/tsdb/grafana-pyroscope-datasource/service.go b/pkg/tsdb/grafana-pyroscope-datasource/service.go index e208372faac..d6e1a0764ea 100644 --- a/pkg/tsdb/grafana-pyroscope-datasource/service.go +++ b/pkg/tsdb/grafana-pyroscope-datasource/service.go @@ -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) } } diff --git a/pkg/tsdb/parca/resources.go b/pkg/tsdb/parca/resources.go index 5bb256d7b16..23803f4082c 100644 --- a/pkg/tsdb/parca/resources.go +++ b/pkg/tsdb/parca/resources.go @@ -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 }