Skip to content

Commit

Permalink
grep: add -s/-e options to limit searched logs date range
Browse files Browse the repository at this point in the history
  • Loading branch information
dmgk committed Jul 18, 2022
1 parent b03c6de commit bcefc0d
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 61 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ Commands (pass -h for command help):
fetch download fallout logs
grep search fallout logs
clean clean log cache
stats show cache statistics
```

##### Fetching failure logs:

```
usage: fallout fetch [-h] [-D days] [-A date] [-N count] [-b builder[,builder]] [-c category[,category]] [-o origin[,origin]] [-n name[,name]]
Expand All @@ -42,8 +45,10 @@ Options:
-n name,... download only logs for these port names
```

###### Searching:

```
usage: fallout grep [-hFOl] [-A count] [-B count] [-C count] [-b builder[,builder]] [-c category[,category]] [-o origin[,origin]] [-n name[,name]] query [query ...]
usage: fallout grep [-hFOl] [-A count] [-B count] [-C count] [-b builder[,builder]] [-c category[,category]] [-o origin[,origin]] [-n name[,name]] [-s since] [-e before] [-j jobs] query [query ...]
Search cached fallout logs.
Expand All @@ -59,9 +64,13 @@ Options:
-c category,... limit search only to these categories
-o origin,... limit search only to these origins
-n name,... limit search only to these port names
-s since list only failures since this date or date-time, in RFC-3339 format
-e before list only failures before this date or date-time, in RFC-3339 format
-j jobs number of parallel jobs, -j1 outputs sorted results (default: 8)
```

##### Cleaning the cache:

```
usage: fallout clean [-hx] [-D days] [-A date]
Expand Down
4 changes: 4 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ type Filter struct {
Origins []string
// Allowed port names, partial names are ok.
Names []string
// Allow logs only since this timestamp.
Since time.Time
// Allow logs only before this timestamp.
Before time.Time
}

