feature(app): Add a debugging summary function, exposed via http

URL is /admin/summary
This commit is contained in:
Bryan Boreham
2019-09-17 10:48:23 +00:00
parent c143099412
commit 11e76f1740
5 changed files with 81 additions and 0 deletions
+19
View File
@@ -31,6 +31,7 @@ type Reporter interface {
Report(context.Context, time.Time) (report.Report, error)
HasReports(context.Context, time.Time) (bool, error)
HasHistoricReports() bool
AdminSummary(context.Context, time.Time) (string, error)
WaitOn(context.Context, chan struct{})
UnWait(context.Context, chan struct{})
}
@@ -172,6 +173,20 @@ func (c *collector) HasHistoricReports() bool {
return false
}
// AdminSummary returns a string with some internal information about
// the report, which may be useful to troubleshoot.
func (c *collector) AdminSummary(ctx context.Context, timestamp time.Time) (string, error) {
c.mtx.Lock()
defer c.mtx.Unlock()
var b strings.Builder
for i := range c.reports {
fmt.Fprintf(&b, "%v: ", c.timestamps[i].Format(time.StampMilli))
b.WriteString(c.reports[i].Summary())
b.WriteByte('\n')
}
return b.String(), nil
}
// remove reports older than the app.window
func (c *collector) clean() {
var (
@@ -240,6 +255,10 @@ func (c StaticCollector) HasHistoricReports() bool {
return false
}
func (c StaticCollector) AdminSummary(ctx context.Context, timestamp time.Time) (string, error) {
return "not implemented", nil
}
// Add adds a report to the collector's internal state. It implements Adder.
func (c StaticCollector) Add(context.Context, report.Report, []byte) error { return nil }
+29
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"strconv"
"strings"
"sync"
"time"
@@ -444,6 +445,34 @@ func (c *awsCollector) HasHistoricReports() bool {
return true
}
// AdminSummary returns a string with some internal information about
// the report, which may be useful to troubleshoot.
func (c *awsCollector) AdminSummary(ctx context.Context, timestamp time.Time) (string, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "awsCollector.Report")
defer span.Finish()
userid, err := c.cfg.UserIDer(ctx)
if err != nil {
return "", err
}
end := timestamp
start := end.Add(-c.cfg.Window)
reportKeys, err := c.getReportKeys(ctx, userid, start, end)
if err != nil {
return "", err
}
reports, err := c.reportsForKeysInRange(ctx, reportKeys, start.UnixNano(), end.UnixNano())
if err != nil {
return "", err
}
var b strings.Builder
for i := range reports {
// TODO: print the key - note reports may be in a different order from reportKeys
b.WriteString(reports[i].Summary())
b.WriteByte('\n')
}
return b.String(), nil
}
// calculateDynamoKeys generates the row & column keys for Dynamo.
func calculateDynamoKeys(userid string, now time.Time) (string, string) {
rowKey := fmt.Sprintf("%s-%s", userid, strconv.FormatInt(now.UnixNano()/time.Hour.Nanoseconds(), 10))
+12
View File
@@ -159,6 +159,18 @@ func RegisterReportPostHandler(a Adder, router *mux.Router) {
}))
}
// RegisterAdminRoutes registers routes for admin calls with a http mux.
func RegisterAdminRoutes(router *mux.Router, reporter Reporter) {
get := router.Methods("GET").Subrouter()
get.Handle("/admin/summary", requestContextDecorator(func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
summary, err := reporter.AdminSummary(ctx, time.Now())
if err != nil {
respondWith(w, http.StatusBadRequest, err)
}
fmt.Fprintln(w, summary)
}))
}
var newVersion = struct {
sync.Mutex
*xfer.NewVersionInfo
+1
View File
@@ -67,6 +67,7 @@ func router(collector app.Collector, controlRouter app.ControlRouter, pipeRouter
app.RegisterControlRoutes(router, controlRouter)
app.RegisterPipeRoutes(router, pipeRouter)
app.RegisterTopologyRoutes(router, app.WebReporter{Reporter: collector, MetricsGraphURL: metricsGraphURL}, capabilities)
app.RegisterAdminRoutes(router, collector)
uiHandler := http.FileServer(GetFS(externalUI))
router.PathPrefix("/ui").Name("static").Handler(
+20
View File
@@ -559,6 +559,26 @@ func (r Report) upgradeDNSRecords() Report {
return r
}
func (r Report) Summary() string {
ret := ""
if len(r.Host.Nodes) == 1 {
for k, _ := range r.Host.Nodes {
ret = k + ":"
}
}
count := 0
r.WalkNamedTopologies(func(n string, t *Topology) {
if len(t.Nodes) > 0 {
count++
if count > 1 {
ret = ret + ", "
}
ret = ret + fmt.Sprintf("%s:%d", n, len(t.Nodes))
}
})
return ret
}
// Sampling describes how the packet data sources for this report were
// sampled. It can be used to calculate effective sample rates. We can't
// just put the rate here, because that can't be accurately merged. Counts