From 38dc3e93eef9cd38f0668ce7efca73a4ca188cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Matczuk?= Date: Mon, 6 Sep 2021 16:42:23 +0200 Subject: [PATCH] fshttp: add prometheus metrics for HTTP status code This patch adds rclone_http_status_code counter vector labeled by * host, * method, * code. It allows to see HTTP errors, backoffs etc. The Metrics struct is designed for extensibility. Adding new metrics is a matter of adding them to Metrics struct and including them in the response handling. This feature has been discussed in the forum [1]. [1] https://forum.rclone.org/t/prometheus-metrics/14484 --- fs/fshttp/http.go | 6 +++++ fs/fshttp/prometheus.go | 51 ++++++++++++++++++++++++++++++++++++++ fs/rc/rcserver/rcserver.go | 14 ++++++++--- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 fs/fshttp/prometheus.go diff --git a/fs/fshttp/http.go b/fs/fshttp/http.go index b2b72bb99..ef7cf2851 100644 --- a/fs/fshttp/http.go +++ b/fs/fshttp/http.go @@ -136,12 +136,14 @@ func NewClient(ctx context.Context) *http.Client { // Transport is our http Transport which wraps an http.Transport // * Sets the User Agent // * Does logging +// * Updates metrics type Transport struct { *http.Transport dump fs.DumpFlags filterRequest func(req *http.Request) userAgent string headers []*fs.HTTPOption + metrics *Metrics } // newTransport wraps the http.Transport passed in and logs all @@ -152,6 +154,7 @@ func newTransport(ci *fs.ConfigInfo, transport *http.Transport) *Transport { dump: ci.Dump, userAgent: ci.UserAgent, headers: ci.Headers, + metrics: DefaultMetrics, } } @@ -283,6 +286,9 @@ func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error fs.Debugf(nil, "%s", separatorResp) logMutex.Unlock() } + // Update metrics + t.metrics.onResponse(req, resp) + if err == nil { checkServerTime(req, resp) } diff --git a/fs/fshttp/prometheus.go b/fs/fshttp/prometheus.go new file mode 100644 index 000000000..2f83113b0 --- /dev/null +++ b/fs/fshttp/prometheus.go @@ -0,0 +1,51 @@ +package fshttp + +import ( + "fmt" + "net/http" + + "github.com/prometheus/client_golang/prometheus" +) + +// Metrics provide Transport HTTP level metrics. +type Metrics struct { + StatusCode *prometheus.CounterVec +} + +// NewMetrics creates a new metrics instance, the instance shall be assigned to +// DefaultMetrics before any processing takes place. +func NewMetrics(namespace string) *Metrics { + return &Metrics{ + StatusCode: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: "http", + Name: "status_code", + }, []string{"host", "method", "code"}), + } +} + +// DefaultMetrics specifies metrics used for new Transports. +var DefaultMetrics = (*Metrics)(nil) + +// Collectors returns all prometheus metrics as collectors for registration. +func (m *Metrics) Collectors() []prometheus.Collector { + if m == nil { + return nil + } + return []prometheus.Collector{ + m.StatusCode, + } +} + +func (m *Metrics) onResponse(req *http.Request, resp *http.Response) { + if m == nil { + return + } + + var statusCode = 0 + if resp != nil { + statusCode = resp.StatusCode + } + + m.StatusCode.WithLabelValues(req.Host, req.Method, fmt.Sprint(statusCode)).Inc() +} diff --git a/fs/rc/rcserver/rcserver.go b/fs/rc/rcserver/rcserver.go index 2e74a7c06..5e5ae84f5 100644 --- a/fs/rc/rcserver/rcserver.go +++ b/fs/rc/rcserver/rcserver.go @@ -18,23 +18,22 @@ import ( "sync" "time" - "github.com/rclone/rclone/fs/rc/webgui" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/skratchdot/open-golang/open" - "github.com/rclone/rclone/cmd/serve/httplib" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/accounting" "github.com/rclone/rclone/fs/cache" "github.com/rclone/rclone/fs/config" + "github.com/rclone/rclone/fs/fshttp" "github.com/rclone/rclone/fs/list" "github.com/rclone/rclone/fs/rc" "github.com/rclone/rclone/fs/rc/jobs" "github.com/rclone/rclone/fs/rc/rcflags" + "github.com/rclone/rclone/fs/rc/webgui" "github.com/rclone/rclone/lib/http/serve" "github.com/rclone/rclone/lib/random" + "github.com/skratchdot/open-golang/open" ) var promHandler http.Handler @@ -43,6 +42,13 @@ var onlyOnceWarningAllowOrigin sync.Once func init() { rcloneCollector := accounting.NewRcloneCollector(context.Background()) prometheus.MustRegister(rcloneCollector) + + m := fshttp.NewMetrics("rclone") + for _, c := range m.Collectors() { + prometheus.MustRegister(c) + } + fshttp.DefaultMetrics = m + promHandler = promhttp.Handler() }