// Walker is the cache walker interface.
Expand Down
9 changes: 6 additions & 3 deletions cache/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ func (w *DirectoryWalker) walkOrigin(builder, origin string, rch chan Entry, ech
ech <- err
continue
}
if ts.Before(w.filter.Since) || !w.filter.Before.IsZero() && ts.After(w.filter.Before) {
continue
}
e, err := newEntry(w.cache, builder, origin, ts)
if err != nil {
ech <- err
Expand All @@ -311,15 +314,15 @@ func loadTimestamp(path string) time.Time {
var zero time.Time
if buf, err := os.ReadFile(filepath.Join(path, cacheTimestampName)); err == nil {
if ts, err := time.Parse(cacheTimestampFormat, string(buf)); err == nil {
return ts.UTC()
return ts
}
}
return zero
}

func (c *Directory) updateTimestamp(ts time.Time) {
if ts.UTC().After(c.timestamp) {
c.timestamp = ts.UTC()
if ts.After(c.timestamp) {
c.timestamp = ts
_ = os.WriteFile(filepath.Join(c.path, cacheTimestampName), []byte(ts.Format(cacheTimestampFormat)), 0664)
}
}
Expand Down
17 changes: 8 additions & 9 deletions clean.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const defaultCleanDaysLimit = 30

var (
cleanDateLimit = time.Now().UTC().AddDate(0, 0, -defaultCleanDaysLimit)
allClean bool
cleanAll bool
)

func showCleanUsage() {
Expand Down Expand Up @@ -64,22 +64,21 @@ func runClean(args []string) int {
showCleanUsage()
os.Exit(0)
case 'x':
allClean = true
cleanAll = true
case 'D':
v, err := opt.Int()
if err != nil {
errExit(err.Error())
errExit("-D: %s", err)
}
cleanDateLimit = time.Now().UTC().AddDate(0, 0, -v)
case 'A':
t, err := time.Parse(dateFormat, opt.String())
t, err := parseDateTime(opt.String())
if err != nil {
errExit(err.Error())
}
if t.After(time.Now().UTC()) {
errExit("date in the future: %s", t.Format(dateFormat))
errExit("-A: %s", err)
}
cleanDateLimit = t
default:
panic("unhandled option: -" + string(opt.Opt))
}
}

Expand All @@ -88,7 +87,7 @@ func runClean(args []string) int {
errExit("error initializing cache: %s", err)
}

if allClean {
if cleanAll {
fmt.Printf("Removing %s\n", c.Path())
if err := c.Remove(); err != nil {
errExit("error removing cache: %s", err)
Expand Down
19 changes: 8 additions & 11 deletions fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const defaultFetchDaysLimit = 7
var (
fetchCountLimit int
fetchDateLimit = time.Now().UTC().AddDate(0, 0, -defaultFetchDaysLimit)
onlyNew = true
fetchOnlyNew = true
)

func showFetchUsage() {
Expand Down Expand Up @@ -75,20 +75,17 @@ func runFetch(args []string) int {
case 'D':
v, err := opt.Int()
if err != nil {
errExit(err.Error())
errExit("-D: %s", err)
}
fetchDateLimit = time.Now().UTC().AddDate(0, 0, -v)
onlyNew = false
fetchOnlyNew = false
case 'A':
t, err := time.Parse(dateFormat, opt.String())
t, err := parseDateTime(opt.String())
if err != nil {
errExit(err.Error())
}
if t.After(time.Now().UTC()) {
errExit("date in the future: %s", t.Format(dateFormat))
errExit("-A: %s", err)
}
fetchDateLimit = t
onlyNew = false
fetchOnlyNew = false
case 'N':
v, err := opt.Int()
if err != nil {
Expand Down Expand Up @@ -133,7 +130,7 @@ func runFetch(args []string) int {
Origins: origins,
Names: names,
}
if onlyNew && !c.Timestamp().IsZero() {
if fetchOnlyNew && !c.Timestamp().IsZero() {
fflt.After = c.Timestamp()
}

Expand All @@ -143,7 +140,7 @@ func runFetch(args []string) int {
return false, err
}
if e.Exists() {
if !onlyNew {
if !fetchOnlyNew {
fmt.Fprintf(os.Stdout, "%s (cached)\n", res)
}
return true, nil
Expand Down
8 changes: 4 additions & 4 deletions fetch/maillist.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ func (f *Maillist) fetchMaillist(ctx context.Context, qfn QueryFunc, rch chan *R
ech <- err
return
}
mi := ts.UTC().Year()*100 + int(ts.UTC().Month())
ma := f.filter.After.UTC().Year()*100 + int(f.filter.After.UTC().Month())
mi := ts.Year()*100 + int(ts.Month())
ma := f.filter.After.Year()*100 + int(f.filter.After.Month())
if mi < ma {
cancel() // link is to the month before "After", stop
return
Expand Down Expand Up @@ -197,7 +197,7 @@ func (f *Maillist) fetchMaillist(ctx context.Context, qfn QueryFunc, rch chan *R
ech <- err
return
}
if ts.UTC().Before(f.filter.After.UTC()) {
if ts.Before(f.filter.After) {
return // timestamp is before "After", skip
}

Expand All @@ -213,7 +213,7 @@ func (f *Maillist) fetchMaillist(ctx context.Context, qfn QueryFunc, rch chan *R
resMap[url] = &Result{
Builder: builder,
Origin: origin,
Timestamp: ts.UTC(),
Timestamp: ts,
URL: url,
}
}
Expand Down
71 changes: 45 additions & 26 deletions grep.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"os"
"runtime"
"time"

"github.com/dmgk/fallout/cache"
"github.com/dmgk/fallout/format"
Expand All @@ -16,7 +17,7 @@ import (
)

var grepUsageTmpl = template.Must(template.New("usage-grep").Parse(`
usage: {{.progname}} grep [-hFOl] [-A count] [-B count] [-C count] [-b builder[,builder]] [-c category[,category]] [-o origin[,origin]] [-n name[,name]] query [query ...]
usage: {{.progname}} grep [-hFOl] [-A count] [-B count] [-C count] [-b builder[,builder]] [-c category[,category]] [-o origin[,origin]] [-n name[,name]] [-s since] [-e before] [-j jobs] query [query ...]
Search cached fallout logs.
Expand All @@ -32,6 +33,8 @@ Options:
-c category,... limit search only to these categories
-o origin,... limit search only to these origins
-n name,... limit search only to these port names
-s since list only failures since this date or date-time, in RFC-3339 format
-e before list only failures before this date or date-time, in RFC-3339 format
-j jobs number of parallel jobs, -j1 outputs sorted results (default: {{.maxJobs}})
`[1:]))

Expand All @@ -42,26 +45,28 @@ var grepCmd = command{
}

var (
queryIsRegexp = true
ored bool
filenamesOnly bool
contextAfter int
contextBefore int
maxJobs = runtime.NumCPU()
grepQueryIsRegexp = true
grepOr bool
grepFilenamesOnly bool
grepContextAfter int
grepContextBefore int
grepSince time.Time
grepBefore time.Time
grepMaxJobs = runtime.NumCPU()
)

func showGrepUsage() {
err := grepUsageTmpl.Execute(os.Stdout, map[string]any{
"progname": progname,
"maxJobs": maxJobs,
"maxJobs": grepMaxJobs,
})
if err != nil {
panic(fmt.Sprintf("error executing template %s: %v", grepUsageTmpl.Name(), err))
}
}

func runGrep(args []string) int {
opts, err := getopt.NewArgv("hFOlA:B:C:b:c:o:n:j:", argsWithDefaults(args, "FALLOUT_GREP_OPTS"))
opts, err := getopt.NewArgv("hFOlA:B:C:b:c:o:n:s:e:j:", argsWithDefaults(args, "FALLOUT_GREP_OPTS"))
if err != nil {
panic(fmt.Sprintf("error creating options parser: %s", err))
}
Expand All @@ -77,30 +82,30 @@ func runGrep(args []string) int {
showGrepUsage()
os.Exit(0)
case 'F':
queryIsRegexp = false
grepQueryIsRegexp = false
case 'O':
ored = true
grepOr = true
case 'l':
filenamesOnly = true
grepFilenamesOnly = true
case 'A':
v, err := opt.Int()
if err != nil {
errExit(err.Error())
errExit("-A: %s", err)
}
contextAfter = v
grepContextAfter = v
case 'B':
v, err := opt.Int()
if err != nil {
errExit(err.Error())
errExit("-B: %s", err)
}
contextBefore = v
grepContextBefore = v
case 'C':
v, err := opt.Int()
if err != nil {
errExit(err.Error())
errExit("-C: %s", err)
}
contextBefore = v
contextAfter = v
grepContextBefore = v
grepContextAfter = v
case 'b':
builders = splitOptions(opt.String())
case 'c':
Expand All @@ -109,6 +114,18 @@ func runGrep(args []string) int {
origins = splitOptions(opt.String())
case 'n':
names = splitOptions(opt.String())
case 's':
t, err := parseDateTime(opt.String())
if err != nil {
errExit("-s: %s", err)
}
grepSince = t
case 'e':
t, err := parseDateTime(opt.String())
if err != nil {
errExit("-e: %s", err)
}
grepBefore = t
case 'j':
v, err := opt.Int()
if err != nil {
Expand All @@ -117,7 +134,7 @@ func runGrep(args []string) int {
if v <= 0 {
v = 1
}
maxJobs = v
grepMaxJobs = v
default:
panic("unhandled option: -" + string(opt.Opt))
}
Expand All @@ -143,12 +160,14 @@ func runGrep(args []string) int {
Categories: categories,
Origins: origins,
Names: names,
Since: grepSince,
Before: grepBefore,
}
w := c.Walker(cflt)

// list only log filenames if no queries were provided
if len(opts.Args()) == 0 {
filenamesOnly = true
grepFilenamesOnly = true

// no need to actually grep if no queries were provided and only filenames were requested
// simple cache walk is enough and also will output results ordered by builder/origin/timestamp
Expand All @@ -170,9 +189,9 @@ func runGrep(args []string) int {
fm := initFormatter()

gopt := &grep.Options{
ContextAfter: contextAfter,
ContextBefore: contextBefore,
QueryIsRegexp: queryIsRegexp,
ContextAfter: grepContextAfter,
ContextBefore: grepContextBefore,
QueryIsRegexp: grepQueryIsRegexp,
}
gfn := func(entry cache.Entry, res []*grep.Match, err error) error {
if err != nil {
Expand All @@ -181,7 +200,7 @@ func runGrep(args []string) int {
return fm.Format(entry, res)
}

if err := g.Grep(gopt, opts.Args(), gfn, maxJobs); err != nil {
if err := g.Grep(gopt, opts.Args(), gfn, grepMaxJobs); err != nil {
errExit("grep error: %s", err)
return 1
}
Expand All @@ -200,7 +219,7 @@ func initFormatter() format.Formatter {
format.SetColors(colors)
}
}
if filenamesOnly {
if grepFilenamesOnly {
flags |= format.FfilenamesOnly
}

Expand Down
Loading

0 comments on commit bcefc0d

Please sign in to comment.