From 7983f94b15b422b94517641bd9cec5c9da6903e1 Mon Sep 17 00:00:00 2001 From: Trevor Whitney Date: Thu, 3 Oct 2024 08:45:50 -0600 Subject: [PATCH] feat: detected field values (#14350) --- cmd/logcli/main.go | 10 +- docs/sources/setup/install/helm/reference.md | 2 + pkg/ingester-rf1/ingester.go | 2 +- pkg/ingester/ingester.go | 2 +- pkg/logcli/client/client.go | 39 +- pkg/logcli/client/file.go | 2 +- pkg/logcli/detected/fields.go | 31 +- pkg/logcli/query/query_test.go | 2 +- pkg/loghttp/detected.go | 1 + pkg/loghttp/params.go | 13 +- pkg/loghttp/query.go | 10 +- pkg/logproto/compat.go | 2 +- pkg/logproto/logproto.pb.go | 530 ++++++++++++------ pkg/logproto/logproto.proto | 9 +- pkg/loki/modules.go | 1 + pkg/lokifrontend/frontend/v1/frontend_test.go | 4 +- pkg/querier-rf1/http.go | 4 +- pkg/querier-rf1/querier.go | 6 +- pkg/querier/http.go | 4 +- pkg/querier/multi_tenant_querier.go | 4 +- pkg/querier/querier.go | 6 +- pkg/querier/queryrange/codec.go | 50 +- pkg/querier/queryrange/codec_test.go | 101 ++-- pkg/querier/queryrange/detected_fields.go | 80 ++- .../queryrange/detected_fields_test.go | 305 ++++++---- pkg/querier/queryrange/roundtrip.go | 10 +- pkg/querier/queryrange/roundtrip_test.go | 15 +- pkg/querier/queryrange/splitters.go | 12 +- pkg/storage/detected/fields.go | 34 +- pkg/storage/detected/fields_test.go | 32 ++ production/helm/loki/values.yaml | 1 + 31 files changed, 909 insertions(+), 415 deletions(-) diff --git a/cmd/logcli/main.go b/cmd/logcli/main.go index 32539e3c83355..976395cd4f420 100644 --- a/cmd/logcli/main.go +++ b/cmd/logcli/main.go @@ -692,7 +692,7 @@ func newVolumeQuery(rangeQuery bool, cmd *kingpin.CmdClause) *volume.Query { func newDetectedFieldsQuery(cmd *kingpin.CmdClause) *detected.FieldsQuery { // calculate query range from cli params - var from, to string + var fieldName, from, to string var since time.Duration q := &detected.FieldsQuery{} @@ -705,24 +705,28 @@ func newDetectedFieldsQuery(cmd *kingpin.CmdClause) *detected.FieldsQuery { q.Start = mustParse(from, defaultStart) q.End = mustParse(to, defaultEnd) + q.FieldName = fieldName + q.Quiet = *quiet return nil }) - cmd.Flag("field-limit", "Limit on number of fields to return."). + cmd.Flag("limit", "Limit on number of fields or values to return."). Default("100"). - IntVar(&q.FieldLimit) + IntVar(&q.Limit) cmd.Flag("line-limit", "Limit the number of lines each subquery is allowed to process."). Default("1000"). IntVar(&q.LineLimit) cmd.Arg("query", "eg '{foo=\"bar\",baz=~\".*blip\"} |~ \".*error.*\"'"). Required(). StringVar(&q.QueryString) + cmd.Arg("field", "The name of the field.").Default("").StringVar(&fieldName) cmd.Flag("since", "Lookback window.").Default("1h").DurationVar(&since) cmd.Flag("from", "Start looking for logs at this absolute time (inclusive)").StringVar(&from) cmd.Flag("to", "Stop looking for logs at this absolute time (exclusive)").StringVar(&to) cmd.Flag("step", "Query resolution step width, for metric queries. Evaluate the query at the specified step over the time range."). + Default("10s"). DurationVar(&q.Step) return q diff --git a/docs/sources/setup/install/helm/reference.md b/docs/sources/setup/install/helm/reference.md index b74a559b12e7f..c8d5f7bbbd107 100644 --- a/docs/sources/setup/install/helm/reference.md +++ b/docs/sources/setup/install/helm/reference.md @@ -5640,6 +5640,7 @@ null "/loki/api/v1/index/volume", "/loki/api/v1/index/volume_range", "/loki/api/v1/format_query", + "/loki/api/v1/detected_field", "/loki/api/v1/detected_fields", "/loki/api/v1/detected_labels", "/loki/api/v1/patterns" @@ -5702,6 +5703,7 @@ null "/loki/api/v1/index/volume", "/loki/api/v1/index/volume_range", "/loki/api/v1/format_query", + "/loki/api/v1/detected_field", "/loki/api/v1/detected_fields", "/loki/api/v1/detected_labels", "/loki/api/v1/patterns" diff --git a/pkg/ingester-rf1/ingester.go b/pkg/ingester-rf1/ingester.go index 583aa6494e77c..d00a95f38e5b5 100644 --- a/pkg/ingester-rf1/ingester.go +++ b/pkg/ingester-rf1/ingester.go @@ -881,6 +881,6 @@ func (i *Ingester) GetDetectedFields(_ context.Context, r *logproto.DetectedFiel Cardinality: 1, }, }, - FieldLimit: r.GetFieldLimit(), + Limit: r.GetLimit(), }, nil } diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go index 8672d5f4cad76..1f3a39415fa3f 100644 --- a/pkg/ingester/ingester.go +++ b/pkg/ingester/ingester.go @@ -1595,7 +1595,7 @@ func (i *Ingester) GetDetectedFields(_ context.Context, r *logproto.DetectedFiel Cardinality: 1, }, }, - FieldLimit: r.GetFieldLimit(), + Limit: r.GetLimit(), }, nil } diff --git a/pkg/logcli/client/client.go b/pkg/logcli/client/client.go index 531ef15f61ad5..f2d42b353f969 100644 --- a/pkg/logcli/client/client.go +++ b/pkg/logcli/client/client.go @@ -28,17 +28,18 @@ import ( ) const ( - queryPath = "/loki/api/v1/query" - queryRangePath = "/loki/api/v1/query_range" - labelsPath = "/loki/api/v1/labels" - labelValuesPath = "/loki/api/v1/label/%s/values" - seriesPath = "/loki/api/v1/series" - tailPath = "/loki/api/v1/tail" - statsPath = "/loki/api/v1/index/stats" - volumePath = "/loki/api/v1/index/volume" - volumeRangePath = "/loki/api/v1/index/volume_range" - detectedFieldsPath = "/loki/api/v1/detected_fields" - defaultAuthHeader = "Authorization" + queryPath = "/loki/api/v1/query" + queryRangePath = "/loki/api/v1/query_range" + labelsPath = "/loki/api/v1/labels" + labelValuesPath = "/loki/api/v1/label/%s/values" + seriesPath = "/loki/api/v1/series" + tailPath = "/loki/api/v1/tail" + statsPath = "/loki/api/v1/index/stats" + volumePath = "/loki/api/v1/index/volume" + volumeRangePath = "/loki/api/v1/index/volume_range" + detectedFieldsPath = "/loki/api/v1/detected_fields" + detectedFieldValuesPath = "/loki/api/v1/detected_field/%s/values" + defaultAuthHeader = "Authorization" // HTTP header keys HTTPScopeOrgID = "X-Scope-OrgID" @@ -61,7 +62,7 @@ type Client interface { GetStats(queryStr string, start, end time.Time, quiet bool) (*logproto.IndexStatsResponse, error) GetVolume(query *volume.Query) (*loghttp.QueryResponse, error) GetVolumeRange(query *volume.Query) (*loghttp.QueryResponse, error) - GetDetectedFields(queryStr string, fieldLimit, lineLimit int, start, end time.Time, step time.Duration, quiet bool) (*loghttp.DetectedFieldsResponse, error) + GetDetectedFields(queryStr, fieldName string, fieldLimit, lineLimit int, start, end time.Time, step time.Duration, quiet bool) (*loghttp.DetectedFieldsResponse, error) } // Tripperware can wrap a roundtripper. @@ -234,15 +235,16 @@ func (c *DefaultClient) getVolume(path string, query *volume.Query) (*loghttp.Qu } func (c *DefaultClient) GetDetectedFields( - queryStr string, - fieldLimit, lineLimit int, + queryStr, fieldName string, + limit, lineLimit int, start, end time.Time, step time.Duration, quiet bool, ) (*loghttp.DetectedFieldsResponse, error) { + qsb := util.NewQueryStringBuilder() qsb.SetString("query", queryStr) - qsb.SetInt("field_limit", int64(fieldLimit)) + qsb.SetInt("limit", int64(limit)) qsb.SetInt("line_limit", int64(lineLimit)) qsb.SetInt("start", start.UnixNano()) qsb.SetInt("end", end.UnixNano()) @@ -251,7 +253,12 @@ func (c *DefaultClient) GetDetectedFields( var err error var r loghttp.DetectedFieldsResponse - if err = c.doRequest(detectedFieldsPath, qsb.Encode(), quiet, &r); err != nil { + path := detectedFieldsPath + if fieldName != "" { + path = fmt.Sprintf(detectedFieldValuesPath, url.PathEscape(fieldName)) + } + + if err = c.doRequest(path, qsb.Encode(), quiet, &r); err != nil { return nil, err } diff --git a/pkg/logcli/client/file.go b/pkg/logcli/client/file.go index 1267681d75c85..b1b97fd57b08a 100644 --- a/pkg/logcli/client/file.go +++ b/pkg/logcli/client/file.go @@ -207,7 +207,7 @@ func (f *FileClient) GetVolumeRange(_ *volume.Query) (*loghttp.QueryResponse, er } func (f *FileClient) GetDetectedFields( - _ string, + _, _ string, _, _ int, _, _ time.Time, _ time.Duration, diff --git a/pkg/logcli/detected/fields.go b/pkg/logcli/detected/fields.go index f8ba585ea2a00..cca74f11497b1 100644 --- a/pkg/logcli/detected/fields.go +++ b/pkg/logcli/detected/fields.go @@ -18,10 +18,11 @@ type FieldsQuery struct { QueryString string Start time.Time End time.Time - FieldLimit int + Limit int LineLimit int Step time.Duration Quiet bool + FieldName string ColoredOutput bool } @@ -30,7 +31,16 @@ func (q *FieldsQuery) Do(c client.Client, outputMode string) { var resp *loghttp.DetectedFieldsResponse var err error - resp, err = c.GetDetectedFields(q.QueryString, q.FieldLimit, q.LineLimit, q.Start, q.End, q.Step, q.Quiet) + resp, err = c.GetDetectedFields( + q.QueryString, + q.FieldName, + q.Limit, + q.LineLimit, + q.Start, + q.End, + q.Step, + q.Quiet, + ) if err != nil { log.Fatalf("Error doing request: %+v", err) } @@ -43,12 +53,17 @@ func (q *FieldsQuery) Do(c client.Client, outputMode string) { } fmt.Println(string(out)) default: - output := make([]string, len(resp.Fields)) - for i, field := range resp.Fields { - bold := color.New(color.Bold) - output[i] = fmt.Sprintf("label: %s\t\t", bold.Sprintf("%s", field.Label)) + - fmt.Sprintf("type: %s\t\t", bold.Sprintf("%s", field.Type)) + - fmt.Sprintf("cardinality: %s", bold.Sprintf("%d", field.Cardinality)) + var output []string + if len(resp.Fields) > 0 { + output = make([]string, len(resp.Fields)) + for i, field := range resp.Fields { + bold := color.New(color.Bold) + output[i] = fmt.Sprintf("label: %s\t\t", bold.Sprintf("%s", field.Label)) + + fmt.Sprintf("type: %s\t\t", bold.Sprintf("%s", field.Type)) + + fmt.Sprintf("cardinality: %s", bold.Sprintf("%d", field.Cardinality)) + } + } else if len(resp.Values) > 0 { + output = resp.Values } slices.Sort(output) diff --git a/pkg/logcli/query/query_test.go b/pkg/logcli/query/query_test.go index 8e52134482b7c..35077968a1176 100644 --- a/pkg/logcli/query/query_test.go +++ b/pkg/logcli/query/query_test.go @@ -486,7 +486,7 @@ func (t *testQueryClient) GetVolumeRange(_ *volume.Query) (*loghttp.QueryRespons } func (t *testQueryClient) GetDetectedFields( - _ string, + _, _ string, _, _ int, _, _ time.Time, _ time.Duration, diff --git a/pkg/loghttp/detected.go b/pkg/loghttp/detected.go index 632ac7cd02410..26c263b8c638c 100644 --- a/pkg/loghttp/detected.go +++ b/pkg/loghttp/detected.go @@ -5,6 +5,7 @@ import "github.com/grafana/loki/v3/pkg/logproto" // LabelResponse represents the http json response to a label query type DetectedFieldsResponse struct { Fields []DetectedField `json:"fields,omitempty"` + Values []string `json:"values,omitempty"` } type DetectedField struct { diff --git a/pkg/loghttp/params.go b/pkg/loghttp/params.go index 4f34992df592b..c32161d5bf5ec 100644 --- a/pkg/loghttp/params.go +++ b/pkg/loghttp/params.go @@ -19,7 +19,7 @@ import ( const ( defaultQueryLimit = 100 - defaultFieldLimit = 1000 + defaultLimit = 1000 defaultSince = 1 * time.Hour defaultDirection = logproto.BACKWARD ) @@ -46,11 +46,18 @@ func lineLimit(r *http.Request) (uint32, error) { return uint32(l), nil } -func fieldLimit(r *http.Request) (uint32, error) { - l, err := parseInt(r.Form.Get("field_limit"), defaultFieldLimit) +func detectedFieldsLimit(r *http.Request) (uint32, error) { + limit := r.Form.Get("limit") + if limit == "" { + // for backwards compatability + limit = r.Form.Get("field_limit") + } + + l, err := parseInt(limit, defaultLimit) if err != nil { return 0, err } + if l <= 0 { return 0, errors.New("limit must be a positive value") } diff --git a/pkg/loghttp/query.go b/pkg/loghttp/query.go index af67b9df2d0a3..a2bce462aab80 100644 --- a/pkg/loghttp/query.go +++ b/pkg/loghttp/query.go @@ -9,6 +9,7 @@ import ( "unsafe" "github.com/c2h5oh/datasize" + "github.com/gorilla/mux" "github.com/grafana/jsonparser" json "github.com/json-iterator/go" "github.com/prometheus/common/model" @@ -650,6 +651,7 @@ func ParseDetectedFieldsQuery(r *http.Request) (*logproto.DetectedFieldsRequest, result := &logproto.DetectedFieldsRequest{} result.Query = query(r) + result.Values, result.Name = values(r) result.Start, result.End, err = bounds(r) if err != nil { return nil, err @@ -664,7 +666,7 @@ func ParseDetectedFieldsQuery(r *http.Request) (*logproto.DetectedFieldsRequest, return nil, err } - result.FieldLimit, err = fieldLimit(r) + result.Limit, err = detectedFieldsLimit(r) if err != nil { return nil, err } @@ -684,9 +686,15 @@ func ParseDetectedFieldsQuery(r *http.Request) (*logproto.DetectedFieldsRequest, if (result.End.Sub(result.Start) / step) > 11000 { return nil, errStepTooSmall } + return result, nil } +func values(r *http.Request) (bool, string) { + name, ok := mux.Vars(r)["name"] + return ok, name +} + func targetLabels(r *http.Request) []string { lbls := strings.Split(r.Form.Get("targetLabels"), ",") if (len(lbls) == 1 && lbls[0] == "") || len(lbls) == 0 { diff --git a/pkg/logproto/compat.go b/pkg/logproto/compat.go index ca45a2add0aa9..405b29cc6148d 100644 --- a/pkg/logproto/compat.go +++ b/pkg/logproto/compat.go @@ -524,7 +524,7 @@ func (m *DetectedFieldsRequest) LogToSpan(sp opentracing.Span) { otlog.String("start", m.Start.String()), otlog.String("end", m.End.String()), otlog.String("step", time.Duration(m.Step).String()), - otlog.String("field_limit", fmt.Sprintf("%d", m.FieldLimit)), + otlog.String("field_limit", fmt.Sprintf("%d", m.Limit)), otlog.String("line_limit", fmt.Sprintf("%d", m.LineLimit)), } sp.LogFields(fields...) diff --git a/pkg/logproto/logproto.pb.go b/pkg/logproto/logproto.pb.go index e5c36bb22b8d7..27b36e7bff093 100644 --- a/pkg/logproto/logproto.pb.go +++ b/pkg/logproto/logproto.pb.go @@ -2696,12 +2696,14 @@ func (m *Volume) GetVolume() uint64 { } type DetectedFieldsRequest struct { - Start time.Time `protobuf:"bytes,1,opt,name=start,proto3,stdtime" json:"start"` - End time.Time `protobuf:"bytes,2,opt,name=end,proto3,stdtime" json:"end"` - Query string `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"` - LineLimit uint32 `protobuf:"varint,4,opt,name=lineLimit,proto3" json:"lineLimit,omitempty"` - FieldLimit uint32 `protobuf:"varint,5,opt,name=fieldLimit,proto3" json:"fieldLimit,omitempty"` - Step int64 `protobuf:"varint,6,opt,name=step,proto3" json:"step,omitempty"` + Start time.Time `protobuf:"bytes,1,opt,name=start,proto3,stdtime" json:"start"` + End time.Time `protobuf:"bytes,2,opt,name=end,proto3,stdtime" json:"end"` + Query string `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"` + LineLimit uint32 `protobuf:"varint,4,opt,name=lineLimit,proto3" json:"lineLimit,omitempty"` + Limit uint32 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"` + Step int64 `protobuf:"varint,6,opt,name=step,proto3" json:"step,omitempty"` + Values bool `protobuf:"varint,7,opt,name=values,proto3" json:"values,omitempty"` + Name string `protobuf:"bytes,8,opt,name=name,proto3" json:"name,omitempty"` } func (m *DetectedFieldsRequest) Reset() { *m = DetectedFieldsRequest{} } @@ -2764,9 +2766,9 @@ func (m *DetectedFieldsRequest) GetLineLimit() uint32 { return 0 } -func (m *DetectedFieldsRequest) GetFieldLimit() uint32 { +func (m *DetectedFieldsRequest) GetLimit() uint32 { if m != nil { - return m.FieldLimit + return m.Limit } return 0 } @@ -2778,9 +2780,24 @@ func (m *DetectedFieldsRequest) GetStep() int64 { return 0 } +func (m *DetectedFieldsRequest) GetValues() bool { + if m != nil { + return m.Values + } + return false +} + +func (m *DetectedFieldsRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + type DetectedFieldsResponse struct { - Fields []*DetectedField `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` - FieldLimit uint32 `protobuf:"varint,2,opt,name=fieldLimit,proto3" json:"fieldLimit,omitempty"` + Fields []*DetectedField `protobuf:"bytes,1,rep,name=fields,proto3" json:"fields,omitempty"` + Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + Values []string `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty"` } func (m *DetectedFieldsResponse) Reset() { *m = DetectedFieldsResponse{} } @@ -2822,13 +2839,20 @@ func (m *DetectedFieldsResponse) GetFields() []*DetectedField { return nil } -func (m *DetectedFieldsResponse) GetFieldLimit() uint32 { +func (m *DetectedFieldsResponse) GetLimit() uint32 { if m != nil { - return m.FieldLimit + return m.Limit } return 0 } +func (m *DetectedFieldsResponse) GetValues() []string { + if m != nil { + return m.Values + } + return nil +} + // TODO: make the detected field include the serialized sketch // we only want cardinality in the JSON response type DetectedField struct { @@ -3130,179 +3154,180 @@ func init() { func init() { proto.RegisterFile("pkg/logproto/logproto.proto", fileDescriptor_c28a5f14f1f4c79a) } var fileDescriptor_c28a5f14f1f4c79a = []byte{ - // 2739 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x3a, 0x4d, 0x6c, 0x1b, 0xc7, - 0xd5, 0x5c, 0x72, 0x49, 0x91, 0x8f, 0x94, 0x2c, 0x8f, 0x68, 0x9b, 0x90, 0x1d, 0xae, 0x32, 0xf8, - 0xbe, 0xc4, 0x5f, 0xec, 0x88, 0xb6, 0xf3, 0x25, 0x75, 0x9c, 0xa6, 0xa9, 0x29, 0xc5, 0x8e, 0x1d, - 0xc5, 0x76, 0x46, 0x8e, 0x93, 0x16, 0x0d, 0x82, 0x35, 0x39, 0x22, 0x17, 0x26, 0x77, 0xe9, 0xdd, - 0x61, 0x1c, 0xde, 0x0a, 0xf4, 0x5c, 0x34, 0x40, 0x0f, 0x6d, 0x2f, 0x05, 0x0a, 0x14, 0x68, 0x51, - 0x20, 0x97, 0xa2, 0xc7, 0xa2, 0xbd, 0x14, 0x68, 0x7a, 0xcb, 0x31, 0xc8, 0x81, 0x6d, 0x94, 0x4b, - 0x21, 0xa0, 0x40, 0x80, 0x02, 0x2d, 0x90, 0x53, 0x31, 0x7f, 0xbb, 0xb3, 0x2b, 0xaa, 0x0e, 0x5d, - 0x17, 0x49, 0x2e, 0xe4, 0xcc, 0x9b, 0x37, 0x6f, 0xe6, 0xfd, 0xcc, 0xfb, 0x23, 0xe1, 0xf8, 0xe8, - 0x4e, 0xaf, 0x35, 0x08, 0x7a, 0xa3, 0x30, 0x60, 0x41, 0x3c, 0x58, 0x17, 0x9f, 0xa8, 0xac, 0xe7, - 0xab, 0xf5, 0x5e, 0xd0, 0x0b, 0x24, 0x0e, 0x1f, 0xc9, 0xf5, 0x55, 0xa7, 0x17, 0x04, 0xbd, 0x01, - 0x6d, 0x89, 0xd9, 0xed, 0xf1, 0x4e, 0x8b, 0x79, 0x43, 0x1a, 0x31, 0x77, 0x38, 0x52, 0x08, 0x6b, - 0x8a, 0xfa, 0xdd, 0xc1, 0x30, 0xe8, 0xd2, 0x41, 0x2b, 0x62, 0x2e, 0x8b, 0xe4, 0xa7, 0xc2, 0x58, - 0xe1, 0x18, 0xa3, 0x71, 0xd4, 0x17, 0x1f, 0x0a, 0x78, 0x86, 0x03, 0x23, 0x16, 0x84, 0x6e, 0x8f, - 0xb6, 0x3a, 0xfd, 0xb1, 0x7f, 0xa7, 0xd5, 0x71, 0x3b, 0x7d, 0xda, 0x0a, 0x69, 0x34, 0x1e, 0xb0, - 0x48, 0x4e, 0xd8, 0x64, 0x44, 0x15, 0x19, 0xfc, 0x1b, 0x0b, 0x8e, 0x6c, 0xb9, 0xb7, 0xe9, 0xe0, - 0x66, 0x70, 0xcb, 0x1d, 0x8c, 0x69, 0x44, 0x68, 0x34, 0x0a, 0xfc, 0x88, 0xa2, 0x0d, 0x28, 0x0d, - 0xf8, 0x42, 0xd4, 0xb0, 0xd6, 0x0a, 0x27, 0xab, 0xe7, 0x4e, 0xad, 0xc7, 0x4c, 0xce, 0xdc, 0x20, - 0xa1, 0xd1, 0x8b, 0x3e, 0x0b, 0x27, 0x44, 0x6d, 0x5d, 0xbd, 0x05, 0x55, 0x03, 0x8c, 0x96, 0xa1, - 0x70, 0x87, 0x4e, 0x1a, 0xd6, 0x9a, 0x75, 0xb2, 0x42, 0xf8, 0x10, 0x9d, 0x85, 0xe2, 0xdb, 0x9c, - 0x4c, 0x23, 0xbf, 0x66, 0x9d, 0xac, 0x9e, 0x3b, 0x9e, 0x1c, 0xf2, 0x9a, 0xef, 0xdd, 0x1d, 0x53, - 0xb1, 0x5b, 0x1d, 0x24, 0x31, 0x2f, 0xe4, 0xcf, 0x5b, 0xf8, 0x14, 0x1c, 0xde, 0xb7, 0x8e, 0x8e, - 0x42, 0x49, 0x60, 0xc8, 0x1b, 0x57, 0x88, 0x9a, 0xe1, 0x3a, 0xa0, 0x6d, 0x16, 0x52, 0x77, 0x48, - 0x5c, 0xc6, 0xef, 0x7b, 0x77, 0x4c, 0x23, 0x86, 0x5f, 0x81, 0x95, 0x14, 0x54, 0xb1, 0xfd, 0x0c, - 0x54, 0xa3, 0x04, 0xac, 0x78, 0xaf, 0x27, 0xd7, 0x4a, 0xf6, 0x10, 0x13, 0x11, 0xff, 0xd4, 0x02, - 0x48, 0xd6, 0x50, 0x13, 0x40, 0xae, 0xbe, 0xe4, 0x46, 0x7d, 0xc1, 0xb0, 0x4d, 0x0c, 0x08, 0x3a, - 0x0d, 0x87, 0x93, 0xd9, 0xb5, 0x60, 0xbb, 0xef, 0x86, 0x5d, 0x21, 0x03, 0x9b, 0xec, 0x5f, 0x40, - 0x08, 0xec, 0xd0, 0x65, 0xb4, 0x51, 0x58, 0xb3, 0x4e, 0x16, 0x88, 0x18, 0x73, 0x6e, 0x19, 0xf5, - 0x5d, 0x9f, 0x35, 0x6c, 0x21, 0x4e, 0x35, 0xe3, 0x70, 0x6e, 0x11, 0x34, 0x6a, 0x14, 0xd7, 0xac, - 0x93, 0x8b, 0x44, 0xcd, 0xf0, 0x3f, 0x0a, 0x50, 0x7b, 0x75, 0x4c, 0xc3, 0x89, 0x12, 0x00, 0x6a, - 0x42, 0x39, 0xa2, 0x03, 0xda, 0x61, 0x41, 0x28, 0x35, 0xd2, 0xce, 0x37, 0x2c, 0x12, 0xc3, 0x50, - 0x1d, 0x8a, 0x03, 0x6f, 0xe8, 0x31, 0x71, 0xad, 0x45, 0x22, 0x27, 0xe8, 0x02, 0x14, 0x23, 0xe6, - 0x86, 0x4c, 0xdc, 0xa5, 0x7a, 0x6e, 0x75, 0x5d, 0x9a, 0xf2, 0xba, 0x36, 0xe5, 0xf5, 0x9b, 0xda, - 0x94, 0xdb, 0xe5, 0xf7, 0xa7, 0x4e, 0xee, 0xdd, 0x3f, 0x3b, 0x16, 0x91, 0x5b, 0xd0, 0x33, 0x50, - 0xa0, 0x7e, 0x57, 0xdc, 0xf7, 0xf3, 0xee, 0xe4, 0x1b, 0xd0, 0x59, 0xa8, 0x74, 0xbd, 0x90, 0x76, - 0x98, 0x17, 0xf8, 0x82, 0xab, 0xa5, 0x73, 0x2b, 0x89, 0x46, 0x36, 0xf5, 0x12, 0x49, 0xb0, 0xd0, - 0x69, 0x28, 0x45, 0x5c, 0x74, 0x51, 0x63, 0x81, 0xdb, 0x42, 0xbb, 0xbe, 0x37, 0x75, 0x96, 0x25, - 0xe4, 0x74, 0x30, 0xf4, 0x18, 0x1d, 0x8e, 0xd8, 0x84, 0x28, 0x1c, 0xf4, 0x04, 0x2c, 0x74, 0xe9, - 0x80, 0x72, 0x85, 0x97, 0x85, 0xc2, 0x97, 0x0d, 0xf2, 0x62, 0x81, 0x68, 0x04, 0xf4, 0x26, 0xd8, - 0xa3, 0x81, 0xeb, 0x37, 0x2a, 0x82, 0x8b, 0xa5, 0x04, 0xf1, 0xc6, 0xc0, 0xf5, 0xdb, 0xcf, 0x7e, - 0x34, 0x75, 0x9e, 0xee, 0x79, 0xac, 0x3f, 0xbe, 0xbd, 0xde, 0x09, 0x86, 0xad, 0x5e, 0xe8, 0xee, - 0xb8, 0xbe, 0xdb, 0x1a, 0x04, 0x77, 0xbc, 0xd6, 0xdb, 0x4f, 0xb5, 0xf8, 0x03, 0xbd, 0x3b, 0xa6, - 0xa1, 0x47, 0xc3, 0x16, 0x27, 0xb3, 0x2e, 0x54, 0xc2, 0xb7, 0x12, 0x41, 0x16, 0x5d, 0xe5, 0xf6, - 0x17, 0x84, 0x74, 0x83, 0xbf, 0xde, 0xa8, 0x01, 0xe2, 0x94, 0x63, 0xc9, 0x29, 0x02, 0x4e, 0xe8, - 0xce, 0xe5, 0x30, 0x18, 0x8f, 0xda, 0x87, 0xf6, 0xa6, 0x8e, 0x89, 0x4f, 0xcc, 0xc9, 0x55, 0xbb, - 0x5c, 0x5a, 0x5e, 0xc0, 0xef, 0x15, 0x00, 0x6d, 0xbb, 0xc3, 0xd1, 0x80, 0xce, 0xa5, 0xfe, 0x58, - 0xd1, 0xf9, 0x07, 0x56, 0x74, 0x61, 0x5e, 0x45, 0x27, 0x5a, 0xb3, 0xe7, 0xd3, 0x5a, 0xf1, 0xf3, - 0x6a, 0xad, 0xf4, 0xa5, 0xd7, 0x1a, 0x6e, 0x80, 0xcd, 0x29, 0x73, 0x67, 0x19, 0xba, 0xf7, 0x84, - 0x6e, 0x6a, 0x84, 0x0f, 0xf1, 0x16, 0x94, 0x24, 0x5f, 0x68, 0x35, 0xab, 0xbc, 0xf4, 0xbb, 0x4d, - 0x14, 0x57, 0xd0, 0x2a, 0x59, 0x4e, 0x54, 0x52, 0x10, 0xc2, 0xc6, 0xbf, 0xb5, 0x60, 0x51, 0x59, - 0x84, 0xf2, 0x7d, 0xb7, 0x61, 0x41, 0xfa, 0x1e, 0xed, 0xf7, 0x8e, 0x65, 0xfd, 0xde, 0xc5, 0xae, - 0x3b, 0x62, 0x34, 0x6c, 0xb7, 0xde, 0x9f, 0x3a, 0xd6, 0x47, 0x53, 0xe7, 0xf1, 0x83, 0x84, 0xa6, - 0xa3, 0x93, 0xf6, 0x97, 0x9a, 0x30, 0x3a, 0x25, 0x6e, 0xc7, 0x22, 0x65, 0x56, 0x87, 0xd6, 0x65, - 0x50, 0xbb, 0xe2, 0xf7, 0x68, 0xc4, 0x29, 0xdb, 0xdc, 0x22, 0x88, 0xc4, 0xe1, 0x6c, 0xde, 0x73, - 0x43, 0xdf, 0xf3, 0x7b, 0x51, 0xa3, 0x20, 0x7c, 0x7a, 0x3c, 0xc7, 0x3f, 0xb6, 0x60, 0x25, 0x65, - 0xd6, 0x8a, 0x89, 0xf3, 0x50, 0x8a, 0xb8, 0xa6, 0x34, 0x0f, 0x86, 0x51, 0x6c, 0x0b, 0x78, 0x7b, - 0x49, 0x5d, 0xbe, 0x24, 0xe7, 0x44, 0xe1, 0x3f, 0xbc, 0xab, 0xfd, 0xc1, 0x82, 0x9a, 0x08, 0x4c, + // 2764 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x3a, 0xcd, 0x6f, 0x1b, 0xc7, + 0xf5, 0x5c, 0x72, 0x49, 0x91, 0x8f, 0x94, 0x2c, 0x8f, 0x68, 0x9b, 0x90, 0x1d, 0xae, 0x32, 0xf8, + 0xfd, 0x12, 0x27, 0x76, 0x44, 0xdb, 0x69, 0xd2, 0xc4, 0x69, 0x9a, 0x9a, 0x52, 0xec, 0xd8, 0x51, + 0x6c, 0x67, 0xe4, 0x38, 0x69, 0xd1, 0x20, 0x58, 0x93, 0x23, 0x72, 0x61, 0x72, 0x97, 0xde, 0x1d, + 0xc6, 0xe1, 0xad, 0xff, 0x40, 0xd1, 0x00, 0x45, 0xd1, 0xf6, 0x52, 0xa0, 0x40, 0x81, 0x16, 0x05, + 0x72, 0x29, 0x7a, 0xe8, 0xa1, 0x68, 0x2f, 0x05, 0x9a, 0xde, 0x72, 0x0c, 0x72, 0x60, 0x1b, 0xe5, + 0x52, 0x08, 0x28, 0x90, 0x53, 0x0b, 0xe4, 0x54, 0xcc, 0xd7, 0xee, 0xec, 0x8a, 0xaa, 0x43, 0xd7, + 0x45, 0x92, 0x0b, 0x39, 0xf3, 0xe6, 0xcd, 0x9b, 0x79, 0x1f, 0xf3, 0xbe, 0x48, 0x38, 0x3e, 0xba, + 0xdd, 0x6b, 0x0d, 0x82, 0xde, 0x28, 0x0c, 0x58, 0x10, 0x0f, 0xd6, 0xc5, 0x27, 0x2a, 0xeb, 0xf9, + 0x6a, 0xbd, 0x17, 0xf4, 0x02, 0x89, 0xc3, 0x47, 0x72, 0x7d, 0xd5, 0xe9, 0x05, 0x41, 0x6f, 0x40, + 0x5b, 0x62, 0x76, 0x6b, 0xbc, 0xd3, 0x62, 0xde, 0x90, 0x46, 0xcc, 0x1d, 0x8e, 0x14, 0xc2, 0x9a, + 0xa2, 0x7e, 0x67, 0x30, 0x0c, 0xba, 0x74, 0xd0, 0x8a, 0x98, 0xcb, 0x22, 0xf9, 0xa9, 0x30, 0x56, + 0x38, 0xc6, 0x68, 0x1c, 0xf5, 0xc5, 0x87, 0x02, 0x9e, 0xe1, 0xc0, 0x88, 0x05, 0xa1, 0xdb, 0xa3, + 0xad, 0x4e, 0x7f, 0xec, 0xdf, 0x6e, 0x75, 0xdc, 0x4e, 0x9f, 0xb6, 0x42, 0x1a, 0x8d, 0x07, 0x2c, + 0x92, 0x13, 0x36, 0x19, 0x51, 0x45, 0x06, 0xff, 0xd6, 0x82, 0x23, 0x5b, 0xee, 0x2d, 0x3a, 0xb8, + 0x11, 0xdc, 0x74, 0x07, 0x63, 0x1a, 0x11, 0x1a, 0x8d, 0x02, 0x3f, 0xa2, 0x68, 0x03, 0x4a, 0x03, + 0xbe, 0x10, 0x35, 0xac, 0xb5, 0xc2, 0xc9, 0xea, 0xb9, 0x53, 0xeb, 0x31, 0x93, 0x33, 0x37, 0x48, + 0x68, 0xf4, 0xa2, 0xcf, 0xc2, 0x09, 0x51, 0x5b, 0x57, 0x6f, 0x42, 0xd5, 0x00, 0xa3, 0x65, 0x28, + 0xdc, 0xa6, 0x93, 0x86, 0xb5, 0x66, 0x9d, 0xac, 0x10, 0x3e, 0x44, 0x67, 0xa1, 0xf8, 0x36, 0x27, + 0xd3, 0xc8, 0xaf, 0x59, 0x27, 0xab, 0xe7, 0x8e, 0x27, 0x87, 0xbc, 0xe6, 0x7b, 0x77, 0xc6, 0x54, + 0xec, 0x56, 0x07, 0x49, 0xcc, 0xf3, 0xf9, 0x67, 0x2c, 0x7c, 0x0a, 0x0e, 0xef, 0x5b, 0x47, 0x47, + 0xa1, 0x24, 0x30, 0xe4, 0x8d, 0x2b, 0x44, 0xcd, 0x70, 0x1d, 0xd0, 0x36, 0x0b, 0xa9, 0x3b, 0x24, + 0x2e, 0xe3, 0xf7, 0xbd, 0x33, 0xa6, 0x11, 0xc3, 0xaf, 0xc0, 0x4a, 0x0a, 0xaa, 0xd8, 0x7e, 0x1a, + 0xaa, 0x51, 0x02, 0x56, 0xbc, 0xd7, 0x93, 0x6b, 0x25, 0x7b, 0x88, 0x89, 0x88, 0x7f, 0x66, 0x01, + 0x24, 0x6b, 0xa8, 0x09, 0x20, 0x57, 0x5f, 0x72, 0xa3, 0xbe, 0x60, 0xd8, 0x26, 0x06, 0x04, 0x9d, + 0x86, 0xc3, 0xc9, 0xec, 0x6a, 0xb0, 0xdd, 0x77, 0xc3, 0xae, 0x90, 0x81, 0x4d, 0xf6, 0x2f, 0x20, + 0x04, 0x76, 0xe8, 0x32, 0xda, 0x28, 0xac, 0x59, 0x27, 0x0b, 0x44, 0x8c, 0x39, 0xb7, 0x8c, 0xfa, + 0xae, 0xcf, 0x1a, 0xb6, 0x10, 0xa7, 0x9a, 0x71, 0x38, 0xb7, 0x08, 0x1a, 0x35, 0x8a, 0x6b, 0xd6, + 0xc9, 0x45, 0xa2, 0x66, 0xf8, 0x9f, 0x05, 0xa8, 0xbd, 0x3a, 0xa6, 0xe1, 0x44, 0x09, 0x00, 0x35, + 0xa1, 0x1c, 0xd1, 0x01, 0xed, 0xb0, 0x20, 0x94, 0x1a, 0x69, 0xe7, 0x1b, 0x16, 0x89, 0x61, 0xa8, + 0x0e, 0xc5, 0x81, 0x37, 0xf4, 0x98, 0xb8, 0xd6, 0x22, 0x91, 0x13, 0x74, 0x1e, 0x8a, 0x11, 0x73, + 0x43, 0x26, 0xee, 0x52, 0x3d, 0xb7, 0xba, 0x2e, 0x4d, 0x79, 0x5d, 0x9b, 0xf2, 0xfa, 0x0d, 0x6d, + 0xca, 0xed, 0xf2, 0xfb, 0x53, 0x27, 0xf7, 0xee, 0x5f, 0x1d, 0x8b, 0xc8, 0x2d, 0xe8, 0x69, 0x28, + 0x50, 0xbf, 0x2b, 0xee, 0xfb, 0x79, 0x77, 0xf2, 0x0d, 0xe8, 0x2c, 0x54, 0xba, 0x5e, 0x48, 0x3b, + 0xcc, 0x0b, 0x7c, 0xc1, 0xd5, 0xd2, 0xb9, 0x95, 0x44, 0x23, 0x9b, 0x7a, 0x89, 0x24, 0x58, 0xe8, + 0x34, 0x94, 0x22, 0x2e, 0xba, 0xa8, 0xb1, 0xc0, 0x6d, 0xa1, 0x5d, 0xdf, 0x9b, 0x3a, 0xcb, 0x12, + 0x72, 0x3a, 0x18, 0x7a, 0x8c, 0x0e, 0x47, 0x6c, 0x42, 0x14, 0x0e, 0x7a, 0x1c, 0x16, 0xba, 0x74, + 0x40, 0xb9, 0xc2, 0xcb, 0x42, 0xe1, 0xcb, 0x06, 0x79, 0xb1, 0x40, 0x34, 0x02, 0x7a, 0x13, 0xec, + 0xd1, 0xc0, 0xf5, 0x1b, 0x15, 0xc1, 0xc5, 0x52, 0x82, 0x78, 0x7d, 0xe0, 0xfa, 0xed, 0x67, 0x3f, + 0x9a, 0x3a, 0x4f, 0xf5, 0x3c, 0xd6, 0x1f, 0xdf, 0x5a, 0xef, 0x04, 0xc3, 0x56, 0x2f, 0x74, 0x77, + 0x5c, 0xdf, 0x6d, 0x0d, 0x82, 0xdb, 0x5e, 0xeb, 0xed, 0x27, 0x5b, 0xfc, 0x81, 0xde, 0x19, 0xd3, + 0xd0, 0xa3, 0x61, 0x8b, 0x93, 0x59, 0x17, 0x2a, 0xe1, 0x5b, 0x89, 0x20, 0x8b, 0xae, 0x70, 0xfb, + 0x0b, 0x42, 0xba, 0xc1, 0x5f, 0x6f, 0xd4, 0x00, 0x71, 0xca, 0xb1, 0xe4, 0x14, 0x01, 0x27, 0x74, + 0xe7, 0x52, 0x18, 0x8c, 0x47, 0xed, 0x43, 0x7b, 0x53, 0xc7, 0xc4, 0x27, 0xe6, 0xe4, 0x8a, 0x5d, + 0x2e, 0x2d, 0x2f, 0xe0, 0xf7, 0x0a, 0x80, 0xb6, 0xdd, 0xe1, 0x68, 0x40, 0xe7, 0x52, 0x7f, 0xac, + 0xe8, 0xfc, 0x7d, 0x2b, 0xba, 0x30, 0xaf, 0xa2, 0x13, 0xad, 0xd9, 0xf3, 0x69, 0xad, 0xf8, 0x79, + 0xb5, 0x56, 0xfa, 0xd2, 0x6b, 0x0d, 0x37, 0xc0, 0xe6, 0x94, 0xb9, 0xb3, 0x0c, 0xdd, 0xbb, 0x42, + 0x37, 0x35, 0xc2, 0x87, 0x78, 0x0b, 0x4a, 0x92, 0x2f, 0xb4, 0x9a, 0x55, 0x5e, 0xfa, 0xdd, 0x26, + 0x8a, 0x2b, 0x68, 0x95, 0x2c, 0x27, 0x2a, 0x29, 0x08, 0x61, 0xe3, 0xdf, 0x5b, 0xb0, 0xa8, 0x2c, + 0x42, 0xf9, 0xbe, 0x5b, 0xb0, 0x20, 0x7d, 0x8f, 0xf6, 0x7b, 0xc7, 0xb2, 0x7e, 0xef, 0x42, 0xd7, + 0x1d, 0x31, 0x1a, 0xb6, 0x5b, 0xef, 0x4f, 0x1d, 0xeb, 0xa3, 0xa9, 0xf3, 0xe8, 0x41, 0x42, 0xd3, + 0xd1, 0x49, 0xfb, 0x4b, 0x4d, 0x18, 0x9d, 0x12, 0xb7, 0x63, 0x91, 0x32, 0xab, 0x43, 0xeb, 0x32, + 0xa8, 0x5d, 0xf6, 0x7b, 0x34, 0xe2, 0x94, 0x6d, 0x6e, 0x11, 0x44, 0xe2, 0x70, 0x36, 0xef, 0xba, + 0xa1, 0xef, 0xf9, 0xbd, 0xa8, 0x51, 0x10, 0x3e, 0x3d, 0x9e, 0xe3, 0x9f, 0x58, 0xb0, 0x92, 0x32, + 0x6b, 0xc5, 0xc4, 0x33, 0x50, 0x8a, 0xb8, 0xa6, 0x34, 0x0f, 0x86, 0x51, 0x6c, 0x0b, 0x78, 0x7b, + 0x49, 0x5d, 0xbe, 0x24, 0xe7, 0x44, 0xe1, 0x3f, 0xb8, 0xab, 0xfd, 0xc9, 0x82, 0x9a, 0x08, 0x4c, 0xfa, 0xad, 0x21, 0xb0, 0x7d, 0x77, 0x48, 0x95, 0xaa, 0xc4, 0xd8, 0x88, 0x56, 0xfc, 0xb8, 0xb2, - 0x8e, 0x56, 0xf3, 0x3a, 0x58, 0xeb, 0x81, 0x1d, 0xac, 0x95, 0xbc, 0xbb, 0x3a, 0x14, 0xb9, 0x79, - 0x4f, 0x84, 0x73, 0xad, 0x10, 0x39, 0xc1, 0x8f, 0xc3, 0xa2, 0xe2, 0x42, 0x89, 0xf6, 0xa0, 0x00, - 0x3b, 0x84, 0x92, 0xd4, 0x04, 0xfa, 0x1f, 0xa8, 0xc4, 0xa9, 0x8c, 0xe0, 0xb6, 0xd0, 0x2e, 0xed, + 0x8e, 0x56, 0xf3, 0x3a, 0x58, 0xeb, 0xbe, 0x1d, 0xac, 0x95, 0xbc, 0xbb, 0x3a, 0x14, 0xb9, 0x79, + 0x4f, 0x84, 0x73, 0xad, 0x10, 0x39, 0xc1, 0x8f, 0xc2, 0xa2, 0xe2, 0x42, 0x89, 0xf6, 0xa0, 0x00, + 0x3b, 0x84, 0x92, 0xd4, 0x04, 0xfa, 0x3f, 0xa8, 0xc4, 0xa9, 0x8c, 0xe0, 0xb6, 0xd0, 0x2e, 0xed, 0x4d, 0x9d, 0x3c, 0x8b, 0x48, 0xb2, 0x80, 0x1c, 0x33, 0xe8, 0x5b, 0xed, 0xca, 0xde, 0xd4, 0x91, 0x00, 0x15, 0xe2, 0xd1, 0x09, 0xb0, 0xfb, 0x3c, 0x6e, 0x72, 0x11, 0xd8, 0xed, 0xf2, 0xde, 0xd4, - 0x11, 0x73, 0x22, 0x3e, 0xf1, 0x65, 0xa8, 0x6d, 0xd1, 0x9e, 0xdb, 0x99, 0xa8, 0x43, 0xeb, 0x9a, - 0x1c, 0x3f, 0xd0, 0xd2, 0x34, 0x1e, 0x85, 0x5a, 0x7c, 0xe2, 0x5b, 0xc3, 0x48, 0xbd, 0x86, 0x6a, - 0x0c, 0x7b, 0x25, 0xc2, 0x3f, 0xb1, 0x40, 0xd9, 0x00, 0xc2, 0x46, 0xb6, 0xc3, 0x7d, 0x21, 0xec, + 0x11, 0x73, 0x22, 0x3e, 0xf1, 0x25, 0xa8, 0x6d, 0xd1, 0x9e, 0xdb, 0x99, 0xa8, 0x43, 0xeb, 0x9a, + 0x1c, 0x3f, 0xd0, 0xd2, 0x34, 0x1e, 0x86, 0x5a, 0x7c, 0xe2, 0x5b, 0xc3, 0x48, 0xbd, 0x86, 0x6a, + 0x0c, 0x7b, 0x25, 0xc2, 0x3f, 0xb5, 0x40, 0xd9, 0x00, 0xc2, 0x46, 0xb6, 0xc3, 0x7d, 0x21, 0xec, 0x4d, 0x1d, 0x05, 0xd1, 0xc9, 0x0c, 0x7a, 0x0e, 0x16, 0x22, 0x71, 0x22, 0x27, 0x96, 0x35, 0x2d, 0xb1, 0xd0, 0x3e, 0xc4, 0x4d, 0x64, 0x6f, 0xea, 0x68, 0x44, 0xa2, 0x07, 0x68, 0x3d, 0x95, 0x10, - 0x48, 0xc6, 0x96, 0xf6, 0xa6, 0x8e, 0x01, 0x35, 0x13, 0x04, 0xfc, 0x99, 0x05, 0xd5, 0x9b, 0xae, + 0x48, 0xc6, 0x96, 0xf6, 0xa6, 0x8e, 0x01, 0x35, 0x13, 0x04, 0xfc, 0x99, 0x05, 0xd5, 0x1b, 0xae, 0x17, 0x9b, 0x50, 0x43, 0xab, 0x28, 0xf1, 0xd5, 0x12, 0xc0, 0x2d, 0xb1, 0x4b, 0x07, 0xee, 0xe4, - 0x52, 0x10, 0x0a, 0xba, 0x8b, 0x24, 0x9e, 0x27, 0x31, 0xdc, 0x9e, 0x19, 0xc3, 0x8b, 0xf3, 0xbb, - 0xf6, 0xff, 0xae, 0x23, 0xbd, 0x6a, 0x97, 0xf3, 0xcb, 0x05, 0xfc, 0x9e, 0x05, 0x35, 0xc9, 0xbc, - 0xb2, 0xbc, 0xef, 0x40, 0x49, 0xca, 0x46, 0xb0, 0xff, 0x6f, 0x1c, 0xd3, 0xa9, 0x79, 0x9c, 0x92, + 0x62, 0x10, 0x0a, 0xba, 0x8b, 0x24, 0x9e, 0x27, 0x31, 0xdc, 0x9e, 0x19, 0xc3, 0x8b, 0xf3, 0xbb, + 0xf6, 0xff, 0xad, 0x23, 0xbd, 0x62, 0x97, 0xf3, 0xcb, 0x05, 0xfc, 0x9e, 0x05, 0x35, 0xc9, 0xbc, + 0xb2, 0xbc, 0xef, 0x42, 0x49, 0xca, 0x46, 0xb0, 0xff, 0x1f, 0x1c, 0xd3, 0xa9, 0x79, 0x9c, 0x92, 0xa2, 0x89, 0x5e, 0x80, 0xa5, 0x6e, 0x18, 0x8c, 0x46, 0xb4, 0xbb, 0xad, 0xdc, 0x5f, 0x3e, 0xeb, - 0xfe, 0x36, 0xcd, 0x75, 0x92, 0x41, 0xc7, 0x7f, 0xb2, 0x60, 0x51, 0x39, 0x13, 0xa5, 0xae, 0x58, - 0xc4, 0xd6, 0x03, 0x47, 0xcf, 0xfc, 0xbc, 0xd1, 0xf3, 0x28, 0x94, 0x7a, 0x3c, 0xbe, 0x68, 0x87, - 0xa4, 0x66, 0xf3, 0x45, 0x55, 0x7c, 0x15, 0x96, 0x34, 0x2b, 0x07, 0x78, 0xd4, 0xd5, 0xac, 0x47, - 0xbd, 0xd2, 0xa5, 0x3e, 0xf3, 0x76, 0xbc, 0xd8, 0x47, 0x2a, 0x7c, 0xfc, 0x03, 0x0b, 0x96, 0xb3, - 0x28, 0x68, 0x33, 0x53, 0x58, 0x3c, 0x76, 0x30, 0x39, 0xb3, 0xa6, 0xd0, 0xa4, 0x55, 0x65, 0xf1, - 0xf4, 0xfd, 0x2a, 0x8b, 0xba, 0xe9, 0x64, 0x2a, 0xca, 0x2b, 0xe0, 0x1f, 0x59, 0xb0, 0x98, 0xd2, - 0x25, 0x3a, 0x0f, 0xf6, 0x4e, 0x18, 0x0c, 0xe7, 0x52, 0x94, 0xd8, 0x81, 0xfe, 0x1f, 0xf2, 0x2c, - 0x98, 0x4b, 0x4d, 0x79, 0x16, 0x70, 0x2d, 0x29, 0xf6, 0x0b, 0x32, 0x6f, 0x97, 0x33, 0xfc, 0x34, - 0x54, 0x04, 0x43, 0x37, 0x5c, 0x2f, 0x9c, 0x19, 0x30, 0x66, 0x33, 0xf4, 0x1c, 0x1c, 0x92, 0xce, + 0xfe, 0x36, 0xcd, 0x75, 0x92, 0x41, 0xc7, 0x7f, 0xb1, 0x60, 0x51, 0x39, 0x13, 0xa5, 0xae, 0x58, + 0xc4, 0xd6, 0x7d, 0x47, 0xcf, 0xfc, 0xbc, 0xd1, 0xf3, 0x28, 0x94, 0x7a, 0x3c, 0xbe, 0x68, 0x87, + 0xa4, 0x66, 0xf3, 0x45, 0x55, 0x7c, 0x05, 0x96, 0x34, 0x2b, 0x07, 0x78, 0xd4, 0xd5, 0xac, 0x47, + 0xbd, 0xdc, 0xa5, 0x3e, 0xf3, 0x76, 0xbc, 0xd8, 0x47, 0x2a, 0x7c, 0xfc, 0x03, 0x0b, 0x96, 0xb3, + 0x28, 0x68, 0x33, 0x53, 0x58, 0x3c, 0x72, 0x30, 0x39, 0xb3, 0xa6, 0xd0, 0xa4, 0x55, 0x65, 0xf1, + 0xd4, 0xbd, 0x2a, 0x8b, 0xba, 0xe9, 0x64, 0x2a, 0xca, 0x2b, 0xe0, 0x1f, 0x5b, 0xb0, 0x98, 0xd2, + 0x25, 0x7a, 0x06, 0xec, 0x9d, 0x30, 0x18, 0xce, 0xa5, 0x28, 0xb1, 0x03, 0x7d, 0x0d, 0xf2, 0x2c, + 0x98, 0x4b, 0x4d, 0x79, 0x16, 0x70, 0x2d, 0x29, 0xf6, 0x0b, 0x32, 0x6f, 0x97, 0x33, 0xfc, 0x14, + 0x54, 0x04, 0x43, 0xd7, 0x5d, 0x2f, 0x9c, 0x19, 0x30, 0x66, 0x33, 0xf4, 0x1c, 0x1c, 0x92, 0xce, 0x70, 0xf6, 0xe6, 0xda, 0xac, 0xcd, 0x35, 0xbd, 0xf9, 0x38, 0x14, 0x45, 0xd2, 0xc1, 0xb7, 0x74, 0x5d, 0xe6, 0xea, 0x2d, 0x7c, 0x8c, 0x8f, 0xc0, 0x0a, 0x7f, 0x83, 0x34, 0x8c, 0x36, 0x82, 0xb1, 0xcf, 0x74, 0xdd, 0x74, 0x1a, 0xea, 0x69, 0xb0, 0xb2, 0x92, 0x3a, 0x14, 0x3b, 0x1c, 0x20, 0x68, - 0x2c, 0x12, 0x39, 0xc1, 0x3f, 0xb7, 0x00, 0x5d, 0xa6, 0x4c, 0x9c, 0x72, 0x65, 0x33, 0x7e, 0x1e, + 0x2c, 0x12, 0x39, 0xc1, 0xbf, 0xb0, 0x00, 0x5d, 0xa2, 0x4c, 0x9c, 0x72, 0x79, 0x33, 0x7e, 0x1e, 0xab, 0x50, 0x1e, 0xba, 0xac, 0xd3, 0xa7, 0x61, 0xa4, 0xf3, 0x17, 0x3d, 0xff, 0x22, 0x12, 0x4f, 0x7c, 0x16, 0x56, 0x52, 0xb7, 0x54, 0x3c, 0xad, 0x42, 0xb9, 0xa3, 0x60, 0x2a, 0xe4, 0xc5, 0x73, - 0xfc, 0xeb, 0x3c, 0x94, 0x75, 0x5a, 0x87, 0xce, 0x42, 0x75, 0xc7, 0xf3, 0x7b, 0x34, 0x1c, 0x85, - 0x9e, 0x12, 0x81, 0x2d, 0xd3, 0x3c, 0x03, 0x4c, 0xcc, 0x09, 0x7a, 0x12, 0x16, 0xc6, 0x11, 0x0d, - 0xdf, 0xf2, 0xe4, 0x4b, 0xaf, 0xb4, 0xeb, 0xbb, 0x53, 0xa7, 0xf4, 0x5a, 0x44, 0xc3, 0x2b, 0x9b, + 0xfc, 0x9b, 0x3c, 0x94, 0x75, 0x5a, 0x87, 0xce, 0x42, 0x75, 0xc7, 0xf3, 0x7b, 0x34, 0x1c, 0x85, + 0x9e, 0x12, 0x81, 0x2d, 0xd3, 0x3c, 0x03, 0x4c, 0xcc, 0x09, 0x7a, 0x02, 0x16, 0xc6, 0x11, 0x0d, + 0xdf, 0xf2, 0xe4, 0x4b, 0xaf, 0xb4, 0xeb, 0xbb, 0x53, 0xa7, 0xf4, 0x5a, 0x44, 0xc3, 0xcb, 0x9b, 0x3c, 0xf8, 0x8c, 0xc5, 0x88, 0xc8, 0xef, 0x2e, 0x7a, 0x59, 0x99, 0xa9, 0x48, 0xe0, 0xda, 0x5f, - 0xe3, 0xd7, 0xcf, 0xb8, 0xba, 0x51, 0x18, 0x0c, 0x29, 0xeb, 0xd3, 0x71, 0xd4, 0xea, 0x04, 0xc3, - 0x61, 0xe0, 0xb7, 0x44, 0xef, 0x40, 0x30, 0xcd, 0x23, 0x28, 0xdf, 0xae, 0x2c, 0xf7, 0x26, 0x2c, - 0xb0, 0x7e, 0x18, 0x8c, 0x7b, 0x7d, 0x11, 0x18, 0x0a, 0xed, 0x0b, 0xf3, 0xd3, 0xd3, 0x14, 0x88, - 0x1e, 0xa0, 0x47, 0xb9, 0xb4, 0x68, 0xe7, 0x4e, 0x34, 0x1e, 0xca, 0xda, 0xb3, 0x5d, 0xdc, 0x9b, - 0x3a, 0xd6, 0x93, 0x24, 0x06, 0xe3, 0x8b, 0xb0, 0x98, 0x4a, 0x85, 0xd1, 0x19, 0xb0, 0x43, 0xba, + 0xe7, 0xd7, 0xcf, 0xb8, 0xba, 0x51, 0x18, 0x0c, 0x29, 0xeb, 0xd3, 0x71, 0xd4, 0xea, 0x04, 0xc3, + 0x61, 0xe0, 0xb7, 0x44, 0xef, 0x40, 0x30, 0xcd, 0x23, 0x28, 0xdf, 0xae, 0x2c, 0xf7, 0x06, 0x2c, + 0xb0, 0x7e, 0x18, 0x8c, 0x7b, 0x7d, 0x11, 0x18, 0x0a, 0xed, 0xf3, 0xf3, 0xd3, 0xd3, 0x14, 0x88, + 0x1e, 0xa0, 0x87, 0xb9, 0xb4, 0x68, 0xe7, 0x76, 0x34, 0x1e, 0xca, 0xda, 0xb3, 0x5d, 0xdc, 0x9b, + 0x3a, 0xd6, 0x13, 0x24, 0x06, 0xe3, 0x0b, 0xb0, 0x98, 0x4a, 0x85, 0xd1, 0x19, 0xb0, 0x43, 0xba, 0xa3, 0x5d, 0x01, 0xda, 0x9f, 0x31, 0xcb, 0xe8, 0xcf, 0x71, 0x88, 0xf8, 0xc4, 0xdf, 0xcf, 0x83, - 0x63, 0x54, 0xfd, 0x97, 0x82, 0xf0, 0x15, 0xca, 0x42, 0xaf, 0x73, 0xcd, 0x1d, 0x52, 0x6d, 0x5e, - 0x0e, 0x54, 0x87, 0x02, 0xf8, 0x96, 0xf1, 0x8a, 0x60, 0x18, 0xe3, 0xa1, 0x47, 0x00, 0xc4, 0xb3, + 0x63, 0x54, 0xfd, 0x17, 0x83, 0xf0, 0x15, 0xca, 0x42, 0xaf, 0x73, 0xd5, 0x1d, 0x52, 0x6d, 0x5e, + 0x0e, 0x54, 0x87, 0x02, 0xf8, 0x96, 0xf1, 0x8a, 0x60, 0x18, 0xe3, 0xa1, 0x87, 0x00, 0xc4, 0xb3, 0x93, 0xeb, 0xf2, 0x41, 0x55, 0x04, 0x44, 0x2c, 0x6f, 0xa4, 0x84, 0xdd, 0x9a, 0x53, 0x38, 0x4a, - 0xc8, 0x57, 0xb2, 0x42, 0x9e, 0x9b, 0x4e, 0x2c, 0x59, 0xf3, 0xb9, 0x14, 0xd3, 0xcf, 0x05, 0xff, - 0xcd, 0x82, 0xe6, 0x96, 0xbe, 0xf9, 0x03, 0x8a, 0x43, 0xf3, 0x9b, 0x7f, 0x48, 0xfc, 0x16, 0x1e, - 0x22, 0xbf, 0x76, 0x86, 0xdf, 0x26, 0xc0, 0x96, 0xe7, 0xd3, 0x4b, 0xde, 0x80, 0xd1, 0x70, 0x46, - 0x91, 0xf4, 0xc3, 0x42, 0xe2, 0x71, 0x08, 0xdd, 0xd1, 0x32, 0xd8, 0x30, 0xdc, 0xfc, 0xc3, 0x60, - 0x31, 0xff, 0x10, 0x59, 0x2c, 0x64, 0x3c, 0xa0, 0x0f, 0x0b, 0x3b, 0x82, 0x3d, 0x19, 0xb1, 0x53, - 0xfd, 0xa7, 0x84, 0xf7, 0xf6, 0x37, 0xd4, 0xe1, 0xcf, 0xdc, 0x27, 0xe1, 0x12, 0x7d, 0xc4, 0x56, + 0xc8, 0x97, 0xb3, 0x42, 0x9e, 0x9b, 0x4e, 0x2c, 0x59, 0xf3, 0xb9, 0x14, 0xd3, 0xcf, 0x05, 0xff, + 0xc3, 0x82, 0xe6, 0x96, 0xbe, 0xf9, 0x7d, 0x8a, 0x43, 0xf3, 0x9b, 0x7f, 0x40, 0xfc, 0x16, 0x1e, + 0x20, 0xbf, 0x76, 0x86, 0xdf, 0x26, 0xc0, 0x96, 0xe7, 0xd3, 0x8b, 0xde, 0x80, 0xd1, 0x70, 0x46, + 0x91, 0xf4, 0xc3, 0x42, 0xe2, 0x71, 0x08, 0xdd, 0xd1, 0x32, 0xd8, 0x30, 0xdc, 0xfc, 0x83, 0x60, + 0x31, 0xff, 0x00, 0x59, 0x2c, 0x64, 0x3c, 0xa0, 0x0f, 0x0b, 0x3b, 0x82, 0x3d, 0x19, 0xb1, 0x53, + 0xfd, 0xa7, 0x84, 0xf7, 0xf6, 0x37, 0xd5, 0xe1, 0x4f, 0xdf, 0x23, 0xe1, 0x12, 0x7d, 0xc4, 0x56, 0x34, 0xf1, 0x99, 0xfb, 0x8e, 0xb1, 0x9f, 0xe8, 0x43, 0x90, 0xab, 0x72, 0xba, 0xe2, 0xcc, 0x9c, - 0xee, 0x79, 0x75, 0xcc, 0x7f, 0x92, 0xd7, 0xe1, 0xe7, 0x13, 0x07, 0x2b, 0x94, 0xa2, 0x1c, 0xec, - 0x63, 0xf7, 0x7b, 0xfe, 0xea, 0xd1, 0xff, 0xce, 0x82, 0xe5, 0xcb, 0x94, 0xa5, 0x73, 0xac, 0xaf, - 0x90, 0x4a, 0xf1, 0x4b, 0x70, 0xd8, 0xb8, 0xbf, 0xe2, 0xfe, 0xa9, 0x4c, 0x62, 0x75, 0x24, 0xe1, - 0xff, 0x8a, 0xdf, 0xa5, 0xef, 0xa8, 0x7a, 0x35, 0x9d, 0x53, 0xdd, 0x80, 0xaa, 0xb1, 0x88, 0x2e, - 0x66, 0xb2, 0xa9, 0x95, 0x4c, 0x9b, 0x96, 0x67, 0x04, 0xed, 0xba, 0xe2, 0x49, 0x56, 0xa5, 0x2a, + 0xee, 0x79, 0x75, 0xcc, 0x7f, 0x93, 0xd7, 0xe1, 0xe7, 0x13, 0x07, 0x2b, 0x94, 0xa2, 0x1c, 0xec, + 0x23, 0xf7, 0x7a, 0xfe, 0xea, 0xd1, 0xff, 0xc1, 0x82, 0xe5, 0x4b, 0x94, 0xa5, 0x73, 0xac, 0xaf, + 0x90, 0x4a, 0xf1, 0x4b, 0x70, 0xd8, 0xb8, 0xbf, 0xe2, 0xfe, 0xc9, 0x4c, 0x62, 0x75, 0x24, 0xe1, + 0xff, 0xb2, 0xdf, 0xa5, 0xef, 0xa8, 0x7a, 0x35, 0x9d, 0x53, 0x5d, 0x87, 0xaa, 0xb1, 0x88, 0x2e, + 0x64, 0xb2, 0xa9, 0x95, 0x4c, 0x9b, 0x96, 0x67, 0x04, 0xed, 0xba, 0xe2, 0x49, 0x56, 0xa5, 0x2a, 0x57, 0x8e, 0x33, 0x8f, 0x6d, 0x40, 0x42, 0x5d, 0x82, 0xac, 0x19, 0xfb, 0x04, 0xf4, 0xe5, 0x38, - 0xad, 0x8a, 0xe7, 0xe8, 0x51, 0xb0, 0xc3, 0xe0, 0x9e, 0x4e, 0x93, 0x17, 0x93, 0x23, 0x49, 0x70, - 0x8f, 0x88, 0x25, 0xfc, 0x1c, 0x14, 0x48, 0x70, 0x0f, 0x35, 0x01, 0x42, 0xd7, 0xef, 0xd1, 0x5b, + 0xad, 0x8a, 0xe7, 0xe8, 0x61, 0xb0, 0xc3, 0xe0, 0xae, 0x4e, 0x93, 0x17, 0x93, 0x23, 0x49, 0x70, + 0x97, 0x88, 0x25, 0xfc, 0x1c, 0x14, 0x48, 0x70, 0x17, 0x35, 0x01, 0x42, 0xd7, 0xef, 0xd1, 0x9b, 0x71, 0x81, 0x56, 0x23, 0x06, 0xe4, 0x80, 0xbc, 0x64, 0x03, 0x0e, 0x9b, 0x37, 0x92, 0xea, 0x5e, 0x87, 0x85, 0x57, 0xc7, 0xa6, 0xb8, 0xea, 0x19, 0x71, 0xc9, 0x3e, 0x80, 0x46, 0xe2, 0x36, 0x03, - 0x09, 0x1c, 0x9d, 0x80, 0x0a, 0x73, 0x6f, 0x0f, 0xe8, 0xb5, 0xc4, 0x05, 0x26, 0x00, 0xbe, 0xca, - 0x6b, 0xcb, 0x5b, 0x46, 0x82, 0x95, 0x00, 0xd0, 0x13, 0xb0, 0x9c, 0xdc, 0xf9, 0x46, 0x48, 0x77, + 0x09, 0x1c, 0x9d, 0x80, 0x0a, 0x73, 0x6f, 0x0d, 0xe8, 0xd5, 0xc4, 0x05, 0x26, 0x00, 0xbe, 0xca, + 0x6b, 0xcb, 0x9b, 0x46, 0x82, 0x95, 0x00, 0xd0, 0xe3, 0xb0, 0x9c, 0xdc, 0xf9, 0x7a, 0x48, 0x77, 0xbc, 0x77, 0x84, 0x86, 0x6b, 0x64, 0x1f, 0x1c, 0x9d, 0x84, 0x43, 0x09, 0x6c, 0x5b, 0x24, 0x32, - 0xb6, 0x40, 0xcd, 0x82, 0xb9, 0x6c, 0x04, 0xbb, 0x2f, 0xde, 0x1d, 0xbb, 0x03, 0xf1, 0xf8, 0x6a, - 0xc4, 0x80, 0xe0, 0xdf, 0x5b, 0x70, 0x58, 0xaa, 0x9a, 0xb9, 0xec, 0x2b, 0x69, 0xf5, 0xbf, 0xb0, - 0x00, 0x99, 0x1c, 0x28, 0xd3, 0xfa, 0x5f, 0xb3, 0xcf, 0xc4, 0x33, 0xa5, 0xaa, 0x28, 0x99, 0x25, + 0xb6, 0x40, 0xcd, 0x82, 0xb9, 0x6c, 0x04, 0xbb, 0x2f, 0xde, 0x19, 0xbb, 0x03, 0xf1, 0xf8, 0x6a, + 0xc4, 0x80, 0xe0, 0x3f, 0x5a, 0x70, 0x58, 0xaa, 0x9a, 0xb9, 0xec, 0x2b, 0x69, 0xf5, 0xbf, 0xb4, + 0x00, 0x99, 0x1c, 0x28, 0xd3, 0xfa, 0x7f, 0xb3, 0xcf, 0xc4, 0x33, 0xa5, 0xaa, 0x28, 0x99, 0x25, 0x28, 0x69, 0x15, 0x61, 0x28, 0x75, 0x64, 0x3f, 0x4d, 0x34, 0xc6, 0x65, 0x4d, 0x2e, 0x21, 0x44, - 0x7d, 0x23, 0x07, 0x8a, 0xb7, 0x27, 0x8c, 0x46, 0xaa, 0xa2, 0x16, 0xad, 0x04, 0x01, 0x20, 0xf2, - 0x8b, 0x9f, 0x45, 0x7d, 0x26, 0xac, 0xc6, 0x4e, 0xce, 0x52, 0x20, 0xa2, 0x07, 0xf8, 0x9f, 0x79, - 0x58, 0xbc, 0x15, 0x0c, 0xc6, 0x49, 0xd0, 0xfc, 0x2a, 0x05, 0x8c, 0x54, 0x99, 0x5f, 0xd4, 0x65, + 0x7d, 0x23, 0x07, 0x8a, 0xb7, 0x26, 0x8c, 0x46, 0xaa, 0xa2, 0x16, 0xad, 0x04, 0x01, 0x20, 0xf2, + 0x8b, 0x9f, 0x45, 0x7d, 0x26, 0xac, 0xc6, 0x4e, 0xce, 0x52, 0x20, 0xa2, 0x07, 0xf8, 0x5f, 0x79, + 0x58, 0xbc, 0x19, 0x0c, 0xc6, 0x49, 0xd0, 0xfc, 0x2a, 0x05, 0x8c, 0x54, 0x99, 0x5f, 0xd4, 0x65, 0x3e, 0x02, 0x3b, 0x62, 0x74, 0x24, 0x2c, 0xab, 0x40, 0xc4, 0x18, 0x61, 0xa8, 0x31, 0x37, 0xec, 0x51, 0x26, 0x8b, 0xa7, 0x46, 0x49, 0x64, 0xb5, 0x29, 0x18, 0x5a, 0x83, 0xaa, 0xdb, 0xeb, 0x85, - 0xb4, 0xe7, 0x32, 0xda, 0x9e, 0x34, 0x16, 0xc4, 0x61, 0x26, 0x08, 0x5d, 0x85, 0xa5, 0x8e, 0xdb, - 0xe9, 0x7b, 0x7e, 0xef, 0xfa, 0x88, 0x79, 0x81, 0x1f, 0x35, 0xca, 0x22, 0x74, 0x9c, 0x58, 0x37, + 0xb4, 0xe7, 0x32, 0xda, 0x9e, 0x34, 0x16, 0xc4, 0x61, 0x26, 0x08, 0x5d, 0x81, 0xa5, 0x8e, 0xdb, + 0xe9, 0x7b, 0x7e, 0xef, 0xda, 0x88, 0x79, 0x81, 0x1f, 0x35, 0xca, 0x22, 0x74, 0x9c, 0x58, 0x37, 0x7f, 0x68, 0x5a, 0xdf, 0x48, 0xe1, 0x28, 0x3f, 0x96, 0xd9, 0x89, 0xdf, 0x80, 0x25, 0x2d, 0x78, 0x65, 0x1e, 0x67, 0x60, 0xe1, 0x6d, 0x01, 0x99, 0xd1, 0xc2, 0x93, 0xa8, 0x8a, 0x94, 0x46, 0x4b, - 0xff, 0x54, 0xa1, 0xf9, 0xc7, 0x57, 0xa1, 0x24, 0xd1, 0xd1, 0x09, 0xb3, 0x9c, 0x92, 0x19, 0x25, + 0xff, 0x54, 0xa1, 0xf9, 0xc7, 0x57, 0xa0, 0x24, 0xd1, 0xd1, 0x09, 0xb3, 0x9c, 0x92, 0x19, 0x25, 0x9f, 0xab, 0xda, 0x08, 0x43, 0x49, 0x12, 0x52, 0x46, 0x24, 0xec, 0x4c, 0x42, 0x88, 0xfa, 0xc6, - 0x7f, 0xb7, 0xe0, 0xc8, 0x26, 0x65, 0xb4, 0xc3, 0x68, 0xf7, 0x92, 0x47, 0x07, 0xdd, 0x2f, 0xb4, - 0xd2, 0x8f, 0xfb, 0x75, 0x05, 0xa3, 0x5f, 0xc7, 0x7d, 0xd8, 0xc0, 0xf3, 0xe9, 0x96, 0xd1, 0xf0, - 0x49, 0x00, 0xdc, 0xdb, 0xec, 0xf0, 0x8b, 0xcb, 0x65, 0xf9, 0xdb, 0x90, 0x01, 0x89, 0xad, 0xa5, - 0x94, 0x58, 0x0b, 0xfe, 0x9e, 0x05, 0x47, 0xb3, 0x5c, 0x2b, 0x25, 0xb5, 0xa0, 0x24, 0x36, 0xcf, - 0x68, 0x15, 0xa7, 0x76, 0x10, 0x85, 0x86, 0xce, 0xa7, 0xce, 0x17, 0xbf, 0x29, 0xb5, 0x1b, 0x7b, - 0x53, 0xa7, 0x9e, 0x40, 0x8d, 0x6e, 0x84, 0x81, 0x8b, 0xff, 0xc8, 0x6b, 0x76, 0x93, 0xa6, 0xd0, - 0x37, 0xb7, 0x55, 0xe5, 0xc7, 0xe5, 0x04, 0xfd, 0x1f, 0xd8, 0x6c, 0x32, 0x52, 0xee, 0xbb, 0x7d, - 0xe4, 0xb3, 0xa9, 0x73, 0x38, 0xb5, 0xed, 0xe6, 0x64, 0x44, 0x89, 0x40, 0xe1, 0x26, 0xde, 0x71, - 0xc3, 0xae, 0xe7, 0xbb, 0x03, 0x8f, 0x49, 0x31, 0xda, 0xc4, 0x04, 0x71, 0xbf, 0x31, 0x72, 0xc3, - 0x48, 0xe7, 0x60, 0x15, 0xe9, 0x37, 0x14, 0x88, 0xe8, 0x81, 0xe8, 0xad, 0xdc, 0xa1, 0xac, 0xd3, - 0x97, 0xfe, 0x5b, 0xf5, 0x56, 0x04, 0x24, 0xd5, 0x5b, 0x11, 0x10, 0xfc, 0x33, 0xc3, 0x8a, 0xe4, - 0x63, 0xfb, 0xd2, 0x59, 0x11, 0xfe, 0x56, 0xa2, 0x72, 0x7d, 0x45, 0xa5, 0xf2, 0x17, 0x60, 0xa9, - 0x9b, 0x5a, 0x39, 0x58, 0xf5, 0xb2, 0x6f, 0x9c, 0x41, 0xc7, 0xe3, 0x44, 0x8f, 0x02, 0x72, 0x80, - 0x1e, 0x33, 0xca, 0xc9, 0xef, 0x57, 0x4e, 0x22, 0xf5, 0xc2, 0xfd, 0xa5, 0xfe, 0xc4, 0x63, 0x50, - 0x89, 0x7f, 0x23, 0x44, 0x55, 0x58, 0xb8, 0x74, 0x9d, 0xbc, 0x7e, 0x91, 0x6c, 0x2e, 0xe7, 0x50, - 0x0d, 0xca, 0xed, 0x8b, 0x1b, 0x2f, 0x8b, 0x99, 0x75, 0xee, 0x57, 0x25, 0x9d, 0x61, 0x84, 0xe8, - 0xeb, 0x50, 0x94, 0x69, 0xc3, 0xd1, 0x84, 0x39, 0xf3, 0xe7, 0xb3, 0xd5, 0x63, 0xfb, 0xe0, 0x52, - 0x4a, 0x38, 0x77, 0xc6, 0x42, 0xd7, 0xa0, 0x2a, 0x80, 0xaa, 0x41, 0x7d, 0x22, 0xdb, 0x27, 0x4e, - 0x51, 0x7a, 0xe4, 0x80, 0x55, 0x83, 0xde, 0x05, 0x28, 0x4a, 0x81, 0x1d, 0xcd, 0x64, 0x77, 0x33, - 0x6e, 0x93, 0x6a, 0xd9, 0xe3, 0x1c, 0x7a, 0x16, 0xec, 0x9b, 0xae, 0x37, 0x40, 0x46, 0x72, 0x69, - 0xf4, 0x95, 0x57, 0x8f, 0x66, 0xc1, 0xc6, 0xb1, 0xcf, 0xc7, 0xed, 0xf1, 0x63, 0xd9, 0x1e, 0x9d, - 0xde, 0xde, 0xd8, 0xbf, 0x10, 0x9f, 0x7c, 0x5d, 0x36, 0x71, 0x75, 0xa7, 0x08, 0x3d, 0x92, 0x3e, - 0x2a, 0xd3, 0x58, 0x5a, 0x6d, 0x1e, 0xb4, 0x1c, 0x13, 0xdc, 0x82, 0xaa, 0xd1, 0xa5, 0x31, 0xc5, - 0xba, 0xbf, 0xc5, 0x64, 0x8a, 0x75, 0x46, 0x6b, 0x07, 0xe7, 0xd0, 0x65, 0x28, 0xf3, 0x94, 0x5c, - 0xfc, 0x9a, 0x73, 0x3c, 0x9b, 0x79, 0x1b, 0x19, 0xd7, 0xea, 0x89, 0xd9, 0x8b, 0x31, 0xa1, 0x6f, - 0x42, 0xe5, 0x32, 0x65, 0x2a, 0xd4, 0x1c, 0xcb, 0xc6, 0xaa, 0x19, 0x92, 0x4a, 0xc7, 0x3b, 0x9c, - 0x43, 0x6f, 0x88, 0xea, 0x20, 0xed, 0x69, 0x91, 0x73, 0x80, 0x47, 0x8d, 0xef, 0xb5, 0x76, 0x30, - 0x42, 0x4c, 0xf9, 0xf5, 0x14, 0x65, 0x15, 0xe0, 0x9d, 0x03, 0x1e, 0x6c, 0x4c, 0xd9, 0xb9, 0xcf, - 0x7f, 0x3d, 0x70, 0xee, 0xdc, 0x9b, 0xfa, 0xef, 0x0e, 0x9b, 0x2e, 0x73, 0xd1, 0x75, 0x58, 0x12, - 0xb2, 0x8c, 0xff, 0x0f, 0x91, 0xb2, 0xf9, 0x7d, 0x7f, 0xbe, 0x48, 0xd9, 0xfc, 0xfe, 0x3f, 0x61, - 0xe0, 0x5c, 0xfb, 0xcd, 0x0f, 0x3e, 0x6e, 0xe6, 0x3e, 0xfc, 0xb8, 0x99, 0xfb, 0xf4, 0xe3, 0xa6, - 0xf5, 0xdd, 0xdd, 0xa6, 0xf5, 0xcb, 0xdd, 0xa6, 0xf5, 0xfe, 0x6e, 0xd3, 0xfa, 0x60, 0xb7, 0x69, - 0xfd, 0x65, 0xb7, 0x69, 0xfd, 0x75, 0xb7, 0x99, 0xfb, 0x74, 0xb7, 0x69, 0xbd, 0xfb, 0x49, 0x33, - 0xf7, 0xc1, 0x27, 0xcd, 0xdc, 0x87, 0x9f, 0x34, 0x73, 0xdf, 0x7e, 0xfc, 0xfe, 0x95, 0xb0, 0x74, - 0x8b, 0x25, 0xf1, 0xf5, 0xd4, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xf9, 0xfc, 0x48, 0x69, 0xc6, - 0x23, 0x00, 0x00, + 0x3f, 0xca, 0xc3, 0x91, 0x4d, 0xca, 0x68, 0x87, 0xd1, 0xee, 0x45, 0x8f, 0x0e, 0xba, 0x5f, 0x68, + 0xa5, 0x1f, 0xf7, 0xeb, 0x0a, 0x46, 0xbf, 0x8e, 0xfb, 0xb0, 0x81, 0xe7, 0xd3, 0x2d, 0xa3, 0xe1, + 0x93, 0x00, 0x12, 0x19, 0x15, 0xcd, 0x56, 0x90, 0xb6, 0x91, 0x92, 0x61, 0x23, 0x49, 0x9b, 0x6f, + 0x21, 0xd5, 0x99, 0xd4, 0x75, 0x65, 0x39, 0x29, 0x4a, 0xf1, 0xef, 0x2c, 0x38, 0x9a, 0x95, 0x8b, + 0x52, 0xe3, 0x8b, 0x50, 0xda, 0x11, 0x90, 0xfd, 0xcd, 0xe4, 0xd4, 0x0e, 0xd9, 0x8f, 0x90, 0xa8, + 0x66, 0x3f, 0x42, 0x42, 0xd0, 0x63, 0xa9, 0x9f, 0xa1, 0xda, 0x2b, 0x7b, 0x53, 0xe7, 0x90, 0x00, + 0x18, 0xb8, 0x8a, 0x99, 0xd3, 0xf1, 0xc5, 0x0b, 0x49, 0xa3, 0x43, 0x42, 0x4c, 0xc2, 0xaa, 0x6b, + 0xf9, 0x67, 0x0b, 0x16, 0x53, 0x17, 0x11, 0x22, 0xe2, 0x4f, 0x40, 0x85, 0x07, 0x39, 0x41, 0x8f, + 0x81, 0xcd, 0x26, 0x23, 0x15, 0x15, 0xda, 0x47, 0x3e, 0x9b, 0x3a, 0x87, 0x53, 0xdb, 0x6e, 0x4c, + 0x46, 0x94, 0x08, 0x14, 0xfe, 0x72, 0x3a, 0x6e, 0xd8, 0xf5, 0x7c, 0x77, 0xe0, 0x31, 0xa9, 0x1d, + 0x9b, 0x98, 0x20, 0xee, 0x8e, 0x46, 0x6e, 0x18, 0xe9, 0xd4, 0xae, 0x22, 0xdd, 0x91, 0x02, 0x11, + 0x3d, 0x10, 0x2d, 0x9b, 0xdb, 0x94, 0x75, 0xfa, 0x32, 0x2c, 0xa8, 0x96, 0x8d, 0x80, 0xa4, 0x5a, + 0x36, 0x02, 0x82, 0x7f, 0x6e, 0x25, 0xc6, 0x29, 0xdf, 0xf0, 0x97, 0xce, 0x38, 0xf1, 0xb7, 0x13, + 0x3b, 0xd1, 0x57, 0x54, 0x76, 0xf2, 0x02, 0x2c, 0x75, 0x53, 0x2b, 0x07, 0xdb, 0x8b, 0x6c, 0x47, + 0x67, 0xd0, 0xf1, 0x38, 0xd1, 0xa3, 0x80, 0x1c, 0xa0, 0xc7, 0x8c, 0x72, 0xf2, 0xfb, 0x95, 0x93, + 0x48, 0xbd, 0x70, 0x6f, 0xa9, 0x3f, 0xfe, 0x08, 0x54, 0xe2, 0x9f, 0x1e, 0x51, 0x15, 0x16, 0x2e, + 0x5e, 0x23, 0xaf, 0x5f, 0x20, 0x9b, 0xcb, 0x39, 0x54, 0x83, 0x72, 0xfb, 0xc2, 0xc6, 0xcb, 0x62, + 0x66, 0x9d, 0xfb, 0x75, 0x49, 0x27, 0x2e, 0x21, 0xfa, 0x06, 0x14, 0x65, 0x36, 0x72, 0x34, 0x61, + 0xce, 0xfc, 0x55, 0x6e, 0xf5, 0xd8, 0x3e, 0xb8, 0x94, 0x12, 0xce, 0x9d, 0xb1, 0xd0, 0x55, 0xa8, + 0x0a, 0xa0, 0xea, 0x7b, 0x9f, 0xc8, 0xb6, 0x9f, 0x53, 0x94, 0x1e, 0x3a, 0x60, 0xd5, 0xa0, 0x77, + 0x1e, 0x8a, 0x52, 0x60, 0x47, 0x33, 0x49, 0xe3, 0x8c, 0xdb, 0xa4, 0x7e, 0x09, 0xc0, 0x39, 0xf4, + 0x2c, 0xd8, 0x37, 0x5c, 0x6f, 0x80, 0x8c, 0x9c, 0xd5, 0x68, 0x57, 0xaf, 0x1e, 0xcd, 0x82, 0x8d, + 0x63, 0x9f, 0x8f, 0xbb, 0xee, 0xc7, 0xb2, 0xad, 0x3f, 0xbd, 0xbd, 0xb1, 0x7f, 0x21, 0x3e, 0xf9, + 0x9a, 0xec, 0x0d, 0xeb, 0x06, 0x14, 0x7a, 0x28, 0x7d, 0x54, 0xa6, 0x5f, 0xb5, 0xda, 0x3c, 0x68, + 0x39, 0x26, 0xb8, 0x05, 0x55, 0xa3, 0xf9, 0x63, 0x8a, 0x75, 0x7f, 0xe7, 0xca, 0x14, 0xeb, 0x8c, + 0x8e, 0x11, 0xce, 0xa1, 0x4b, 0x50, 0xe6, 0x99, 0xbe, 0xf8, 0x91, 0xe8, 0x78, 0x36, 0xa1, 0x37, + 0x12, 0xb9, 0xd5, 0x13, 0xb3, 0x17, 0x63, 0x42, 0xdf, 0x82, 0xca, 0x25, 0xca, 0x54, 0x04, 0x3b, + 0x96, 0x0d, 0x81, 0x33, 0x24, 0x95, 0x0e, 0xa3, 0x38, 0x87, 0xde, 0x10, 0x45, 0x47, 0xda, 0x3d, + 0x23, 0xe7, 0x00, 0x37, 0x1c, 0xdf, 0x6b, 0xed, 0x60, 0x84, 0x98, 0xf2, 0xeb, 0x29, 0xca, 0x2a, + 0x6f, 0x70, 0x0e, 0x78, 0xb0, 0x31, 0x65, 0xe7, 0x1e, 0x7f, 0x21, 0xc1, 0xb9, 0x73, 0x6f, 0xea, + 0x7f, 0x51, 0x6c, 0xba, 0xcc, 0x45, 0xd7, 0x60, 0x49, 0xc8, 0x32, 0xfe, 0x9b, 0x45, 0xca, 0xe6, + 0xf7, 0xfd, 0xa7, 0x23, 0x65, 0xf3, 0xfb, 0xff, 0xdb, 0x81, 0x73, 0xed, 0x37, 0x3f, 0xf8, 0xb8, + 0x99, 0xfb, 0xf0, 0xe3, 0x66, 0xee, 0xd3, 0x8f, 0x9b, 0xd6, 0xf7, 0x76, 0x9b, 0xd6, 0xaf, 0x76, + 0x9b, 0xd6, 0xfb, 0xbb, 0x4d, 0xeb, 0x83, 0xdd, 0xa6, 0xf5, 0xb7, 0xdd, 0xa6, 0xf5, 0xf7, 0xdd, + 0x66, 0xee, 0xd3, 0xdd, 0xa6, 0xf5, 0xee, 0x27, 0xcd, 0xdc, 0x07, 0x9f, 0x34, 0x73, 0x1f, 0x7e, + 0xd2, 0xcc, 0x7d, 0xe7, 0xd1, 0x7b, 0x17, 0xd8, 0xd2, 0x2d, 0x96, 0xc4, 0xd7, 0x93, 0xff, 0x0e, + 0x00, 0x00, 0xff, 0xff, 0xc7, 0x66, 0x64, 0x64, 0x1d, 0x24, 0x00, 0x00, } func (x Direction) String() string { @@ -4932,12 +4957,18 @@ func (this *DetectedFieldsRequest) Equal(that interface{}) bool { if this.LineLimit != that1.LineLimit { return false } - if this.FieldLimit != that1.FieldLimit { + if this.Limit != that1.Limit { return false } if this.Step != that1.Step { return false } + if this.Values != that1.Values { + return false + } + if this.Name != that1.Name { + return false + } return true } func (this *DetectedFieldsResponse) Equal(that interface{}) bool { @@ -4967,9 +4998,17 @@ func (this *DetectedFieldsResponse) Equal(that interface{}) bool { return false } } - if this.FieldLimit != that1.FieldLimit { + if this.Limit != that1.Limit { + return false + } + if len(this.Values) != len(that1.Values) { return false } + for i := range this.Values { + if this.Values[i] != that1.Values[i] { + return false + } + } return true } func (this *DetectedField) Equal(that interface{}) bool { @@ -5741,14 +5780,16 @@ func (this *DetectedFieldsRequest) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 10) + s := make([]string, 0, 12) s = append(s, "&logproto.DetectedFieldsRequest{") s = append(s, "Start: "+fmt.Sprintf("%#v", this.Start)+",\n") s = append(s, "End: "+fmt.Sprintf("%#v", this.End)+",\n") s = append(s, "Query: "+fmt.Sprintf("%#v", this.Query)+",\n") s = append(s, "LineLimit: "+fmt.Sprintf("%#v", this.LineLimit)+",\n") - s = append(s, "FieldLimit: "+fmt.Sprintf("%#v", this.FieldLimit)+",\n") + s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") s = append(s, "Step: "+fmt.Sprintf("%#v", this.Step)+",\n") + s = append(s, "Values: "+fmt.Sprintf("%#v", this.Values)+",\n") + s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -5756,12 +5797,13 @@ func (this *DetectedFieldsResponse) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 6) + s := make([]string, 0, 7) s = append(s, "&logproto.DetectedFieldsResponse{") if this.Fields != nil { s = append(s, "Fields: "+fmt.Sprintf("%#v", this.Fields)+",\n") } - s = append(s, "FieldLimit: "+fmt.Sprintf("%#v", this.FieldLimit)+",\n") + s = append(s, "Limit: "+fmt.Sprintf("%#v", this.Limit)+",\n") + s = append(s, "Values: "+fmt.Sprintf("%#v", this.Values)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -8637,13 +8679,30 @@ func (m *DetectedFieldsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintLogproto(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x42 + } + if m.Values { + i-- + if m.Values { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x38 + } if m.Step != 0 { i = encodeVarintLogproto(dAtA, i, uint64(m.Step)) i-- dAtA[i] = 0x30 } - if m.FieldLimit != 0 { - i = encodeVarintLogproto(dAtA, i, uint64(m.FieldLimit)) + if m.Limit != 0 { + i = encodeVarintLogproto(dAtA, i, uint64(m.Limit)) i-- dAtA[i] = 0x28 } @@ -8698,8 +8757,17 @@ func (m *DetectedFieldsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l - if m.FieldLimit != 0 { - i = encodeVarintLogproto(dAtA, i, uint64(m.FieldLimit)) + if len(m.Values) > 0 { + for iNdEx := len(m.Values) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Values[iNdEx]) + copy(dAtA[i:], m.Values[iNdEx]) + i = encodeVarintLogproto(dAtA, i, uint64(len(m.Values[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.Limit != 0 { + i = encodeVarintLogproto(dAtA, i, uint64(m.Limit)) i-- dAtA[i] = 0x10 } @@ -9891,12 +9959,19 @@ func (m *DetectedFieldsRequest) Size() (n int) { if m.LineLimit != 0 { n += 1 + sovLogproto(uint64(m.LineLimit)) } - if m.FieldLimit != 0 { - n += 1 + sovLogproto(uint64(m.FieldLimit)) + if m.Limit != 0 { + n += 1 + sovLogproto(uint64(m.Limit)) } if m.Step != 0 { n += 1 + sovLogproto(uint64(m.Step)) } + if m.Values { + n += 2 + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovLogproto(uint64(l)) + } return n } @@ -9912,8 +9987,14 @@ func (m *DetectedFieldsResponse) Size() (n int) { n += 1 + l + sovLogproto(uint64(l)) } } - if m.FieldLimit != 0 { - n += 1 + sovLogproto(uint64(m.FieldLimit)) + if m.Limit != 0 { + n += 1 + sovLogproto(uint64(m.Limit)) + } + if len(m.Values) > 0 { + for _, s := range m.Values { + l = len(s) + n += 1 + l + sovLogproto(uint64(l)) + } } return n } @@ -10663,8 +10744,10 @@ func (this *DetectedFieldsRequest) String() string { `End:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.End), "Timestamp", "types.Timestamp", 1), `&`, ``, 1) + `,`, `Query:` + fmt.Sprintf("%v", this.Query) + `,`, `LineLimit:` + fmt.Sprintf("%v", this.LineLimit) + `,`, - `FieldLimit:` + fmt.Sprintf("%v", this.FieldLimit) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, `Step:` + fmt.Sprintf("%v", this.Step) + `,`, + `Values:` + fmt.Sprintf("%v", this.Values) + `,`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, `}`, }, "") return s @@ -10680,7 +10763,8 @@ func (this *DetectedFieldsResponse) String() string { repeatedStringForFields += "}" s := strings.Join([]string{`&DetectedFieldsResponse{`, `Fields:` + repeatedStringForFields + `,`, - `FieldLimit:` + fmt.Sprintf("%v", this.FieldLimit) + `,`, + `Limit:` + fmt.Sprintf("%v", this.Limit) + `,`, + `Values:` + fmt.Sprintf("%v", this.Values) + `,`, `}`, }, "") return s @@ -17352,9 +17436,9 @@ func (m *DetectedFieldsRequest) Unmarshal(dAtA []byte) error { } case 5: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field FieldLimit", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) } - m.FieldLimit = 0 + m.Limit = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogproto @@ -17364,7 +17448,7 @@ func (m *DetectedFieldsRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.FieldLimit |= uint32(b&0x7F) << shift + m.Limit |= uint32(b&0x7F) << shift if b < 0x80 { break } @@ -17388,6 +17472,58 @@ func (m *DetectedFieldsRequest) Unmarshal(dAtA []byte) error { break } } + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Values = bool(v != 0) + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLogproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLogproto(dAtA[iNdEx:]) @@ -17477,9 +17613,28 @@ func (m *DetectedFieldsResponse) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 2: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field FieldLimit", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType) + } + m.Limit = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogproto + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Limit |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) } - m.FieldLimit = 0 + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogproto @@ -17489,11 +17644,24 @@ func (m *DetectedFieldsResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.FieldLimit |= uint32(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLogproto + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogproto + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Values = append(m.Values, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLogproto(dAtA[iNdEx:]) diff --git a/pkg/logproto/logproto.proto b/pkg/logproto/logproto.proto index 18f9d6c01adb6..5bf2d37e2d6bf 100644 --- a/pkg/logproto/logproto.proto +++ b/pkg/logproto/logproto.proto @@ -459,13 +459,16 @@ message DetectedFieldsRequest { ]; string query = 3; // Naming this query instead of match because this should be with queryrangebase.Request interface uint32 lineLimit = 4; - uint32 fieldLimit = 5; + uint32 limit = 5; int64 step = 6; + bool values = 7; // True to fetch detected field values, false for fetch detected field label names. + string name = 8; // Name of the detected field to fetch values for. } message DetectedFieldsResponse { - repeated DetectedField fields = 1; - uint32 fieldLimit = 2 [(gogoproto.jsontag) = "fieldLimit,omitempty"]; + repeated DetectedField fields = 1 [(gogoproto.jsontag) = "fields,omitempty"]; + uint32 limit = 2 [(gogoproto.jsontag) = "limit,omitempty"]; + repeated string values = 3 [(gogoproto.jsontag) = "values,omitempty"]; } // TODO: make the detected field include the serialized sketch diff --git a/pkg/loki/modules.go b/pkg/loki/modules.go index 1a72cb3c5b91e..4c718722a509f 100644 --- a/pkg/loki/modules.go +++ b/pkg/loki/modules.go @@ -1177,6 +1177,7 @@ func (t *Loki) initQueryFrontend() (_ services.Service, err error) { t.Server.HTTP.Path("/loki/api/v1/patterns").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/detected_labels").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/detected_fields").Methods("GET", "POST").Handler(frontendHandler) + t.Server.HTTP.Path("/loki/api/v1/detected_field/{name}/values").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/index/stats").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/index/shards").Methods("GET", "POST").Handler(frontendHandler) t.Server.HTTP.Path("/loki/api/v1/index/volume").Methods("GET", "POST").Handler(frontendHandler) diff --git a/pkg/lokifrontend/frontend/v1/frontend_test.go b/pkg/lokifrontend/frontend/v1/frontend_test.go index b7e4061d804a0..00235af5ce99c 100644 --- a/pkg/lokifrontend/frontend/v1/frontend_test.go +++ b/pkg/lokifrontend/frontend/v1/frontend_test.go @@ -40,9 +40,9 @@ import ( ) const ( - query = "/api/v1/query_range?end=1536716898&query=sum%28container_memory_rss%29+by+%28namespace%29&start=1536673680&step=120" + query = "/loki/api/v1/query_range?end=1536716898&query=sum%28container_memory_rss%29+by+%28namespace%29&start=1536673680&step=120" responseBody = `{"status":"success","data":{"resultType":"Matrix","result":[{"metric":{"foo":"bar"},"values":[[1536673680,"137"],[1536673780,"137"]]}]}}` - labelQuery = `/api/v1/label/foo/values` + labelQuery = `/prom/label/foo/values` ) func TestFrontend(t *testing.T) { diff --git a/pkg/querier-rf1/http.go b/pkg/querier-rf1/http.go index baa820a99460f..2e10e5c576348 100644 --- a/pkg/querier-rf1/http.go +++ b/pkg/querier-rf1/http.go @@ -277,8 +277,8 @@ func (q *QuerierAPI) DetectedFieldsHandler(ctx context.Context, req *logproto.De "msg", "queried store for detected fields that does not support it, no response from querier.DetectedFields", ) return &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{}, - FieldLimit: req.GetFieldLimit(), + Fields: []*logproto.DetectedField{}, + Limit: req.GetLimit(), }, nil } return resp, nil diff --git a/pkg/querier-rf1/querier.go b/pkg/querier-rf1/querier.go index a2ff7100376ad..34149733dd041 100644 --- a/pkg/querier-rf1/querier.go +++ b/pkg/querier-rf1/querier.go @@ -691,7 +691,7 @@ func (q *Rf1Querier) DetectedFields(ctx context.Context, req *logproto.DetectedF return nil, err } - detectedFields := parseDetectedFields(ctx, req.FieldLimit, streams) + detectedFields := parseDetectedFields(ctx, req.Limit, streams) fields := make([]*logproto.DetectedField, len(detectedFields)) fieldCount := 0 @@ -714,8 +714,8 @@ func (q *Rf1Querier) DetectedFields(ctx context.Context, req *logproto.DetectedF } return &logproto.DetectedFieldsResponse{ - Fields: fields, - FieldLimit: req.GetFieldLimit(), + Fields: fields, + Limit: req.GetLimit(), }, nil } diff --git a/pkg/querier/http.go b/pkg/querier/http.go index 862f9e2a20138..c78005ffc99a3 100644 --- a/pkg/querier/http.go +++ b/pkg/querier/http.go @@ -377,8 +377,8 @@ func (q *QuerierAPI) DetectedFieldsHandler(ctx context.Context, req *logproto.De "msg", "queried store for detected fields that does not support it, no response from querier.DetectedFields", ) return &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{}, - FieldLimit: req.GetFieldLimit(), + Fields: []*logproto.DetectedField{}, + Limit: req.GetLimit(), }, nil } return resp, nil diff --git a/pkg/querier/multi_tenant_querier.go b/pkg/querier/multi_tenant_querier.go index 961b35a916514..86fb7ecea68ae 100644 --- a/pkg/querier/multi_tenant_querier.go +++ b/pkg/querier/multi_tenant_querier.go @@ -303,8 +303,8 @@ func (q *MultiTenantQuerier) DetectedFields(ctx context.Context, req *logproto.D ) return &logproto.DetectedFieldsResponse{ - Fields: []*logproto.DetectedField{}, - FieldLimit: req.GetFieldLimit(), + Fields: []*logproto.DetectedField{}, + Limit: req.GetLimit(), }, nil } diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index b6000b5479ecb..7c7f973b8c90c 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -1104,7 +1104,7 @@ func (q *SingleTenantQuerier) DetectedFields(ctx context.Context, req *logproto. return nil, err } - detectedFields := parseDetectedFields(req.FieldLimit, streams) + detectedFields := parseDetectedFields(req.Limit, streams) fields := make([]*logproto.DetectedField, len(detectedFields)) fieldCount := 0 @@ -1130,8 +1130,8 @@ func (q *SingleTenantQuerier) DetectedFields(ctx context.Context, req *logproto. } return &logproto.DetectedFieldsResponse{ - Fields: fields, - FieldLimit: req.GetFieldLimit(), + Fields: fields, + Limit: req.GetLimit(), }, nil } diff --git a/pkg/querier/queryrange/codec.go b/pkg/querier/queryrange/codec.go index 5212d9bdebb52..c957d07af6119 100644 --- a/pkg/querier/queryrange/codec.go +++ b/pkg/querier/queryrange/codec.go @@ -331,7 +331,6 @@ func (Codec) DecodeRequest(_ context.Context, r *http.Request, _ []string) (quer } disableCacheReq := false - if strings.ToLower(strings.TrimSpace(r.Header.Get(cacheControlHeader))) == noCacheVal { disableCacheReq = true } @@ -920,11 +919,11 @@ func (c Codec) EncodeRequest(ctx context.Context, r queryrangebase.Request) (*ht return req.WithContext(ctx), nil case *DetectedFieldsRequest: params := url.Values{ - "query": []string{request.GetQuery()}, - "start": []string{fmt.Sprintf("%d", request.Start.UnixNano())}, - "end": []string{fmt.Sprintf("%d", request.End.UnixNano())}, - "line_limit": []string{fmt.Sprintf("%d", request.GetLineLimit())}, - "field_limit": []string{fmt.Sprintf("%d", request.GetFieldLimit())}, + "query": []string{request.GetQuery()}, + "start": []string{fmt.Sprintf("%d", request.Start.UnixNano())}, + "end": []string{fmt.Sprintf("%d", request.End.UnixNano())}, + "line_limit": []string{fmt.Sprintf("%d", request.GetLineLimit())}, + "limit": []string{fmt.Sprintf("%d", request.GetLimit())}, } if request.Step != 0 { @@ -932,7 +931,7 @@ func (c Codec) EncodeRequest(ctx context.Context, r queryrangebase.Request) (*ht } u := &url.URL{ - Path: "/loki/api/v1/detected_fields", + Path: request.Path(), RawQuery: params.Encode(), } req := &http.Request{ @@ -1014,6 +1013,10 @@ func (c Codec) Path(r queryrangebase.Request) string { case *logproto.VolumeRequest: return "/loki/api/v1/index/volume_range" case *DetectedFieldsRequest: + if request.Values { + // This request contains user-generated input in the URL, which is not safe to reflect in the route path. + return "loki/api/v1/detected_field/values" + } return "/loki/api/v1/detected_fields" case *logproto.QueryPatternsRequest: return "/loki/api/v1/patterns" @@ -1548,23 +1551,30 @@ func (Codec) MergeResponse(responses ...queryrangebase.Response) (queryrangebase case *DetectedFieldsResponse: resp0 := responses[0].(*DetectedFieldsResponse) headers := resp0.Headers - fieldLimit := resp0.Response.GetFieldLimit() + limit := resp0.Response.GetLimit() fields := []*logproto.DetectedField{} + values := []string{} for _, r := range responses { fields = append(fields, r.(*DetectedFieldsResponse).Response.Fields...) + values = append(values, r.(*DetectedFieldsResponse).Response.Values...) } - mergedFields, err := detected.MergeFields(fields, fieldLimit) + mergedFields, err := detected.MergeFields(fields, limit) + if err != nil { + return nil, err + } + mergedValues, err := detected.MergeValues(values, limit) if err != nil { return nil, err } return &DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ - Fields: mergedFields, - FieldLimit: 0, + Fields: mergedFields, + Values: mergedValues, + Limit: limit, }, Headers: headers, }, nil @@ -2292,12 +2302,12 @@ type DetectedFieldsRequest struct { func NewDetectedFieldsRequest(start, end time.Time, lineLimit, fieldLimit uint32, step int64, query, path string) *DetectedFieldsRequest { return &DetectedFieldsRequest{ DetectedFieldsRequest: logproto.DetectedFieldsRequest{ - Start: start, - End: end, - Query: query, - LineLimit: lineLimit, - FieldLimit: fieldLimit, - Step: step, + Start: start, + End: end, + Query: query, + LineLimit: lineLimit, + Limit: fieldLimit, + Step: step, }, path: path, } @@ -2331,8 +2341,8 @@ func (r *DetectedFieldsRequest) GetLineLimit() uint32 { return r.LineLimit } -func (r *DetectedFieldsRequest) GetFieldLimit() uint32 { - return r.FieldLimit +func (r *DetectedFieldsRequest) GetLimit() uint32 { + return r.Limit } func (r *DetectedFieldsRequest) Path() string { @@ -2364,7 +2374,7 @@ func (r *DetectedFieldsRequest) LogToSpan(sp opentracing.Span) { otlog.String("query", r.GetQuery()), otlog.Int64("step (ms)", r.GetStep()), otlog.Int64("line_limit", int64(r.GetLineLimit())), - otlog.Int64("field_limit", int64(r.GetFieldLimit())), + otlog.Int64("limit", int64(r.GetLimit())), otlog.String("step", fmt.Sprintf("%d", r.GetStep())), ) } diff --git a/pkg/querier/queryrange/codec_test.go b/pkg/querier/queryrange/codec_test.go index f5d00e263d0f6..e52bb1ec50a0a 100644 --- a/pkg/querier/queryrange/codec_test.go +++ b/pkg/querier/queryrange/codec_test.go @@ -47,7 +47,6 @@ var ( ) func Test_codec_EncodeDecodeRequest(t *testing.T) { - ctx := user.InjectOrgID(context.Background(), "1") tests := []struct { @@ -113,18 +112,22 @@ func Test_codec_EncodeDecodeRequest(t *testing.T) { StartTs: start, EndTs: end, }, false}, - {"labels", func() (*http.Request, error) { - return http.NewRequest(http.MethodGet, - fmt.Sprintf(`/label?start=%d&end=%d&query={foo="bar"}`, start.UnixNano(), end.UnixNano()), nil) - }, NewLabelRequest(start, end, `{foo="bar"}`, "", "/label"), - false}, - {"label_values", func() (*http.Request, error) { - req, err := http.NewRequest(http.MethodGet, - fmt.Sprintf(`/label/test/values?start=%d&end=%d&query={foo="bar"}`, start.UnixNano(), end.UnixNano()), nil) - req = mux.SetURLVars(req, map[string]string{"name": "test"}) - return req, err - }, NewLabelRequest(start, end, `{foo="bar"}`, "test", "/label/test/values"), - false}, + { + "labels", func() (*http.Request, error) { + return http.NewRequest(http.MethodGet, + fmt.Sprintf(`/loki/api/v1/labels?start=%d&end=%d&query={foo="bar"}`, start.UnixNano(), end.UnixNano()), nil) + }, NewLabelRequest(start, end, `{foo="bar"}`, "", "/loki/api/v1/labels"), + false, + }, + { + "label_values", func() (*http.Request, error) { + req, err := http.NewRequest(http.MethodGet, + fmt.Sprintf(`/loki/api/v1/label/test/values?start=%d&end=%d&query={foo="bar"}`, start.UnixNano(), end.UnixNano()), nil) + req = mux.SetURLVars(req, map[string]string{"name": "test"}) + return req, err + }, NewLabelRequest(start, end, `{foo="bar"}`, "test", "/loki/api/v1/label/test/values"), + false, + }, {"index_stats", func() (*http.Request, error) { return DefaultCodec.EncodeRequest(ctx, &logproto.IndexStatsRequest{ From: model.TimeFromUnixNano(start.UnixNano()), @@ -205,26 +208,57 @@ func Test_codec_EncodeDecodeRequest(t *testing.T) { {"detected_fields", func() (*http.Request, error) { return DefaultCodec.EncodeRequest(ctx, &DetectedFieldsRequest{ logproto.DetectedFieldsRequest{ - Query: `{foo="bar"}`, - Start: start, - End: end, - Step: 30 * 1e3, // step is expected in ms; default is 0 or no step - LineLimit: 100, - FieldLimit: 100, + Query: `{foo="bar"}`, + Start: start, + End: end, + Step: 30 * 1e3, // step is expected in ms; default is 0 or no step + LineLimit: 100, + Limit: 100, }, "/loki/api/v1/detected_fields", }) }, &DetectedFieldsRequest{ logproto.DetectedFieldsRequest{ - Query: `{foo="bar"}`, - Start: start, - End: end, - Step: 30 * 1e3, // step is expected in ms; default is 0 or no step - LineLimit: 100, - FieldLimit: 100, + Query: `{foo="bar"}`, + Start: start, + End: end, + Step: 30 * 1e3, // step is expected in ms; default is 0 or no step + LineLimit: 100, + Limit: 100, }, "/loki/api/v1/detected_fields", }, false}, + {"detected field values", func() (*http.Request, error) { + req, err := DefaultCodec.EncodeRequest(ctx, &DetectedFieldsRequest{ + logproto.DetectedFieldsRequest{ + Query: `{baz="bar"}`, + Start: start, + End: end, + Step: 30 * 1e3, // step is expected in ms; default is 0 or no step + LineLimit: 100, + Limit: 100, + }, + "/loki/api/v1/detected_field/foo/values", + }) + if err != nil { + return nil, err + } + + req = mux.SetURLVars(req, map[string]string{"name": "foo"}) + return req, nil + }, &DetectedFieldsRequest{ + logproto.DetectedFieldsRequest{ + Query: `{baz="bar"}`, + Start: start, + End: end, + Step: 30 * 1e3, // step is expected in ms; default is 0 or no step + LineLimit: 100, + Limit: 100, + Values: true, + Name: "foo", + }, + "/loki/api/v1/detected_field/foo/values", + }, false}, {"patterns", func() (*http.Request, error) { return DefaultCodec.EncodeRequest(ctx, &logproto.QueryPatternsRequest{ Start: start, @@ -586,7 +620,6 @@ func TestLokiSeriesRequestSpanLogging(t *testing.T) { } if field.Key == "end" { require.Equal(t, timestamp.Time(end.UnixMilli()).String(), field.ValueString) - } } } @@ -619,7 +652,7 @@ func TestLabelRequestSpanLogging(t *testing.T) { func Test_codec_DecodeProtobufResponseParity(t *testing.T) { // test fixtures from pkg/util/marshal_test - var queryTests = []struct { + queryTests := []struct { name string actual parser.Value expected string @@ -1049,7 +1082,6 @@ func Test_codec_seriesVolume_EncodeRequest(t *testing.T) { func Test_codec_seriesVolume_DecodeRequest(t *testing.T) { ctx := user.InjectOrgID(context.Background(), "1") t.Run("instant queries set a step of 0", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/loki/api/v1/index/volume"+ "?start=0"+ "&end=1"+ @@ -1106,7 +1138,8 @@ func Test_codec_EncodeResponse(t *testing.T) { }, }, Statistics: statsResult, - }, matrixString, false, nil}, + }, matrixString, false, nil, + }, { "loki v1", "/loki/api/v1/query_range", &LokiResponse{ @@ -1774,7 +1807,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { Fields: []*logproto.DetectedField{ buildDetctedField("foo", 1), }, - FieldLimit: 2, + Limit: 2, }, }, &DetectedFieldsResponse{ @@ -1782,7 +1815,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { Fields: []*logproto.DetectedField{ buildDetctedField("foo", 3), }, - FieldLimit: 2, + Limit: 2, }, }, } @@ -1806,7 +1839,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { buildDetctedField("foo", 1), buildDetctedField("bar", 42), }, - FieldLimit: 2, + Limit: 2, }, }, &DetectedFieldsResponse{ @@ -1815,7 +1848,7 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { buildDetctedField("foo", 27), buildDetctedField("baz", 3), }, - FieldLimit: 2, + Limit: 2, }, }, } @@ -1842,7 +1875,6 @@ func Test_codec_MergeResponse_DetectedFieldsResponse(t *testing.T) { require.Nil(t, baz) }) - } type badResponse struct{} @@ -2593,7 +2625,6 @@ func Benchmark_CodecDecodeSeries(b *testing.B) { } }) } - } func Benchmark_MergeResponses(b *testing.B) { diff --git a/pkg/querier/queryrange/detected_fields.go b/pkg/querier/queryrange/detected_fields.go index 115ba9601573c..3248d3b2cda81 100644 --- a/pkg/querier/queryrange/detected_fields.go +++ b/pkg/querier/queryrange/detected_fields.go @@ -48,40 +48,86 @@ func NewDetectedFieldsHandler( return resp, nil } - detectedFields := parseDetectedFields(r.FieldLimit, re.Data.Result) - fields := make([]*logproto.DetectedField, len(detectedFields)) - fieldCount := 0 - for k, v := range detectedFields { - p := v.parsers - if len(p) == 0 { - p = nil - } - fields[fieldCount] = &logproto.DetectedField{ - Label: k, - Type: v.fieldType, - Cardinality: v.Estimate(), - Parsers: p, - } + var fields []*logproto.DetectedField + var values []string + + if r.Values && r.Name != "" { + values = parseDetectedFieldValues(r.Limit, re.Data.Result, r.Name) + } else { + detectedFields := parseDetectedFields(r.Limit, re.Data.Result) + fields = make([]*logproto.DetectedField, len(detectedFields)) + fieldCount := 0 + for k, v := range detectedFields { + p := v.parsers + if len(p) == 0 { + p = nil + } + fields[fieldCount] = &logproto.DetectedField{ + Label: k, + Type: v.fieldType, + Cardinality: v.Estimate(), + Parsers: p, + } - fieldCount++ + fieldCount++ + } } dfResp := DetectedFieldsResponse{ Response: &logproto.DetectedFieldsResponse{ Fields: fields, + Values: values, }, Headers: re.Headers, } // Otherwise all they get is the field limit, which is a bit confusing - if len(fields) > 0 { - dfResp.Response.FieldLimit = r.GetFieldLimit() + if len(fields) > 0 || len(values) > 0 { + dfResp.Response.Limit = r.GetLimit() } return &dfResp, nil }) } +func parseDetectedFieldValues(limit uint32, streams []push.Stream, name string) []string { + values := map[string]struct{}{} + for _, stream := range streams { + streamLbls, err := syntax.ParseLabels(stream.Labels) + if err != nil { + streamLbls = labels.EmptyLabels() + } + + for _, entry := range stream.Entries { + if len(values) >= int(limit) { + break + } + + structuredMetadata := getStructuredMetadata(entry) + if vals, ok := structuredMetadata[name]; ok { + for _, v := range vals { + values[v] = struct{}{} + } + } + + entryLbls := logql_log.NewBaseLabelsBuilder().ForLabels(streamLbls, streamLbls.Hash()) + parsedLabels, _ := parseEntry(entry, entryLbls) + if vals, ok := parsedLabels[name]; ok { + for _, v := range vals { + values[v] = struct{}{} + } + } + } + } + + response := make([]string, 0, len(values)) + for v := range values { + response = append(response, v) + } + + return response +} + func makeDownstreamRequest( ctx context.Context, limits Limits, diff --git a/pkg/querier/queryrange/detected_fields_test.go b/pkg/querier/queryrange/detected_fields_test.go index 654a42ac8d00a..b0b363e4735d1 100644 --- a/pkg/querier/queryrange/detected_fields_test.go +++ b/pkg/querier/queryrange/detected_fields_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "slices" "testing" "time" @@ -15,7 +16,6 @@ import ( "github.com/grafana/loki/v3/pkg/loghttp" "github.com/grafana/loki/v3/pkg/logproto" - "github.com/grafana/loki/v3/pkg/logql/log" logql_log "github.com/grafana/loki/v3/pkg/logql/log" "github.com/grafana/loki/v3/pkg/logql/syntax" "github.com/grafana/loki/v3/pkg/logqlmodel" @@ -354,7 +354,10 @@ func Test_parseDetectedFeilds(t *testing.T) { duration = field } - streamLbls.Add(log.ParsedLabel, labels.Label{Name: field.Name, Value: field.Value}) + streamLbls.Add( + logql_log.ParsedLabel, + labels.Label{Name: field.Name, Value: field.Value}, + ) } rulerStreams = append(rulerStreams, push.Stream{ @@ -495,7 +498,7 @@ func Test_parseDetectedFeilds(t *testing.T) { } nginxStreamLbls.Add( - log.ParsedLabel, + logql_log.ParsedLabel, labels.Label{Name: field.Name, Value: field.Value}, ) } @@ -958,6 +961,38 @@ func mockLogfmtStreamWithLabelsAndStructuredMetadata( } } +func limitedHandler(stream logproto.Stream) base.Handler { + return base.HandlerFunc( + func(_ context.Context, _ base.Request) (base.Response, error) { + return &LokiResponse{ + Status: "success", + Data: LokiData{ + ResultType: loghttp.ResultTypeStream, + Result: []logproto.Stream{ + stream, + }, + }, + Direction: logproto.BACKWARD, + }, nil + }) +} + +func logHandler(stream logproto.Stream) base.Handler { + return base.HandlerFunc( + func(_ context.Context, _ base.Request) (base.Response, error) { + return &LokiResponse{ + Status: "success", + Data: LokiData{ + ResultType: loghttp.ResultTypeStream, + Result: []logproto.Stream{ + stream, + }, + }, + Direction: logproto.BACKWARD, + }, nil + }) +} + func TestQuerier_DetectedFields(t *testing.T) { limits := fakeLimits{ maxSeries: math.MaxInt32, @@ -967,50 +1002,18 @@ func TestQuerier_DetectedFields(t *testing.T) { maxQuerierBytesRead: 100, } - limitedHandler := func(stream logproto.Stream) base.Handler { - return base.HandlerFunc( - func(_ context.Context, _ base.Request) (base.Response, error) { - return &LokiResponse{ - Status: "success", - Data: LokiData{ - ResultType: loghttp.ResultTypeStream, - Result: []logproto.Stream{ - stream, - }, - }, - Direction: logproto.BACKWARD, - }, nil - }) - } - - logHandler := func(stream logproto.Stream) base.Handler { - return base.HandlerFunc( - func(_ context.Context, _ base.Request) (base.Response, error) { - return &LokiResponse{ - Status: "success", - Data: LokiData{ - ResultType: loghttp.ResultTypeStream, - Result: []logproto.Stream{ - stream, - }, - }, - Direction: logproto.BACKWARD, - }, nil - }) - } - request := DetectedFieldsRequest{ logproto.DetectedFieldsRequest{ - Start: time.Now().Add(-1 * time.Minute), - End: time.Now(), - Query: `{type="test"} | logfmt | json`, - LineLimit: 1000, - FieldLimit: 1000, + Start: time.Now().Add(-1 * time.Minute), + End: time.Now(), + Query: `{type="test"} | logfmt | json`, + LineLimit: 1000, + Limit: 1000, }, "/loki/api/v1/detected_fields", } - handleRequest := func(handler base.Handler, request DetectedFieldsRequest) []*logproto.DetectedField { + handleRequest := func(handler base.Handler, request DetectedFieldsRequest) *logproto.DetectedFieldsResponse { ctx := context.Background() ctx = user.InjectOrgID(ctx, "test-tenant") @@ -1020,7 +1023,7 @@ func TestQuerier_DetectedFields(t *testing.T) { r, ok := resp.(*DetectedFieldsResponse) require.True(t, ok) - return r.Response.Fields + return r.Response } t.Run("returns detected fields from queried logs", func(t *testing.T) { @@ -1030,7 +1033,7 @@ func TestQuerier_DetectedFields(t *testing.T) { limits, ) - detectedFields := handleRequest(handler, request) + detectedFields := handleRequest(handler, request).Fields // log lines come from querier_mock_test.go // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t assert.Len(t, detectedFields, 8) @@ -1052,12 +1055,16 @@ func TestQuerier_DetectedFields(t *testing.T) { t.Run("returns detected fields with structured metadata from queried logs", func(t *testing.T) { handler := NewDetectedFieldsHandler( - limitedHandler(mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`)), - logHandler(mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`)), + limitedHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`), + ), + logHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`), + ), limits, ) - detectedFields := handleRequest(handler, request) + detectedFields := handleRequest(handler, request).Fields // log lines come from querier_mock_test.go // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t assert.Len(t, detectedFields, 10) @@ -1086,7 +1093,7 @@ func TestQuerier_DetectedFields(t *testing.T) { limits, ) - detectedFields := handleRequest(handler, request) + detectedFields := handleRequest(handler, request).Fields // log lines come from querier_mock_test.go // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t assert.Len(t, detectedFields, 8) @@ -1129,7 +1136,7 @@ func TestQuerier_DetectedFields(t *testing.T) { limits, ) - detectedFields := handleRequest(handler, request) + detectedFields := handleRequest(handler, request).Fields // log lines come from querier_mock_test.go // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t assert.Len(t, detectedFields, 10) @@ -1172,13 +1179,23 @@ func TestQuerier_DetectedFields(t *testing.T) { func(t *testing.T) { handler := NewDetectedFieldsHandler( limitedHandler( - mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 2, `{type="test", name="bob"}`), + mockLogfmtStreamWithLabelsAndStructuredMetadata( + 1, + 2, + `{type="test", name="bob"}`, + ), + ), + logHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata( + 1, + 2, + `{type="test", name="bob"}`, + ), ), - logHandler(mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 2, `{type="test", name="bob"}`)), limits, ) - detectedFields := handleRequest(handler, request) + detectedFields := handleRequest(handler, request).Fields // log lines come from querier_mock_test.go // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t assert.Len(t, detectedFields, 10) @@ -1198,48 +1215,148 @@ func TestQuerier_DetectedFields(t *testing.T) { assert.Equal(t, uint64(1), nameField.Cardinality) }, ) + + t.Run("returns values for a detected fields", func(t *testing.T) { + handler := NewDetectedFieldsHandler( + limitedHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`), + ), + logHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`), + ), + limits, + ) + + request := DetectedFieldsRequest{ + logproto.DetectedFieldsRequest{ + Start: time.Now().Add(-1 * time.Minute), + End: time.Now(), + Query: `{type="test"} | logfmt | json`, + LineLimit: 1000, + Limit: 1000, + Values: true, + Name: "message", + }, + "/loki/api/v1/detected_field/message/values", + } + + detectedFieldValues := handleRequest(handler, request).Values + // log lines come from querier_mock_test.go + // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t + assert.Len(t, detectedFieldValues, 5) + + slices.Sort(detectedFieldValues) + assert.Equal(t, []string{ + "line 1", + "line 2", + "line 3", + "line 4", + "line 5", + }, detectedFieldValues) + }) + + t.Run( + "returns values for a detected fields, enforcing the limit and removing duplicates", + func(t *testing.T) { + handler := NewDetectedFieldsHandler( + limitedHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata( + 1, + 5, + `{type="test"}`, + ), + ), + logHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata( + 1, + 5, + `{type="test"}`, + ), + ), + limits, + ) + + request := DetectedFieldsRequest{ + logproto.DetectedFieldsRequest{ + Start: time.Now().Add(-1 * time.Minute), + End: time.Now(), + Query: `{type="test"} | logfmt | json`, + LineLimit: 1000, + Limit: 3, + Values: true, + Name: "message", + }, + "/loki/api/v1/detected_field/message/values", + } + + detectedFieldValues := handleRequest(handler, request).Values + // log lines come from querier_mock_test.go + // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t + assert.Len(t, detectedFieldValues, 3) + + request = DetectedFieldsRequest{ + logproto.DetectedFieldsRequest{ + Start: time.Now().Add(-1 * time.Minute), + End: time.Now(), + Query: `{type="test"} | logfmt | json`, + LineLimit: 1000, + Limit: 3, + Values: true, + Name: "name", + }, + "/loki/api/v1/detected_field/name/values", + } + + secondValues := handleRequest(handler, request).Values + // log lines come from querier_mock_test.go + // message="line %d" count=%d fake=true bytes=%dMB duration=%dms percent=%f even=%t name=bar + assert.Len(t, secondValues, 1) + + assert.Equal(t, []string{ + "bar", + }, secondValues) + }, + ) } -// func BenchmarkQuerierDetectedFields(b *testing.B) { -// limits, _ := validation.NewOverrides(defaultLimitsTestConfig(), nil) -// ctx := user.InjectOrgID(context.Background(), "test") - -// conf := mockQuerierConfig() -// conf.IngesterQueryStoreMaxLookback = 0 - -// request := logproto.DetectedFieldsRequest{ -// Start: time.Now().Add(-1 * time.Minute), -// End: time.Now(), -// Query: `{type="test"}`, -// LineLimit: 1000, -// FieldLimit: 1000, -// } - -// store := newStoreMock() -// store.On("SelectLogs", mock.Anything, mock.Anything). -// Return(mockLogfmtStreamIterator(1, 2), nil) - -// queryClient := newQueryClientMock() -// queryClient.On("Recv"). -// Return(mockQueryResponse([]logproto.Stream{mockLogfmtStream(1, 2)}), nil) - -// ingesterClient := newQuerierClientMock() -// ingesterClient.On("Query", mock.Anything, mock.Anything, mock.Anything). -// Return(queryClient, nil) - -// querier, _ := newQuerier( -// conf, -// mockIngesterClientConfig(), -// newIngesterClientMockFactory(ingesterClient), -// mockReadRingWithOneActiveIngester(), -// &mockDeleteGettter{}, -// store, limits) - -// b.ReportAllocs() -// b.ResetTimer() - -// for i := 0; i < b.N; i++ { -// _, err := querier.DetectedFields(ctx, &request) -// assert.NoError(b, err) -// } -// } +func BenchmarkQuerierDetectedFields(b *testing.B) { + limits := fakeLimits{ + maxSeries: math.MaxInt32, + maxQueryParallelism: 1, + tsdbMaxQueryParallelism: 1, + maxQueryBytesRead: 1000, + maxQuerierBytesRead: 100, + } + + request := logproto.DetectedFieldsRequest{ + Start: time.Now().Add(-1 * time.Minute), + End: time.Now(), + Query: `{type="test"}`, + LineLimit: 1000, + Limit: 1000, + } + + b.ReportAllocs() + b.ResetTimer() + + handler := NewDetectedFieldsHandler( + limitedHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`), + ), + logHandler( + mockLogfmtStreamWithLabelsAndStructuredMetadata(1, 5, `{type="test", name="bob"}`), + ), + limits, + ) + + for i := 0; i < b.N; i++ { + ctx := context.Background() + ctx = user.InjectOrgID(ctx, "test-tenant") + + resp, err := handler.Do(ctx, &request) + assert.NoError(b, err) + + _, ok := resp.(*DetectedFieldsResponse) + require.True(b, ok) + } +} diff --git a/pkg/querier/queryrange/roundtrip.go b/pkg/querier/queryrange/roundtrip.go index 4b9f3dbca9da8..af0d57f1c0c62 100644 --- a/pkg/querier/queryrange/roundtrip.go +++ b/pkg/querier/queryrange/roundtrip.go @@ -445,7 +445,7 @@ func (r roundTripper) Do(ctx context.Context, req base.Request) (base.Response, "msg", "executing query", "type", "detected_fields", "end", op.End, - "field_limit", op.FieldLimit, + "field_limit", op.Limit, "length", op.End.Sub(op.Start), "line_limit", op.LineLimit, "query", op.Query, @@ -508,7 +508,7 @@ func getOperation(path string) string { return QueryRangeOp case strings.HasSuffix(path, "/series"): return SeriesOp - case strings.HasSuffix(path, "/labels") || strings.HasSuffix(path, "/label") || strings.HasSuffix(path, "/values"): + case strings.HasSuffix(path, "/labels") || strings.HasSuffix(path, "/label"): return LabelNamesOp case strings.HasSuffix(path, "/v1/query"): return InstantQueryOp @@ -521,6 +521,12 @@ func getOperation(path string) string { case path == "/loki/api/v1/index/shards": return IndexShardsOp case path == "/loki/api/v1/detected_fields": + return DetectedFieldsOp + case strings.HasSuffix(path, "/values"): + if strings.HasPrefix(path, "/loki/api/v1/label") || strings.HasPrefix(path, "/prom/label") { + return LabelNamesOp + } + return DetectedFieldsOp case path == "/loki/api/v1/patterns": return PatternsQueryOp diff --git a/pkg/querier/queryrange/roundtrip_test.go b/pkg/querier/queryrange/roundtrip_test.go index 2f3b5fcd92ee3..a899d64c21be0 100644 --- a/pkg/querier/queryrange/roundtrip_test.go +++ b/pkg/querier/queryrange/roundtrip_test.go @@ -360,7 +360,7 @@ func TestInstantQueryTripperwareResultCaching(t *testing.T) { testLocal.CacheResults = false testLocal.CacheIndexStatsResults = false testLocal.CacheInstantMetricResults = false - var l = fakeLimits{ + l := fakeLimits{ maxQueryParallelism: 1, tsdbMaxQueryParallelism: 1, maxQueryBytesRead: 1000, @@ -1043,7 +1043,6 @@ func TestTripperware_EntriesLimit(t *testing.T) { } func TestTripperware_RequiredLabels(t *testing.T) { - const noErr = "" for _, test := range []struct { @@ -1095,7 +1094,6 @@ func TestTripperware_RequiredLabels(t *testing.T) { } func TestTripperware_RequiredNumberLabels(t *testing.T) { - const noErr = "" for _, tc := range []struct { @@ -1263,6 +1261,16 @@ func Test_getOperation(t *testing.T) { path: "/prom/label/__name__/values", expectedOp: LabelNamesOp, }, + { + name: "detected_fields", + path: "/loki/api/v1/detected_fields", + expectedOp: DetectedFieldsOp, + }, + { + name: "detected_fields_values", + path: "/loki/api/v1/detected_field/foo/values", + expectedOp: DetectedFieldsOp, + }, } for _, tc := range cases { @@ -1523,6 +1531,7 @@ func (f fakeLimits) VolumeEnabled(_ string) bool { func (f fakeLimits) TSDBMaxBytesPerShard(_ string) int { return valid.DefaultTSDBMaxBytesPerShard } + func (f fakeLimits) TSDBShardingStrategy(string) string { return logql.PowerOfTwoVersion.String() } diff --git a/pkg/querier/queryrange/splitters.go b/pkg/querier/queryrange/splitters.go index fe3453b2ee717..06f4f903eb6b1 100644 --- a/pkg/querier/queryrange/splitters.go +++ b/pkg/querier/queryrange/splitters.go @@ -103,12 +103,12 @@ func (s *defaultSplitter) split(execTime time.Time, tenantIDs []string, req quer factory = func(start, end time.Time) { reqs = append(reqs, &DetectedFieldsRequest{ DetectedFieldsRequest: logproto.DetectedFieldsRequest{ - Start: start, - End: end, - Query: r.GetQuery(), - LineLimit: r.GetLineLimit(), - FieldLimit: r.GetFieldLimit(), - Step: r.GetStep(), + Start: start, + End: end, + Query: r.GetQuery(), + LineLimit: r.GetLineLimit(), + Limit: r.GetLimit(), + Step: r.GetStep(), }, path: r.path, }) diff --git a/pkg/storage/detected/fields.go b/pkg/storage/detected/fields.go index 8dd9dd1a15126..4d96e2baea3f2 100644 --- a/pkg/storage/detected/fields.go +++ b/pkg/storage/detected/fields.go @@ -53,9 +53,9 @@ func (f *UnmarshaledDetectedField) Merge(df *logproto.DetectedField) error { func MergeFields( fields []*logproto.DetectedField, - fieldLimit uint32, + limit uint32, ) ([]*logproto.DetectedField, error) { - mergedFields := make(map[string]*UnmarshaledDetectedField, fieldLimit) + mergedFields := make(map[string]*UnmarshaledDetectedField, limit) foundFields := uint32(0) for _, field := range fields { @@ -66,7 +66,7 @@ func MergeFields( // TODO(twhitney): this will take the first N up to limit, is there a better // way to rank the fields to make sure we get the most interesting ones? f, ok := mergedFields[field.Label] - if !ok && foundFields < fieldLimit { + if !ok && foundFields < limit { unmarshaledField, err := UnmarshalDetectedField(field) if err != nil { return nil, err @@ -86,7 +86,7 @@ func MergeFields( } } - result := make([]*logproto.DetectedField, 0, fieldLimit) + result := make([]*logproto.DetectedField, 0, limit) for _, field := range mergedFields { detectedField := &logproto.DetectedField{ Label: field.Label, @@ -100,3 +100,29 @@ func MergeFields( return result, nil } + +func MergeValues( + values []string, + limit uint32, +) ([]string, error) { + mergedValues := make(map[string]struct{}, limit) + + for _, value := range values { + if value == "" { + continue + } + + if len(mergedValues) >= int(limit) { + break + } + + mergedValues[value] = struct{}{} + } + + result := make([]string, 0, limit) + for value := range mergedValues { + result = append(result, value) + } + + return result, nil +} diff --git a/pkg/storage/detected/fields_test.go b/pkg/storage/detected/fields_test.go index 2c31fa1d2775e..9d0bff10d4376 100644 --- a/pkg/storage/detected/fields_test.go +++ b/pkg/storage/detected/fields_test.go @@ -113,3 +113,35 @@ func Test_MergeFields(t *testing.T) { require.Error(t, err) }) } + +func Test_MergeValues(t *testing.T) { + t.Run("merges different values", func(t *testing.T) { + values := []string{"foo", "bar", "baz", "qux"} + limit := uint32(50) + + result, err := MergeValues(values, limit) + require.NoError(t, err) + assert.Equal(t, 4, len(result)) + assert.ElementsMatch(t, []string{"foo", "bar", "baz", "qux"}, result) + }) + + t.Run("merges repeating values", func(t *testing.T) { + values := []string{"foo", "bar", "baz", "qux", "foo", "bar", "baz", "qux"} + limit := uint32(50) + + result, err := MergeValues(values, limit) + require.NoError(t, err) + assert.Equal(t, 4, len(result)) + assert.ElementsMatch(t, []string{"foo", "bar", "baz", "qux"}, result) + }) + + t.Run("enforces the limit", func(t *testing.T) { + values := []string{"foo", "bar", "baz", "qux", "foo", "bar", "baz", "qux"} + limit := uint32(2) + + result, err := MergeValues(values, limit) + require.NoError(t, err) + assert.Equal(t, 2, len(result)) + assert.ElementsMatch(t, []string{"foo", "bar"}, result) + }) +} diff --git a/production/helm/loki/values.yaml b/production/helm/loki/values.yaml index ab62323c95f86..9640891480513 100644 --- a/production/helm/loki/values.yaml +++ b/production/helm/loki/values.yaml @@ -1196,6 +1196,7 @@ ingress: - /loki/api/v1/index/volume - /loki/api/v1/index/volume_range - /loki/api/v1/format_query + - /loki/api/v1/detected_field - /loki/api/v1/detected_fields - /loki/api/v1/detected_labels - /loki/api/v1/patterns