78 lines
1.7 KiB
Go
78 lines
1.7 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"net/url"
|
||
|
|
"sort"
|
||
|
|
"strconv"
|
||
|
|
)
|
||
|
|
|
||
|
|
func usageCommands() []Command {
|
||
|
|
return []Command{
|
||
|
|
{Path: []string{"usage", "top"}, Tier: TierRead,
|
||
|
|
Summary: "rank homelab verb usage across users (from Loki): usage top [--since 30d] [--user U] [--json]", Run: usageTop},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// usageQuery builds the LogQL metric query that counts invocations per verb.
|
||
|
|
func usageQuery(since, user string) string {
|
||
|
|
sel := `job="` + usageJob + `"`
|
||
|
|
if user != "" {
|
||
|
|
sel += `, user="` + user + `"`
|
||
|
|
}
|
||
|
|
return fmt.Sprintf(`sum by (verb) (count_over_time({%s}[%s]))`, sel, since)
|
||
|
|
}
|
||
|
|
|
||
|
|
func usageTop(args []string) error {
|
||
|
|
since := flagValue(args, "--since")
|
||
|
|
if since == "" {
|
||
|
|
since = "30d"
|
||
|
|
}
|
||
|
|
v := url.Values{}
|
||
|
|
v.Set("query", usageQuery(since, flagValue(args, "--user")))
|
||
|
|
body, err := lbGetBody(lokiHost, "/loki/api/v1/query", v)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if containsArg(args, "--json") {
|
||
|
|
fmt.Println(string(body))
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
var r struct {
|
||
|
|
Data struct {
|
||
|
|
Result []struct {
|
||
|
|
Metric map[string]string `json:"metric"`
|
||
|
|
Value []interface{} `json:"value"`
|
||
|
|
} `json:"result"`
|
||
|
|
} `json:"data"`
|
||
|
|
}
|
||
|
|
if err := json.Unmarshal(body, &r); err != nil {
|
||
|
|
fmt.Println(string(body))
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
type row struct {
|
||
|
|
verb string
|
||
|
|
n int
|
||
|
|
}
|
||
|
|
var rows []row
|
||
|
|
for _, s := range r.Data.Result {
|
||
|
|
n := 0
|
||
|
|
if len(s.Value) == 2 {
|
||
|
|
if f, e := strconv.ParseFloat(fmt.Sprint(s.Value[1]), 64); e == nil {
|
||
|
|
n = int(f)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
rows = append(rows, row{s.Metric["verb"], n})
|
||
|
|
}
|
||
|
|
if len(rows) == 0 {
|
||
|
|
fmt.Println("(no usage recorded yet)")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
sort.Slice(rows, func(i, j int) bool { return rows[i].n > rows[j].n })
|
||
|
|
for _, r := range rows {
|
||
|
|
fmt.Printf("%6d %s\n", r.n, r.verb)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|