mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* tempo-cli: support relative time strings (now, now-1h) for start/end args Add support for relative time strings across all CLI commands that accept start/end arguments, matching Grafana's time string format. example: now-1h, now, now-30m, etc. Affected commands: - query api search - query api search-tags - query api search-tag-values - query api metrics query-range - query search Help text previously said "ISO8601 format" or showed a ISO 8601 format but the code was parsing with `time.RFC3339` (2006-01-02T15:04:05Z07:00). ISO 8601 is a broad standard that allows many representations including timestamps without timezone offsets. RFC 3339 is a strict profile of ISO 8601 that always requires a timezone offset (Z or +HH:MM). Help text is update to say RFC3339 with an example. We now accept a uniform time format across all commands: either RFC3339 absolute timestamps or relative time strings. query search: previously accepted timestamps without timezone (2024-03-15T10:30:00) using the layout "2006-01-02T15:04:05". This is valid ISO 8601 but not valid RFC 3339. These timestamps are now rejected. Users must include a timezone offset (e.g. 2024-03-15T10:30:00Z) or use relative time (e.g. now-1h). * update docs * Add CHANGELOG.md * trim spaces before parsing and add tests
129 lines
2.7 KiB
Go
129 lines
2.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/grafana/tempo/pkg/boundedwaitgroup"
|
|
"github.com/grafana/tempo/pkg/tempopb"
|
|
"github.com/grafana/tempo/tempodb"
|
|
"github.com/grafana/tempo/tempodb/backend"
|
|
"github.com/grafana/tempo/tempodb/encoding"
|
|
"github.com/grafana/tempo/tempodb/encoding/common"
|
|
)
|
|
|
|
const (
|
|
limit = 20
|
|
)
|
|
|
|
type searchBlocksCmd struct {
|
|
backendOptions
|
|
|
|
Name string `arg:"" help:"attribute name to search for"`
|
|
Value string `arg:"" help:"attribute value to search for"`
|
|
Start string `arg:"" help:"start time in RFC3339 (e.g. 2006-01-02T15:04:05Z07:00) or relative (e.g. now-1h) format"`
|
|
End string `arg:"" help:"end time in RFC3339 (e.g. 2006-01-02T15:04:05Z07:00) or relative (e.g. now) format"`
|
|
TenantID string `arg:"" help:"tenant ID to search"`
|
|
}
|
|
|
|
func (cmd *searchBlocksCmd) Run(opts *globalOptions) error {
|
|
r, _, _, err := loadBackend(&cmd.backendOptions, opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
startTime, err := parseTime(cmd.Start)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
endTime, err := parseTime(cmd.End)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
searchReq := &tempopb.SearchRequest{
|
|
Tags: map[string]string{cmd.Name: cmd.Value},
|
|
Limit: limit,
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
blockIDs, _, err := r.Blocks(ctx, cmd.TenantID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println("Total Blocks:", len(blockIDs))
|
|
|
|
// Load in parallel
|
|
wg := boundedwaitgroup.New(20)
|
|
resultsCh := make(chan *backend.BlockMeta, len(blockIDs))
|
|
for _, id := range blockIDs {
|
|
wg.Add(1)
|
|
|
|
go func(id2 uuid.UUID) {
|
|
defer wg.Done()
|
|
|
|
// search here
|
|
meta, err := r.BlockMeta(ctx, id2, cmd.TenantID)
|
|
if errors.Is(err, backend.ErrDoesNotExist) {
|
|
return
|
|
}
|
|
if err != nil {
|
|
fmt.Println("Error reading block meta:", err)
|
|
return
|
|
}
|
|
if meta.StartTime.Unix() <= endTime.Unix() &&
|
|
meta.EndTime.Unix() >= startTime.Unix() {
|
|
resultsCh <- meta
|
|
}
|
|
}(id)
|
|
}
|
|
|
|
wg.Wait()
|
|
close(resultsCh)
|
|
|
|
blockmetas := []*backend.BlockMeta{}
|
|
for q := range resultsCh {
|
|
blockmetas = append(blockmetas, q)
|
|
}
|
|
|
|
searchOpts := common.SearchOptions{}
|
|
tempodb.SearchConfig{}.ApplyToOptions(&searchOpts)
|
|
|
|
fmt.Println("Blocks In Range:", len(blockmetas))
|
|
foundids := []string{}
|
|
for _, meta := range blockmetas {
|
|
block, err := encoding.OpenBlock(meta, r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
resp, err := block.Search(ctx, searchReq, searchOpts)
|
|
if err != nil {
|
|
fmt.Println("Error searching block:", err)
|
|
return nil
|
|
}
|
|
|
|
if resp != nil {
|
|
for _, r := range resp.Traces {
|
|
foundids = append(foundids, r.TraceID)
|
|
}
|
|
}
|
|
|
|
if len(foundids) >= limit {
|
|
break
|
|
}
|
|
}
|
|
|
|
fmt.Println("Matching Traces:", len(foundids))
|
|
for _, id := range foundids {
|
|
fmt.Println(" ", id)
|
|
}
|
|
|
|
return nil
|
|
}
|