Skip to content

Commit

Permalink
stats: add basic cache statistic subcommand
Browse files Browse the repository at this point in the history
dmgk committed Jul 17, 2022
1 parent 38e1426 commit 60edef8
Showing 3 changed files with 164 additions and 1 deletion.
2 changes: 1 addition & 1 deletion fetch.go
Original file line number Diff line number Diff line change
@@ -157,7 +157,7 @@ func runFetch(args []string) int {
return err
}

fmt.Fprintf(os.Stdout, "%s : %d bytes\n", res, len(res.Content))
fmt.Fprintf(os.Stdout, "%s : %s\n", res, formatSize(int64(len(res.Content))))
e, err := c.Entry(res.Builder, res.Origin, res.Timestamp)
if err != nil {
return err
17 changes: 17 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -81,6 +81,7 @@ var cmds = []*command{
&fetchCmd,
&grepCmd,
&cleanCmd,
&statsCmd,
}

func main() {
@@ -155,3 +156,19 @@ func splitOptions(s string) []string {
return unicode.IsSpace(r) || r == ','
})
}

func formatSize(size int64) string {
const suffixes = "KMG"
if size < 1000 {
return fmt.Sprintf("%dB", size)
}
fsize := float64(size)
var s rune
for _, s = range suffixes {
if fsize < 999999.0 {
break
}
fsize /= 1000.0
}
return fmt.Sprintf("%.1f %cB", fsize/1000.0, s)
}
146 changes: 146 additions & 0 deletions stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package main

import (
"fmt"
"html/template"
"os"
"time"

"github.com/dmgk/fallout/cache"
"github.com/dmgk/getopt"
)

var statsUsageTmpl = template.Must(template.New("usage-stats").Parse(`
usage: {{.progname}} stats [-h]
Show cached logs statistics.
Options:
-h show help and exit
`[1:]))

var statsCmd = command{
Name: "stats",
Summary: "show cache statistics",
run: runStats,
}

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

var statsTmpl = template.Must(template.New("stats-output").Parse(`
Cache size: {{.logsSize}}
Latest log: {{.latestTimestamp}}
Earliest log: {{.earliestTimestamp}}
Builders: {{.buildersCount}}
Failing ports: {{.originsCount}}
Logs: {{.logsCount}}
Most failures: {{.topBuilderName}} ({{.topBuilderCount}} ports)
`[1:]))

func runStats(args []string) int {
opts, err := getopt.NewArgv("h", args)
if err != nil {
panic(fmt.Sprintf("error creating options parser: %s", err))
}

for opts.Scan() {
opt, err := opts.Option()
if err != nil {
errExit(err.Error())
}

switch opt.Opt {
case 'h':
showStatsUsage()
os.Exit(0)
}
}

const tsFormat = "2006-01-02 15:04:05 UTC"
var (
latestTs time.Time
earliestTs time.Time
buildersMap = map[string]map[string]struct{}{}
originsSet = map[string]struct{}{}
logTotalCount int
logTotalSize int64
topBuilderName string
topBuilderCount int
)

c, err := cache.NewDefaultDirectory(progname)
if err != nil {
errExit("error initializing cache: %s", err)
}

w := c.Walker(nil)
err = w.Walk(func(entry cache.Entry, err error) error {
if err != nil {
return err
}

inf := entry.Info()

if inf.Timestamp.Before(earliestTs) || earliestTs.IsZero() {
earliestTs = inf.Timestamp
}
if inf.Timestamp.After(latestTs) {
latestTs = inf.Timestamp
}
if _, ok := buildersMap[inf.Builder]; !ok {
buildersMap[inf.Builder] = map[string]struct{}{}
}
buildersMap[inf.Builder][inf.Origin] = struct{}{}
originsSet[inf.Origin] = struct{}{}
logTotalCount += 1

f, err := os.Open(entry.Path())
if err != nil {
return err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return err
}
logTotalSize += fi.Size()

return nil
})
if err != nil {
errExit("error: %s", err)
}

if logTotalCount == 0 {
fmt.Println("No logs in cache.")
return 0
}

for n, bo := range buildersMap {
if len(bo) > topBuilderCount {
topBuilderName = n
topBuilderCount = len(bo)
}
}

err = statsTmpl.Execute(os.Stdout, map[string]any{
"latestTimestamp": latestTs.Format(tsFormat),
"earliestTimestamp": earliestTs.Format(tsFormat),
"buildersCount": len(buildersMap),
"topBuilderName": topBuilderName,
"topBuilderCount": topBuilderCount,
"originsCount": len(originsSet),
"logsCount": logTotalCount,
"logsSize": formatSize(logTotalSize),
})
if err != nil {
errExit("error: %s", err)
}

return 0
}

0 comments on commit 60edef8

Please sign in to comment.