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
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"path/filepath"
|
||||
@@ -14,14 +15,24 @@ import (
|
||||
"jarl/internal/stats"
|
||||
)
|
||||
|
||||
// statsQuerier checks permission and that statistics are enabled.
|
||||
func (s *Server) statsQuerier(p *auth.Principal) (*stats.Querier, error) {
|
||||
// statsQuerier checks permission and that statistics are enabled. With
|
||||
// ?views=internal,vps the figures are for those views only.
|
||||
func (s *Server) statsQuerier(r *http.Request, p *auth.Principal) (*stats.Querier, error) {
|
||||
if err := require(p, auth.Action{Resource: auth.ResStats}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.statsQ == nil {
|
||||
return nil, &httpError{http.StatusNotFound, "stats_disabled", "query statistics are disabled; set stats.enabled: true in /etc/jarl/jarl.yaml and restart jarl (see docs/stats.md)"}
|
||||
}
|
||||
var views []string
|
||||
for _, v := range strings.Split(r.URL.Query().Get("views"), ",") {
|
||||
if v = strings.TrimSpace(v); v != "" {
|
||||
views = append(views, v)
|
||||
}
|
||||
}
|
||||
if len(views) > 0 {
|
||||
return s.statsQ.ForViews(views), nil
|
||||
}
|
||||
return s.statsQ, nil
|
||||
}
|
||||
|
||||
@@ -46,7 +57,7 @@ func (s *Server) statsStatus(w http.ResponseWriter, r *http.Request, p *auth.Pri
|
||||
}
|
||||
|
||||
func (s *Server) statsSummary(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
q, err := s.statsQuerier(p)
|
||||
q, err := s.statsQuerier(r, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,7 +77,7 @@ func (s *Server) statsSummary(w http.ResponseWriter, r *http.Request, p *auth.Pr
|
||||
}
|
||||
|
||||
func (s *Server) statsTop(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
q, err := s.statsQuerier(p)
|
||||
q, err := s.statsQuerier(r, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,7 +104,7 @@ func (s *Server) statsTop(w http.ResponseWriter, r *http.Request, p *auth.Princi
|
||||
}
|
||||
|
||||
func (s *Server) statsTimeline(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
q, err := s.statsQuerier(p)
|
||||
q, err := s.statsQuerier(r, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -124,7 +135,7 @@ func (s *Server) statsTimeline(w http.ResponseWriter, r *http.Request, p *auth.P
|
||||
}
|
||||
|
||||
func (s *Server) statsHeatmap(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
q, err := s.statsQuerier(p)
|
||||
q, err := s.statsQuerier(r, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -143,7 +154,7 @@ func (s *Server) statsHeatmap(w http.ResponseWriter, r *http.Request, p *auth.Pr
|
||||
}
|
||||
|
||||
func (s *Server) statsBreakdown(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
q, err := s.statsQuerier(p)
|
||||
q, err := s.statsQuerier(r, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -152,6 +163,9 @@ func (s *Server) statsBreakdown(w http.ResponseWriter, r *http.Request, p *auth.
|
||||
return err
|
||||
}
|
||||
es, err := q.Breakdown(r.Context(), r.URL.Query().Get("group"), since)
|
||||
if errors.Is(err, stats.ErrNotPerView) {
|
||||
return &httpError{http.StatusBadRequest, "not_per_view", r.URL.Query().Get("group") + ": " + err.Error()}
|
||||
}
|
||||
if err != nil {
|
||||
return badRequest("%v", err)
|
||||
}
|
||||
@@ -247,7 +261,7 @@ func clientNames(st *model.State, want map[netip.Addr]bool, readable func(*model
|
||||
// limits (rndc status), and the space left on the file systems Jarl and
|
||||
// named write to.
|
||||
func (s *Server) statsServer(w http.ResponseWriter, r *http.Request, p *auth.Principal) error {
|
||||
if _, err := s.statsQuerier(p); err != nil {
|
||||
if _, err := s.statsQuerier(r, p); err != nil {
|
||||
return err
|
||||
}
|
||||
out := map[string]any{"server": s.cfg.Stats.Server()}
|
||||
|
||||
Reference in new issue
Block a user