Merge pull request #9 in DNS/adguard-dns from consistent-stats to master
* commit '31893410892bd047c9f6ea8f602717e6996c9491': web interface -- Make refresh buttons reload all data, not just counters web interface -- change text from 'general counters' to 'general statistics' Fixup of previous commit -- errand keystroke crept in API /stats_top -- sort top entries by value API /stats_top -- show only top entries for last 3 minutes
This commit is contained in:
commit
ba836220b8
@ -5,7 +5,7 @@ import Card from '../ui/Card';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
|
||||
const Counters = props => (
|
||||
<Card title="General counters" subtitle="in the last 3 minutes" bodyType="card-table" refresh={props.refreshButton}>
|
||||
<Card title="General statistics" subtitle="in the last 3 minutes" bodyType="card-table" refresh={props.refreshButton}>
|
||||
<table className="table card-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
@ -27,8 +27,8 @@ class Dashboard extends Component {
|
||||
dashboard.processingTopStats;
|
||||
|
||||
const disableButton = <button type="button" className="btn btn-outline-secondary btn-sm mr-2" onClick={() => this.props.disableDns()}>Disable DNS</button>;
|
||||
const refreshFullButton = <button type="button" className="btn btn-outline-primary btn-sm" onClick={() => this.props.getStats()}>Refresh statistics</button>;
|
||||
const refreshButton = <button type="button" className="btn btn-outline-primary btn-sm card-refresh" onClick={() => this.props.getStats()}></button>;
|
||||
const refreshFullButton = <button type="button" className="btn btn-outline-primary btn-sm" onClick={() => this.componentDidMount()}>Refresh statistics</button>;
|
||||
const refreshButton = <button type="button" className="btn btn-outline-primary btn-sm card-refresh" onClick={() => this.componentDidMount()}></button>;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
47
control.go
47
control.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -409,6 +410,9 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) {
|
||||
domains := map[string]int{}
|
||||
blocked := map[string]int{}
|
||||
clients := map[string]int{}
|
||||
now := time.Now()
|
||||
timeWindow := time.Minute * 3
|
||||
notBefore := now.Add(timeWindow * -1)
|
||||
|
||||
for _, value := range values {
|
||||
entry, ok := value.(map[string]interface{})
|
||||
@ -419,6 +423,11 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) {
|
||||
host := getHost(entry)
|
||||
reason := getReason(entry)
|
||||
client := getClient(entry)
|
||||
time := getTime(entry)
|
||||
if time.Before(notBefore) {
|
||||
// skip if the entry is before specified cutoff
|
||||
continue
|
||||
}
|
||||
if len(host) > 0 {
|
||||
domains[host]++
|
||||
}
|
||||
@ -430,21 +439,35 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
toMarshal := map[string]interface{}{
|
||||
"top_queried_domains": produceTop(domains, 50),
|
||||
"top_blocked_domains": produceTop(blocked, 50),
|
||||
"top_clients": produceTop(clients, 50),
|
||||
}
|
||||
json, err := json.Marshal(toMarshal)
|
||||
if err != nil {
|
||||
errortext := fmt.Sprintf("Couldn't marshal into JSON: %s", err)
|
||||
log.Println(errortext)
|
||||
http.Error(w, errortext, http.StatusBadGateway)
|
||||
return
|
||||
// use manual json marshalling because we want maps to be sorted by value
|
||||
json := bytes.Buffer{}
|
||||
json.WriteString("{\n")
|
||||
|
||||
gen := func(json *bytes.Buffer, name string, top map[string]int, addComma bool) {
|
||||
json.WriteString(" \"")
|
||||
json.WriteString(name)
|
||||
json.WriteString("\": {\n")
|
||||
sorted := sortByValue(top)
|
||||
for i, key := range sorted {
|
||||
fmt.Fprintf(json, " \"%s\": %d", key, top[key])
|
||||
if i+1 != len(sorted) {
|
||||
json.WriteByte(',')
|
||||
}
|
||||
json.WriteByte('\n')
|
||||
}
|
||||
json.WriteString(" }")
|
||||
if addComma {
|
||||
json.WriteByte(',')
|
||||
}
|
||||
json.WriteByte('\n')
|
||||
}
|
||||
gen(&json, "top_queried_domains", domains, true)
|
||||
gen(&json, "top_blocked_domains", blocked, true)
|
||||
gen(&json, "top_clients", clients, false)
|
||||
json.WriteString("}\n")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err = w.Write(json)
|
||||
_, err = w.Write(json.Bytes())
|
||||
if err != nil {
|
||||
errortext := fmt.Sprintf("Couldn't write body: %s", err)
|
||||
log.Println(errortext)
|
||||
|
17
helpers.go
17
helpers.go
@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func clamp(value, low, high int) int {
|
||||
@ -167,6 +168,22 @@ func getClient(entry map[string]interface{}) string {
|
||||
return client
|
||||
}
|
||||
|
||||
func getTime(entry map[string]interface{}) time.Time {
|
||||
t, ok := entry["time"]
|
||||
if !ok {
|
||||
return time.Time{}
|
||||
}
|
||||
tstr, ok := t.(string)
|
||||
if !ok {
|
||||
return time.Time{}
|
||||
}
|
||||
value, err := time.Parse(time.RFC3339, tstr)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// helper functions for parsing parameters from body
|
||||
// -------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user