summaryrefslogtreecommitdiff
path: root/cmd/rr/http
diff options
context:
space:
mode:
authorAnton Titov <[email protected]>2019-06-27 13:00:34 +0300
committerGitHub <[email protected]>2019-06-27 13:00:34 +0300
commitce7f331a5e6c6129331e06224865abdb1298584d (patch)
tree4fdf5b9158cf481ee77c408ea8d57d83a7e692f3 /cmd/rr/http
parent5b6e0a535fef745594a4966c724509dcda05b422 (diff)
parent1633d128309765536e7cbb176225926efda7a33c (diff)
Merge pull request #168 from spiral/feature/metrics
Feature/metrics
Diffstat (limited to 'cmd/rr/http')
-rw-r--r--cmd/rr/http/metrics.go123
1 files changed, 123 insertions, 0 deletions
diff --git a/cmd/rr/http/metrics.go b/cmd/rr/http/metrics.go
new file mode 100644
index 00000000..21bbbaf1
--- /dev/null
+++ b/cmd/rr/http/metrics.go
@@ -0,0 +1,123 @@
+package http
+
+import (
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/spf13/cobra"
+ rr "github.com/spiral/roadrunner/cmd/rr/cmd"
+ rrhttp "github.com/spiral/roadrunner/service/http"
+ "github.com/spiral/roadrunner/service/metrics"
+ "github.com/spiral/roadrunner/util"
+ "strconv"
+ "time"
+)
+
+func init() {
+ cobra.OnInitialize(func() {
+ svc, _ := rr.Container.Get(metrics.ID)
+ mtr, ok := svc.(*metrics.Service)
+ if !ok || !mtr.Enabled() {
+ return
+ }
+
+ ht, _ := rr.Container.Get(rrhttp.ID)
+ if ht, ok := ht.(*rrhttp.Service); ok {
+ collector := newCollector()
+
+ // register metrics
+ mtr.MustRegister(collector.requestCounter)
+ mtr.MustRegister(collector.requestDuration)
+ mtr.MustRegister(collector.workersMemory)
+
+ // collect events
+ ht.AddListener(collector.listener)
+
+ // update memory usage every 10 seconds
+ go collector.collectMemory(ht, time.Second*10)
+ }
+ })
+}
+
+// listener provide debug callback for system events. With colors!
+type metricCollector struct {
+ requestCounter *prometheus.CounterVec
+ requestDuration *prometheus.HistogramVec
+ workersMemory prometheus.Gauge
+}
+
+func newCollector() *metricCollector {
+ return &metricCollector{
+ requestCounter: prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "rr_http_request_total",
+ Help: "Total number of handled http requests after server restart.",
+ },
+ []string{"status"},
+ ),
+ requestDuration: prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "rr_http_request_duration_seconds",
+ Help: "HTTP request duration.",
+ },
+ []string{"status"},
+ ),
+ workersMemory: prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "rr_http_workers_memory_bytes",
+ Help: "Memory usage by HTTP workers.",
+ },
+ ),
+ }
+}
+
+// listener listens to http events and generates nice looking output.
+func (c *metricCollector) listener(event int, ctx interface{}) {
+ // http events
+ switch event {
+ case rrhttp.EventResponse:
+ e := ctx.(*rrhttp.ResponseEvent)
+
+ c.requestCounter.With(prometheus.Labels{
+ "status": strconv.Itoa(e.Response.Status),
+ }).Inc()
+
+ c.requestDuration.With(prometheus.Labels{
+ "status": strconv.Itoa(e.Response.Status),
+ }).Observe(e.Elapsed().Seconds())
+
+ case rrhttp.EventError:
+ e := ctx.(*rrhttp.ErrorEvent)
+
+ c.requestCounter.With(prometheus.Labels{
+ "status": "500",
+ }).Inc()
+
+ c.requestDuration.With(prometheus.Labels{
+ "status": "500",
+ }).Observe(e.Elapsed().Seconds())
+ }
+}
+
+// collect memory usage by server workers
+func (c *metricCollector) collectMemory(service *rrhttp.Service, tick time.Duration) {
+ started := false
+ for {
+ server := service.Server()
+ if server == nil && started {
+ // stopped
+ return
+ }
+
+ started = true
+
+ if workers, err := util.ServerState(server); err == nil {
+ sum := 0.0
+ for _, w := range workers {
+ sum = sum + float64(w.MemoryUsage)
+ }
+
+ c.workersMemory.Set(sum)
+ }
+
+ time.Sleep(tick)
+ }
+}