github.com/grafana/pyroscope@v1.18.0/pkg/frontend/readpath/queryfrontend/query_series_labels.go (about) 1 package queryfrontend 2 3 import ( 4 "context" 5 6 "connectrpc.com/connect" 7 "github.com/go-kit/log/level" 8 "github.com/grafana/dskit/tenant" 9 10 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 11 12 querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1" 13 queryv1 "github.com/grafana/pyroscope/api/gen/proto/go/query/v1" 14 "github.com/grafana/pyroscope/pkg/featureflags" 15 "github.com/grafana/pyroscope/pkg/validation" 16 ) 17 18 func (q *QueryFrontend) filterLabelNames( 19 ctx context.Context, 20 c *connect.Request[querierv1.SeriesRequest], 21 labelNames []string, 22 matchers []string, 23 ) ([]string, error) { 24 if capabilities, ok := featureflags.GetClientCapabilities(ctx); ok && capabilities.AllowUtf8LabelNames { 25 return labelNames, nil 26 } 27 28 toFilter := make([]string, len(labelNames)) 29 copy(toFilter, labelNames) 30 31 // Filter out label names not passing legacy validation if utf8 label names not enabled 32 if len(labelNames) == 0 { 33 // Querying for all label names; must retrieve all label names to then filter out 34 response, err := q.LabelNames(ctx, connect.NewRequest(&typesv1.LabelNamesRequest{ 35 Start: c.Msg.Start, 36 End: c.Msg.End, 37 Matchers: matchers, 38 })) 39 40 if err != nil { 41 return nil, err 42 } 43 if response != nil { 44 toFilter = response.Msg.Names 45 } 46 } 47 48 filtered := make([]string, 0, len(labelNames)) 49 for _, name := range toFilter { 50 if _, _, ok := validation.SanitizeLegacyLabelName(name); !ok { 51 level.Debug(q.logger).Log("msg", "filtering out label", "label_name", name) 52 continue 53 } 54 filtered = append(filtered, name) 55 } 56 return filtered, nil 57 } 58 59 func (q *QueryFrontend) Series( 60 ctx context.Context, 61 c *connect.Request[querierv1.SeriesRequest], 62 ) (*connect.Response[querierv1.SeriesResponse], error) { 63 tenantIDs, err := tenant.TenantIDs(ctx) 64 if err != nil { 65 return nil, connect.NewError(connect.CodeInvalidArgument, err) 66 } 67 empty, err := validation.SanitizeTimeRange(q.limits, tenantIDs, &c.Msg.Start, &c.Msg.End) 68 if err != nil { 69 return nil, connect.NewError(connect.CodeInvalidArgument, err) 70 } 71 if empty { 72 return connect.NewResponse(&querierv1.SeriesResponse{}), nil 73 } 74 75 if q.isProfileTypeQuery(c.Msg.LabelNames, c.Msg.Matchers) { 76 level.Debug(q.logger).Log("msg", "listing profile types from metadata as series labels") 77 return q.queryProfileTypeMetadataLabels(ctx, tenantIDs, c.Msg.Start, c.Msg.End, c.Msg.LabelNames) 78 } 79 80 labelSelector, err := buildLabelSelectorFromMatchers(c.Msg.Matchers) 81 if err != nil { 82 return nil, connect.NewError(connect.CodeInvalidArgument, err) 83 } 84 85 labelNames, err := q.filterLabelNames(ctx, c, c.Msg.LabelNames, c.Msg.Matchers) 86 if err != nil { 87 return nil, err 88 } 89 90 report, err := q.querySingle(ctx, &queryv1.QueryRequest{ 91 StartTime: c.Msg.Start, 92 EndTime: c.Msg.End, 93 LabelSelector: labelSelector, 94 Query: []*queryv1.Query{{ 95 QueryType: queryv1.QueryType_QUERY_SERIES_LABELS, 96 SeriesLabels: &queryv1.SeriesLabelsQuery{ 97 LabelNames: labelNames, 98 }, 99 }}, 100 }) 101 if err != nil { 102 return nil, err 103 } 104 if report == nil { 105 return connect.NewResponse(&querierv1.SeriesResponse{}), nil 106 } 107 108 return connect.NewResponse(&querierv1.SeriesResponse{LabelsSet: report.SeriesLabels.SeriesLabels}), nil 109 }