diff --git a/pkg/tsdb/parca/plugin.go b/pkg/tsdb/parca/plugin.go index fd46dbe6ace..32eec50b246 100644 --- a/pkg/tsdb/parca/plugin.go +++ b/pkg/tsdb/parca/plugin.go @@ -9,6 +9,9 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana/pkg/infra/httpclient" + "github.com/grafana/grafana/pkg/infra/tracing" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) // Make sure ParcaDatasource implements required interfaces. This is important to do @@ -29,10 +32,11 @@ var ( // ParcaDatasource is a datasource for querying application performance profiles. type ParcaDatasource struct { client queryv1alpha1connect.QueryServiceClient + tracer tracing.Tracer } // NewParcaDatasource creates a new datasource instance. -func NewParcaDatasource(httpClientProvider httpclient.Provider, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { +func NewParcaDatasource(httpClientProvider httpclient.Provider, settings backend.DataSourceInstanceSettings, tracer tracing.Tracer) (instancemgmt.Instance, error) { opt, err := settings.HTTPClientOptions() if err != nil { return nil, err @@ -44,6 +48,7 @@ func NewParcaDatasource(httpClientProvider httpclient.Provider, settings backend return &ParcaDatasource{ client: queryv1alpha1connect.NewQueryServiceClient(httpClient, settings.URL, connect.WithGRPCWeb()), + tracer: tracer, }, nil } @@ -56,6 +61,9 @@ func (d *ParcaDatasource) Dispose() { func (d *ParcaDatasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { logger.Debug("CallResource", "Path", req.Path, "Method", req.Method, "Body", req.Body) + ctx, span := d.tracer.Start(ctx, "datasource.parca.CallResource", trace.WithAttributes(attribute.String("path", req.Path), attribute.String("method", req.Method))) + defer span.End() + if req.Path == "profileTypes" { return d.callProfileTypes(ctx, req, sender) } diff --git a/pkg/tsdb/parca/query.go b/pkg/tsdb/parca/query.go index 07618f8a521..ee89699dab9 100644 --- a/pkg/tsdb/parca/query.go +++ b/pkg/tsdb/parca/query.go @@ -12,6 +12,9 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/parca/kinds/dataquery" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -27,12 +30,17 @@ const ( // query processes single Parca query transforming the response to data.Frame packaged in DataResponse func (d *ParcaDatasource) query(ctx context.Context, pCtx backend.PluginContext, query backend.DataQuery) backend.DataResponse { + ctx, span := d.tracer.Start(ctx, "datasource.parca.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 { response.Error = err + span.RecordError(response.Error) + span.SetStatus(codes.Error, response.Error.Error()) return response } @@ -40,6 +48,8 @@ func (d *ParcaDatasource) query(ctx context.Context, pCtx backend.PluginContext, seriesResp, err := d.client.QueryRange(ctx, makeMetricRequest(qm, query)) if err != nil { response.Error = err + span.RecordError(response.Error) + span.SetStatus(codes.Error, response.Error.Error()) return response } response.Frames = append(response.Frames, seriesToDataFrame(seriesResp, qm.ProfileTypeId)...) @@ -50,6 +60,8 @@ func (d *ParcaDatasource) query(ctx context.Context, pCtx backend.PluginContext, resp, err := d.client.Query(ctx, makeProfileRequest(qm, query)) if err != nil { response.Error = err + span.RecordError(response.Error) + span.SetStatus(codes.Error, response.Error.Error()) return response } frame := responseToDataFrames(resp) diff --git a/pkg/tsdb/parca/resources.go b/pkg/tsdb/parca/resources.go index 87fad90a0e5..5bb256d7b16 100644 --- a/pkg/tsdb/parca/resources.go +++ b/pkg/tsdb/parca/resources.go @@ -9,6 +9,7 @@ import ( v1alpha1 "buf.build/gen/go/parca-dev/parca/protocolbuffers/go/parca/query/v1alpha1" "github.com/bufbuild/connect-go" "github.com/grafana/grafana-plugin-sdk-go/backend" + "go.opentelemetry.io/otel/codes" ) type ProfileType struct { @@ -23,6 +24,8 @@ type ProfileType struct { } func (d *ParcaDatasource) callProfileTypes(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + ctx, span := d.tracer.Start(ctx, "datasource.parca.callProfileTypes") + defer span.End() res, err := d.client.ProfileTypes(ctx, connect.NewRequest(&v1alpha1.ProfileTypesRequest{})) if err != nil { return err @@ -50,35 +53,51 @@ func (d *ParcaDatasource) callProfileTypes(ctx context.Context, req *backend.Cal data, err := json.Marshal(types) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: req.Headers, Status: 200}) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } return nil } func (d *ParcaDatasource) callLabelNames(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + ctx, span := d.tracer.Start(ctx, "datasource.parca.callLabelNames") + defer span.End() res, err := d.client.Labels(ctx, connect.NewRequest(&v1alpha1.LabelsRequest{})) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } data, err := json.Marshal(res.Msg.LabelNames) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: req.Headers, Status: 200}) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } return nil } func (d *ParcaDatasource) callLabelValues(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + ctx, span := d.tracer.Start(ctx, "datasource.parca.callLabelValues") + defer span.End() parsedUrl, err := url.Parse(req.URL) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } label, ok := parsedUrl.Query()["label"] @@ -87,14 +106,20 @@ func (d *ParcaDatasource) callLabelValues(ctx context.Context, req *backend.Call } res, err := d.client.Values(ctx, connect.NewRequest(&v1alpha1.ValuesRequest{LabelName: label[0]})) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } data, err := json.Marshal(res.Msg.LabelValues) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: req.Headers, Status: 200}) if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) return err } return nil diff --git a/pkg/tsdb/parca/service.go b/pkg/tsdb/parca/service.go index 08c71da0bfe..3e867ccce52 100644 --- a/pkg/tsdb/parca/service.go +++ b/pkg/tsdb/parca/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" ) // Make sure ParcaDatasource implements required interfaces. This is important to do @@ -28,7 +29,8 @@ var ( var logger = log.New("tsdb.parca") type Service struct { - im instancemgmt.InstanceManager + im instancemgmt.InstanceManager + tracer tracing.Tracer } func (s *Service) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*ParcaDatasource, error) { @@ -40,15 +42,16 @@ func (s *Service) getInstance(ctx context.Context, pluginCtx backend.PluginConte return in, nil } -func ProvideService(httpClientProvider httpclient.Provider) *Service { +func ProvideService(httpClientProvider httpclient.Provider, tracer tracing.Tracer) *Service { return &Service{ - im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)), + im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider, tracer)), + tracer: tracer, } } -func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc { +func newInstanceSettings(httpClientProvider httpclient.Provider, tracer tracing.Tracer) datasource.InstanceFactoryFunc { return func(_ context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return NewParcaDatasource(httpClientProvider, settings) + return NewParcaDatasource(httpClientProvider, settings, tracer) } }