Explore the statistics: a query log to drill down by client, name, view and more; storage on the dashboard
jarld keeps every answer for a while (stats.retention.queries, default 7 days) in the statistics database: time, client, view, name, type, result and what blocked it. Every client, name, view, record type, result and blocklist on the Statistics page leads to an explore page for it: answers over time, the top values of every other dimension (each a link that narrows further) and the latest answers. GET /stats/explore. The dashboard shows the size of Jarl's databases (and the query log's rows) and, per disk Jarl uses, how full it is and the databases' share. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fef916b7ab
commit
01ec37b9b2
21 files changed
+922
-27
No files matched your search
@@ -0,0 +1,72 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
|
||||
"jarl/internal/api"
|
||||
"jarl/internal/stats"
|
||||
)
|
||||
|
||||
// storage reports the size of Jarl's databases and how full the disks
|
||||
// Jarl uses are (the dashboard's Storage card).
|
||||
func (s *Server) storage(ctx context.Context) *api.Storage {
|
||||
out := &api.Storage{Databases: []api.Database{}, Disks: []api.Disk{}}
|
||||
dbFile := func(label, path string) *api.Database {
|
||||
if path == "" || path == ":memory:" {
|
||||
return nil
|
||||
}
|
||||
d := api.Database{Label: label, Path: path}
|
||||
for _, suffix := range []string{"", "-wal", "-shm"} {
|
||||
if fi, err := os.Stat(path + suffix); err == nil {
|
||||
d.Bytes += fi.Size()
|
||||
}
|
||||
}
|
||||
out.Databases = append(out.Databases, d)
|
||||
out.Total += d.Bytes
|
||||
return &out.Databases[len(out.Databases)-1]
|
||||
}
|
||||
labels := []string{}
|
||||
dirs := map[string]string{}
|
||||
add := func(label, dir string) {
|
||||
if dir != "" {
|
||||
labels = append(labels, label)
|
||||
dirs[label] = dir
|
||||
}
|
||||
}
|
||||
const jarlDB, statsDB = "Jarl's database", "statistics database"
|
||||
if p := s.store.Path(); p != "" {
|
||||
dbFile(jarlDB, p)
|
||||
add(jarlDB, filepath.Dir(p))
|
||||
}
|
||||
if s.cfg.Stats != nil {
|
||||
if p := s.cfg.Stats.Store().Path(); p != "" && p != ":memory:" {
|
||||
if d := dbFile(statsDB, p); d != nil && s.cfg.Stats.Options().RetainQueries > 0 {
|
||||
if n, err := s.cfg.Stats.Store().QueryLogSize(ctx); err == nil {
|
||||
d.QueryLogRows = &n
|
||||
}
|
||||
}
|
||||
add(statsDB, filepath.Dir(p))
|
||||
}
|
||||
if lf := s.cfg.Stats.Options().LogFile; lf != "" {
|
||||
add("named's logs", filepath.Dir(lf))
|
||||
}
|
||||
}
|
||||
paths := s.eng.Config().Paths
|
||||
add("zone files", paths.ZoneDir)
|
||||
add("named.conf", paths.ConfigDir)
|
||||
add("Jarl's cache and blocklists", paths.CacheDir)
|
||||
add("backups", s.cfg.BackupDir)
|
||||
for _, d := range stats.DiskUsage(labels, dirs) {
|
||||
disk := api.Disk{Path: d.Path, Holds: d.Holds, Total: d.Total, Free: d.Free}
|
||||
for _, db := range out.Databases {
|
||||
if slices.Contains(d.Holds, db.Label) {
|
||||
disk.Databases += db.Bytes
|
||||
}
|
||||
}
|
||||
out.Disks = append(out.Disks, disk)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user