diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 68c335ba3..a05e30275 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -25,6 +25,7 @@ import ( "strings" "time" + "github.com/rcrowley/go-metrics" "github.com/syncthing/syncthing/lib/auto" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/db" @@ -180,10 +181,11 @@ func (s *apiSvc) Serve() { // Debug endpoints, not for general use getRestMux.HandleFunc("/rest/debug/peerCompletion", s.getPeerCompletion) + getRestMux.HandleFunc("/rest/debug/httpmetrics", s.getSystemHTTPMetrics) // A handler that splits requests between the two above and disables // caching - restMux := noCacheMiddleware(getPostHandler(getRestMux, postRestMux)) + restMux := noCacheMiddleware(metricsMiddleware(getPostHandler(getRestMux, postRestMux))) // The main routing handler mux := http.NewServeMux() @@ -320,6 +322,15 @@ func debugMiddleware(h http.Handler) http.Handler { }) } +func metricsMiddleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t := metrics.GetOrRegisterTimer(r.URL.Path, nil) + t0 := time.Now() + h.ServeHTTP(w, r) + t.UpdateSince(t0) + }) +} + func redirectToHTTPSMiddleware(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Add a generous access-control-allow-origin header since we may be @@ -743,6 +754,26 @@ func (s *apiSvc) getSystemLogTxt(w http.ResponseWriter, r *http.Request) { } } +func (s *apiSvc) getSystemHTTPMetrics(w http.ResponseWriter, r *http.Request) { + stats := make(map[string]interface{}) + metrics.Each(func(name string, intf interface{}) { + if m, ok := intf.(*metrics.StandardTimer); ok { + pct := m.Percentiles([]float64{0.50, 0.95, 0.99}) + for i := range pct { + pct[i] /= 1e6 // ns to ms + } + stats[name] = map[string]interface{}{ + "count": m.Count(), + "sumMs": m.Sum() / 1e6, // ns to ms + "ratesPerS": []float64{m.Rate1(), m.Rate5(), m.Rate15()}, + "percentilesMs": pct, + } + } + }) + bs, _ := json.MarshalIndent(stats, "", " ") + w.Write(bs) +} + func (s *apiSvc) getSystemDiscovery(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") devices := make(map[string]discover.CacheEntry)