mirror of
https://github.com/bakito/adguardhome-sync.git
synced 2025-10-26 11:19:54 +00:00
Draw also stats of single instances in dashboard diagram (#462)
* allow multiple graphs * draw single instances
This commit is contained in:
parent
00ee5415a5
commit
89aeec5f97
@ -7,6 +7,8 @@ import (
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
const StatsTotal = "total"
|
||||
|
||||
var (
|
||||
l = log.GetLogger("metrics")
|
||||
|
||||
@ -155,14 +157,11 @@ func initMetric(name string, metric *prometheus.GaugeVec) {
|
||||
}
|
||||
|
||||
func Update(ims ...InstanceMetrics) {
|
||||
s := OverallStats{}
|
||||
for _, im := range ims {
|
||||
update(im)
|
||||
s[im.HostName] = im.Stats
|
||||
stats[im.HostName] = im.Stats
|
||||
}
|
||||
|
||||
stats = s
|
||||
|
||||
l.Debug("updated")
|
||||
}
|
||||
|
||||
@ -241,12 +240,13 @@ type InstanceMetrics struct {
|
||||
|
||||
type OverallStats map[string]*model.Stats
|
||||
|
||||
func (s OverallStats) consolidate() *model.Stats {
|
||||
total := model.NewStats()
|
||||
for _, stats := range s {
|
||||
total.Add(stats)
|
||||
func (os OverallStats) consolidate() OverallStats {
|
||||
consolidated := OverallStats{StatsTotal: model.NewStats()}
|
||||
for host, stats := range os {
|
||||
consolidated[host] = stats
|
||||
consolidated[StatsTotal].Add(stats)
|
||||
}
|
||||
return total
|
||||
return consolidated
|
||||
}
|
||||
|
||||
func safeMetric[T Number](v *T) float64 {
|
||||
@ -260,6 +260,10 @@ type Number interface {
|
||||
constraints.Float | constraints.Integer
|
||||
}
|
||||
|
||||
func GetStats() *model.Stats {
|
||||
func GetStats() OverallStats {
|
||||
return stats.consolidate()
|
||||
}
|
||||
|
||||
func (os OverallStats) Total() *model.Stats {
|
||||
return os[StatsTotal]
|
||||
}
|
||||
|
||||
@ -33,6 +33,8 @@ func (w *worker) handleSync(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (w *worker) handleRoot(c *gin.Context) {
|
||||
total, dns, blocked, malware, adult := statsGraph()
|
||||
|
||||
c.HTML(http.StatusOK, "index.html", map[string]interface{}{
|
||||
"DarkMode": w.cfg.API.DarkMode,
|
||||
"Metrics": w.cfg.API.Metrics.Enabled,
|
||||
@ -41,18 +43,18 @@ func (w *worker) handleRoot(c *gin.Context) {
|
||||
"SyncStatus": w.status(),
|
||||
"Stats": map[string]interface{}{
|
||||
"Labels": getLast24Hours(),
|
||||
"DNS": metrics.GetStats().DnsQueries,
|
||||
"Blocked": metrics.GetStats().BlockedFiltering,
|
||||
"BlockedPercentage": fmt.Sprintf("%.2f", (float64(*metrics.GetStats().NumBlockedFiltering)*100.0)/float64(*metrics.GetStats().NumDnsQueries)),
|
||||
"Malware": metrics.GetStats().ReplacedSafebrowsing,
|
||||
"MalwarePercentage": fmt.Sprintf("%.2f", (float64(*metrics.GetStats().NumReplacedSafebrowsing)*100.0)/float64(*metrics.GetStats().NumDnsQueries)),
|
||||
"Adult": metrics.GetStats().ReplacedParental,
|
||||
"AdultPercentage": fmt.Sprintf("%.2f", (float64(*metrics.GetStats().NumReplacedParental)*100.0)/float64(*metrics.GetStats().NumDnsQueries)),
|
||||
"DNS": dns,
|
||||
"Blocked": blocked,
|
||||
"BlockedPercentage": fmt.Sprintf("%.2f", (float64(*total.NumBlockedFiltering)*100.0)/float64(*total.NumDnsQueries)),
|
||||
"Malware": malware,
|
||||
"MalwarePercentage": fmt.Sprintf("%.2f", (float64(*total.NumReplacedSafebrowsing)*100.0)/float64(*total.NumDnsQueries)),
|
||||
"Adult": adult,
|
||||
"AdultPercentage": fmt.Sprintf("%.2f", (float64(*total.NumReplacedParental)*100.0)/float64(*total.NumDnsQueries)),
|
||||
|
||||
"TotalDNS": metrics.GetStats().NumDnsQueries,
|
||||
"TotalBlocked": metrics.GetStats().NumBlockedFiltering,
|
||||
"TotalMalware": metrics.GetStats().NumReplacedSafebrowsing,
|
||||
"TotalAdult": metrics.GetStats().NumReplacedParental,
|
||||
"TotalDNS": total.NumDnsQueries,
|
||||
"TotalBlocked": total.NumBlockedFiltering,
|
||||
"TotalMalware": total.NumReplacedSafebrowsing,
|
||||
"TotalAdult": total.NumReplacedParental,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@ -181,20 +181,27 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
// Function to create minimal line charts
|
||||
function createMinimalChart(canvasId, lineData, r, g, b) {
|
||||
function createChart(canvasId, data) {
|
||||
const ctx = document.getElementById(canvasId).getContext('2d');
|
||||
|
||||
const datasets = Array(data.length);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
datasets[i] = {
|
||||
data: data[i].data,
|
||||
title: data[i].title,
|
||||
backgroundColor: `rgb(${data[i].r}, ${data[i].g}, ${data[i].b}, 0.2)`,
|
||||
borderColor: `rgb(${data[i].r}, ${data[i].g}, ${data[i].b}, 1)`,
|
||||
borderWidth: 3,
|
||||
fill: data[i].fill,
|
||||
pointRadius: 0,
|
||||
}
|
||||
}
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: {{.Stats.Labels}},
|
||||
datasets: [{
|
||||
data: lineData,
|
||||
backgroundColor: `rgb(${r}, ${g}, ${b}, 0.2)`,
|
||||
borderColor: `rgb(${r}, ${g}, ${b}, 1)`,
|
||||
borderWidth: 3,
|
||||
fill: true,
|
||||
pointRadius: 0,
|
||||
}]
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
@ -216,6 +223,9 @@
|
||||
displayColors: false,
|
||||
callbacks: {
|
||||
label: function(tooltipItem) {
|
||||
if (tooltipItem.dataset.title) {
|
||||
return tooltipItem.raw + " - " + tooltipItem.dataset.title;
|
||||
}
|
||||
return tooltipItem.raw;
|
||||
}
|
||||
}
|
||||
@ -237,10 +247,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
createMinimalChart('dnsQueriesChart', {{.Stats.DNS}}, 78, 141, 245); // RGB Blue
|
||||
createMinimalChart('blockedFiltersChart', {{.Stats.Blocked}}, 255, 94, 94); // RGB Red
|
||||
createMinimalChart('malwareChart', {{.Stats.Malware}}, 110, 224, 122); // RGB Green
|
||||
createMinimalChart('adultWebsitesChart', {{.Stats.Adult}}, 232, 198, 78); // RGB Yellow
|
||||
createChart('dnsQueriesChart', {{.Stats.DNS}});
|
||||
createChart('blockedFiltersChart', {{.Stats.Blocked}});
|
||||
createChart('malwareChart', {{.Stats.Malware}});
|
||||
createChart('adultWebsitesChart', {{.Stats.Adult}});
|
||||
</script>
|
||||
{{- end }}
|
||||
</body>
|
||||
|
||||
116
pkg/sync/stats.go
Normal file
116
pkg/sync/stats.go
Normal file
@ -0,0 +1,116 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/bakito/adguardhome-sync/pkg/client/model"
|
||||
"github.com/bakito/adguardhome-sync/pkg/metrics"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
blue = []int{78, 141, 245}
|
||||
blueAlternatives = [][]int{
|
||||
{44, 95, 163},
|
||||
{122, 166, 247},
|
||||
{30, 61, 92},
|
||||
{93, 158, 255},
|
||||
{58, 123, 213},
|
||||
}
|
||||
|
||||
red = []int{255, 94, 94}
|
||||
redAlternatives = [][]int{
|
||||
{204, 59, 59},
|
||||
{255, 127, 127},
|
||||
{140, 36, 36},
|
||||
{255, 153, 153},
|
||||
{255, 66, 66},
|
||||
}
|
||||
|
||||
yellow = []int{232, 198, 78}
|
||||
yellowAlternatives = [][]int{
|
||||
{196, 163, 60},
|
||||
{255, 220, 110},
|
||||
{140, 114, 36},
|
||||
{250, 233, 156},
|
||||
{212, 180, 84},
|
||||
}
|
||||
|
||||
green = []int{110, 224, 122}
|
||||
greenAlternatives = [][]int{
|
||||
{68, 160, 80},
|
||||
{142, 255, 158},
|
||||
{44, 140, 63},
|
||||
{163, 255, 192},
|
||||
{85, 198, 102},
|
||||
}
|
||||
)
|
||||
|
||||
func statsGraph() (*model.Stats, []line, []line, []line, []line) {
|
||||
s := metrics.GetStats()
|
||||
t := s.Total()
|
||||
dns := graphLines(t, s, blue, blueAlternatives, func(s *model.Stats) []int {
|
||||
return *s.DnsQueries
|
||||
})
|
||||
blocked := graphLines(t, s, red, redAlternatives, func(s *model.Stats) []int {
|
||||
return *s.BlockedFiltering
|
||||
})
|
||||
malware := graphLines(t, s, green, greenAlternatives, func(s *model.Stats) []int {
|
||||
return *s.ReplacedSafebrowsing
|
||||
})
|
||||
adult := graphLines(t, s, yellow, yellowAlternatives, func(s *model.Stats) []int {
|
||||
return *s.ReplacedParental
|
||||
})
|
||||
|
||||
return t, dns, blocked, malware, adult
|
||||
}
|
||||
|
||||
func graphLines(t *model.Stats, s metrics.OverallStats, baseColor []int, altColors [][]int, dataCB func(s *model.Stats) []int) []line {
|
||||
g := &graph{
|
||||
total: line{
|
||||
Fill: true,
|
||||
Title: "Total",
|
||||
Data: dataCB(t),
|
||||
R: baseColor[0],
|
||||
G: baseColor[1],
|
||||
B: baseColor[2],
|
||||
},
|
||||
}
|
||||
|
||||
var i int
|
||||
for name, data := range s {
|
||||
if name != metrics.StatsTotal {
|
||||
g.replicas = append(g.replicas, line{
|
||||
Fill: false,
|
||||
Title: name,
|
||||
Data: dataCB(data),
|
||||
R: altColors[i%len(altColors)][0],
|
||||
G: altColors[i%len(altColors)][1],
|
||||
B: altColors[i%len(altColors)][2],
|
||||
})
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
lines := []line{g.total}
|
||||
|
||||
slices.SortFunc(g.replicas, func(a, b line) int {
|
||||
return strings.Compare(a.Title, b.Title)
|
||||
})
|
||||
lines = append(lines, g.replicas...)
|
||||
return lines
|
||||
}
|
||||
|
||||
type graph struct {
|
||||
total line
|
||||
replicas []line
|
||||
}
|
||||
|
||||
type line struct {
|
||||
Data []int `json:"data"`
|
||||
R int `json:"r"`
|
||||
G int `json:"g"`
|
||||
B int `json:"b"`
|
||||
Title string `json:"title"`
|
||||
Fill bool `json:"fill"`
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user