Skip to content

Commit 48d3cd3

Browse files
committed
Implement last filter
1 parent 19f485f commit 48d3cd3

File tree

5 files changed

+120
-115
lines changed

5 files changed

+120
-115
lines changed

cmd/ax/query.go

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"os"
88
"regexp"
9+
"strconv"
910
"strings"
1011
"time"
1112

@@ -19,6 +20,7 @@ import (
1920

2021
func addQueryFlags(cmd *kingpin.CmdClause) *common.QuerySelectors {
2122
flags := &common.QuerySelectors{}
23+
cmd.Flag("last", "Results from last x minutes, hours, days, months, years. If used after and before are ignored").StringVar(&flags.Last)
2224
cmd.Flag("before", "Results from before").StringVar(&flags.Before)
2325
cmd.Flag("after", "Results from after").StringVar(&flags.After)
2426
cmd.Flag("select", "Fields to select").Short('s').HintAction(selectHintAction).StringsVar(&flags.Select)
@@ -149,28 +151,63 @@ func buildExistenceFilters(exists []string, notExists []string) []common.Existen
149151
return filters
150152
}
151153

152-
func querySelectorsToQuery(flags *common.QuerySelectors) common.Query {
153-
var before *time.Time
154-
var after *time.Time
155-
if flags.After != "" {
156-
var err error
157-
afterTime, err := dateparse.ParseLocal(flags.After)
154+
func stringToTime(raw, name string) *time.Time {
155+
if raw != "" {
156+
parsed, err := dateparse.ParseLocal(raw)
158157
if err != nil {
159-
fmt.Println("Could parse after date:", flags.After)
158+
fmt.Printf("Could not parse %s date: %s\n", name, raw)
160159
os.Exit(1)
161160
}
162-
fmt.Fprintf(os.Stderr, "Parsed --after as %s", afterTime.Format(common.TimeFormat))
163-
after = &afterTime
161+
fmt.Printf("Parsed --%s as %s", name, parsed.Format(common.TimeFormat))
162+
return &parsed
164163
}
165-
if flags.Before != "" {
166-
var err error
167-
beforeTime, err := dateparse.ParseLocal(flags.Before)
168-
if err != nil {
169-
fmt.Println("Could parse before date:", flags.Before)
170-
os.Exit(1)
171-
}
172-
fmt.Fprintf(os.Stderr, "Parsed --before as %s", beforeTime.Format(common.TimeFormat))
173-
before = &beforeTime
164+
return nil
165+
}
166+
167+
// Do this in order to mock it in test
168+
var timeNow = time.Now
169+
170+
func lastToTimeInterval(raw string) (*time.Time, *time.Time, error) {
171+
splitted := strings.Split(raw, " ")
172+
if len(splitted) != 2 {
173+
return nil, nil, fmt.Errorf("last filter should contain amount and unit")
174+
}
175+
176+
amountStr, unit := splitted[0], splitted[1]
177+
amount, err := strconv.Atoi(amountStr)
178+
if err != nil {
179+
return nil, nil, fmt.Errorf("amount has to be numeric")
180+
}
181+
before := timeNow()
182+
183+
var after time.Time
184+
switch unit {
185+
case "minutes", "minute", "min":
186+
after = before.Add(-time.Minute * time.Duration(amount))
187+
case "hours", "hour", "h":
188+
after = before.Add(-time.Hour * time.Duration(amount))
189+
case "days", "day", "d":
190+
after = before.AddDate(0, 0, -amount)
191+
case "months", "month", "m":
192+
after = before.AddDate(0, -amount, 0)
193+
case "years", "year", "y":
194+
after = before.AddDate(-amount, 0, 0)
195+
default:
196+
return nil, nil, fmt.Errorf("unknown unit")
197+
}
198+
199+
return &before, &after, nil
200+
}
201+
202+
func querySelectorsToQuery(flags *common.QuerySelectors) common.Query {
203+
var before, after *time.Time
204+
last := flags.Last
205+
206+
if last != "" {
207+
before, after, _ = lastToTimeInterval(last)
208+
} else {
209+
before = stringToTime(flags.Before, "before")
210+
after = stringToTime(flags.After, "after")
174211
}
175212

176213
return common.Query{

cmd/ax/query_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"reflect"
55
"sort"
66
"testing"
7+
"time"
78

89
"github.com/egnyte/ax/pkg/backend/common"
910
)
@@ -126,3 +127,54 @@ func Test_buildExistenceFilters(t *testing.T) {
126127
})
127128
}
128129
}
130+
131+
func setupTestTime(testTime time.Time) func() {
132+
timeNow = func() time.Time {
133+
return testTime
134+
}
135+
return func() { timeNow = time.Now }
136+
}
137+
138+
func Test_humanToTimeInterval(t *testing.T) {
139+
testTime := time.Date(2018, 10, 1, 12, 15, 30, 0, time.UTC)
140+
141+
testCases := []struct {
142+
input string
143+
wantAfter time.Time
144+
}{
145+
{"1 day", time.Date(2018, 9, 30, 12, 15, 30, 0, time.UTC)},
146+
{"0 days", time.Date(2018, 10, 1, 12, 15, 30, 0, time.UTC)},
147+
{"31 days", time.Date(2018, 8, 31, 12, 15, 30, 0, time.UTC)},
148+
{"31 d", time.Date(2018, 8, 31, 12, 15, 30, 0, time.UTC)},
149+
{"1 hour", time.Date(2018, 10, 1, 11, 15, 30, 0, time.UTC)},
150+
{"1 h", time.Date(2018, 10, 1, 11, 15, 30, 0, time.UTC)},
151+
{"4 hours", time.Date(2018, 10, 1, 8, 15, 30, 0, time.UTC)},
152+
{"2 years", time.Date(2016, 10, 1, 12, 15, 30, 0, time.UTC)},
153+
{"1 year", time.Date(2017, 10, 1, 12, 15, 30, 0, time.UTC)},
154+
{"1 y", time.Date(2017, 10, 1, 12, 15, 30, 0, time.UTC)},
155+
{"1 months", time.Date(2018, 9, 1, 12, 15, 30, 0, time.UTC)},
156+
{"1 month", time.Date(2018, 9, 1, 12, 15, 30, 0, time.UTC)},
157+
{"1 m", time.Date(2018, 9, 1, 12, 15, 30, 0, time.UTC)},
158+
{"30 minutes", time.Date(2018, 10, 1, 11, 45, 30, 0, time.UTC)},
159+
{"1 minutes", time.Date(2018, 10, 1, 12, 14, 30, 0, time.UTC)},
160+
{"1 minute", time.Date(2018, 10, 1, 12, 14, 30, 0, time.UTC)},
161+
{"1 min", time.Date(2018, 10, 1, 12, 14, 30, 0, time.UTC)},
162+
}
163+
164+
for _, tc := range testCases {
165+
t.Run(tc.input, func(*testing.T) {
166+
teardown := setupTestTime(testTime)
167+
defer teardown()
168+
before, after, _ := lastToTimeInterval(tc.input)
169+
170+
if *before != testTime {
171+
// it should stay constant through all cases
172+
t.Fatalf("exepcted before to equal now, got %v", *before)
173+
}
174+
175+
if *after != tc.wantAfter {
176+
t.Fatalf("exepcted after to be %v, got %v", tc.wantAfter, *after)
177+
}
178+
})
179+
}
180+
}

pkg/backend/common/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type Query struct {
5353
}
5454

5555
type QuerySelectors struct {
56+
Last string `yaml:"last,omitempty"`
5657
Before string `yaml:"before,omitempty"`
5758
After string `yaml:"after,omitempty"`
5859
Select []string `yaml:"select,omitempty"`

pkg/heuristic/timestamp.go

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ var formatsToTryRx []*regexp.Regexp = []*regexp.Regexp{
5757
regexp.MustCompile(`\d+-\d+-\d+ \d+:\d+:\d+(,\d+)?`),
5858
}
5959

60+
func FindTimestampFunc(exampleMessage common.LogMessage) LogTimestampParser {
61+
for k, v := range exampleMessage.Attributes {
62+
if fn := guessTimestampParseFunc(v); fn != nil {
63+
return func(lm common.LogMessage) *time.Time {
64+
return fn(lm.Attributes[k])
65+
}
66+
}
67+
}
68+
return findTimestampInMessage(exampleMessage)
69+
}
70+
6071
func epochMsToTime(i int64) *time.Time {
6172
now := time.Now()
6273
ts := time.Unix(i/1000, 0)
@@ -75,7 +86,7 @@ func epochToTime(i int64) *time.Time {
7586
return &ts
7687
}
7788

78-
func GuessTimestampParseFunc(exampleV interface{}) TimestampParser {
89+
func guessTimestampParseFunc(exampleV interface{}) TimestampParser {
7990
switch exampleVal := exampleV.(type) {
8091
case float64:
8192
t := epochToTime(int64(exampleVal))
@@ -126,14 +137,6 @@ func GuessTimestampParseFunc(exampleV interface{}) TimestampParser {
126137
return nil
127138
}
128139

129-
func ParseTimestamp(v interface{}) (*time.Time, error) {
130-
fn := GuessTimestampParseFunc(v)
131-
if fn == nil {
132-
return nil, ErrorCouldNotParse
133-
}
134-
return fn(v), nil
135-
}
136-
137140
// Currently unused
138141
func formatToRegex(format string) *regexp.Regexp {
139142
rformat := regexp.QuoteMeta(format)
@@ -185,24 +188,3 @@ func findTimestampInMessage(exampleMessage common.LogMessage) LogTimestampParser
185188
}
186189
return nil
187190
}
188-
189-
func FindTimestampFunc(exampleMessage common.LogMessage) LogTimestampParser {
190-
for k, v := range exampleMessage.Attributes {
191-
if fn := GuessTimestampParseFunc(v); fn != nil {
192-
return func(lm common.LogMessage) *time.Time {
193-
return fn(lm.Attributes[k])
194-
}
195-
}
196-
}
197-
return findTimestampInMessage(exampleMessage)
198-
}
199-
200-
//func GetTimestamp(lm common.LogMessage) (*time.Time, error) {
201-
// if fn := FindTimestampFunc(lm); fn != nil {
202-
// return fn(lm), nil
203-
// }
204-
// if fn := findTimestampInMessage(lm); fn != nil {
205-
// return fn(lm), nil
206-
// }
207-
// return nil, errors.New("Not found")
208-
//}

pkg/heuristic/timestamp_test.go

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)