Skip to content

Commit

Permalink
feat: detected field values (#14350)
Browse files Browse the repository at this point in the history
  • Loading branch information
trevorwhitney authored Oct 3, 2024
1 parent e9efcb5 commit 7983f94
Show file tree
Hide file tree
Showing 31 changed files with 909 additions and 415 deletions.
10 changes: 7 additions & 3 deletions cmd/logcli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/sources/setup/install/helm/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion pkg/ingester-rf1/ingester.go
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,6 @@ func (i *Ingester) GetDetectedFields(_ context.Context, r *logproto.DetectedFiel
Cardinality: 1,
},
},
FieldLimit: r.GetFieldLimit(),
Limit: r.GetLimit(),
}, nil
}
2 changes: 1 addition & 1 deletion pkg/ingester/ingester.go
Original file line number Diff line number Diff line change
Expand Up @@ -1595,7 +1595,7 @@ func (i *Ingester) GetDetectedFields(_ context.Context, r *logproto.DetectedFiel
Cardinality: 1,
},
},
FieldLimit: r.GetFieldLimit(),
Limit: r.GetLimit(),
}, nil
}

Expand Down
39 changes: 23 additions & 16 deletions pkg/logcli/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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())
Expand All @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/logcli/client/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (f *FileClient) GetVolumeRange(_ *volume.Query) (*loghttp.QueryResponse, er
}

func (f *FileClient) GetDetectedFields(
_ string,
_, _ string,
_, _ int,
_, _ time.Time,
_ time.Duration,
Expand Down
31 changes: 23 additions & 8 deletions pkg/logcli/detected/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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)
}
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/logcli/query/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ func (t *testQueryClient) GetVolumeRange(_ *volume.Query) (*loghttp.QueryRespons
}

func (t *testQueryClient) GetDetectedFields(
_ string,
_, _ string,
_, _ int,
_, _ time.Time,
_ time.Duration,
Expand Down
1 change: 1 addition & 0 deletions pkg/loghttp/detected.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 10 additions & 3 deletions pkg/loghttp/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (

const (
defaultQueryLimit = 100
defaultFieldLimit = 1000
defaultLimit = 1000
defaultSince = 1 * time.Hour
defaultDirection = logproto.BACKWARD
)
Expand All @@ -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")
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/loghttp/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/logproto/compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down
Loading

0 comments on commit 7983f94

Please sign in to comment.