Statistics per view: toggle views on the Statistics page
Answers, result codes, record types, zones, blocking, rate limiting and the top lists are also counted per view, from named's response log. The Statistics page has a toggle per view with All and None, remembered per browser; jarl stats --view and ?views= on the API. named's own counters stay for the whole server, and the page says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ead749dd3d
commit
12820b17bb
13 files changed
+526
-61
No files matched your search
@@ -0,0 +1,103 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Figures for chosen views: from the response log, per view, and added
|
||||
// up for several.
|
||||
func TestViewFilter(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
loc := time.FixedZone("test", 0)
|
||||
dir := t.TempDir()
|
||||
logFile := filepath.Join(dir, "responses.log")
|
||||
store, err := OpenStore(filepath.Join(dir, "stats.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer store.Close()
|
||||
named := newFakeNamed(t)
|
||||
clk := &clock{time.Date(2026, 10, 5, 10, 0, 30, 0, loc)}
|
||||
opts := Options{QueryLog: true, LogFile: logFile, StatisticsChannel: "unused", TopN: 10,
|
||||
RetainMinute: 7 * 24 * time.Hour, RetainHour: 90 * 24 * time.Hour, RetainDay: 730 * 24 * time.Hour, Location: loc}
|
||||
zones := func() map[string][]string {
|
||||
return map[string][]string{"internal": {"adbraun.lol"}, "external": {"adbraun.lol"}}
|
||||
}
|
||||
c := NewCollector(store, opts, zones, slog.New(slog.NewTextHandler(io.Discard, nil)))
|
||||
c.poller.URL = named.srv.URL
|
||||
c.now = clk.now
|
||||
c.start(ctx)
|
||||
at := time.Date(2026, 10, 5, 10, 0, 40, 0, loc)
|
||||
lines := []string{
|
||||
logLine(at, "10.10.0.37", "internal", "nas.adbraun.lol", "A", "NOERROR"),
|
||||
logLine(at, "10.10.0.37", "internal", "nas.adbraun.lol", "AAAA", "NOERROR"),
|
||||
logLine(at, "10.10.0.42", "internal", "nope.adbraun.lol", "A", "NXDOMAIN"),
|
||||
logLine(at, "203.0.113.9", "external", "www.adbraun.lol", "A", "NOERROR"),
|
||||
logLine(at, "203.0.113.9", "external", "x.example", "A", "REFUSED"),
|
||||
logLine(at, "192.0.2.5", "vps", "debian.org", "AAAA", "NOERROR"),
|
||||
}
|
||||
f, _ := os.Create(logFile)
|
||||
for _, l := range lines {
|
||||
f.WriteString(l)
|
||||
}
|
||||
f.Close()
|
||||
c.readLog()
|
||||
named.add(6, 1)
|
||||
clk.t = time.Date(2026, 10, 5, 10, 1, 2, 0, loc)
|
||||
c.flush(ctx, false)
|
||||
clk.t = time.Date(2026, 10, 5, 11, 0, 2, 0, loc)
|
||||
c.flush(ctx, false)
|
||||
|
||||
all := &Querier{Store: store, Opts: opts, Now: clk.now}
|
||||
s, err := all.Summary(ctx, 24*time.Hour, 5)
|
||||
if err != nil || s.Queries != 6 || s.FilterViews != nil {
|
||||
t.Fatalf("all views: %+v %v", s, err)
|
||||
}
|
||||
in := all.ForViews([]string{"internal"})
|
||||
s, err = in.Summary(ctx, 24*time.Hour, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s.Queries != 3 || len(s.Views) != 1 || s.Views[0].Key != "internal" || s.ViewsSince == nil {
|
||||
t.Errorf("internal: queries %d views %v since %v", s.Queries, s.Views, s.ViewsSince)
|
||||
}
|
||||
if len(s.RCodes) != 2 || s.RCodes[0] != (Entry{Key: "NOERROR", Count: 2}) || len(s.QTypes) != 2 {
|
||||
t.Errorf("internal rcodes %v qtypes %v", s.RCodes, s.QTypes)
|
||||
}
|
||||
if s.Tops[KindName][0] != (Entry{Key: "nas.adbraun.lol", Count: 2}) || len(s.Tops[KindNXDomain]) != 1 || len(s.Answers) != 0 {
|
||||
t.Errorf("internal tops %v answers %v", s.Tops, s.Answers)
|
||||
}
|
||||
// Linked views, say: two together.
|
||||
two := all.ForViews([]string{"internal", "external"})
|
||||
s, _ = two.Summary(ctx, 24*time.Hour, 5)
|
||||
if s.Queries != 5 || len(s.Views) != 2 {
|
||||
t.Errorf("internal+external: %d %v", s.Queries, s.Views)
|
||||
}
|
||||
if es, _ := two.Top(ctx, KindClient, 24*time.Hour, 5); len(es) != 3 || es[2].Key != "10.10.0.42" {
|
||||
t.Errorf("clients of two views: %v", es)
|
||||
}
|
||||
if es, _ := two.Breakdown(ctx, "rcode", 24*time.Hour); len(es) != 3 {
|
||||
t.Errorf("rcodes of two views: %v", es)
|
||||
}
|
||||
if _, err := two.Breakdown(ctx, "answer", 24*time.Hour); !errors.Is(err, ErrNotPerView) {
|
||||
t.Errorf("answers per view: %v", err)
|
||||
}
|
||||
tl, err := all.ForViews([]string{"vps"}).Timeline(ctx, 2*time.Hour, "1m", []string{"queries", "rcode:*"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var total int64
|
||||
for _, v := range tl.Series[0].Values {
|
||||
total += v
|
||||
}
|
||||
if total != 1 || len(tl.Series) != 2 || tl.Series[1].Key != "rcode:NOERROR" {
|
||||
t.Errorf("vps timeline: %d %+v", total, tl.Series)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user