Skip to content

Commit c8459e1

Browse files
committed
prometheus
1 parent e19c5d4 commit c8459e1

File tree

5 files changed

+168
-3
lines changed

5 files changed

+168
-3
lines changed

Diff for: cmd/rr/http/metrics.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package http
2+
3+
import (
4+
"github.com/prometheus/client_golang/prometheus"
5+
"github.com/spf13/cobra"
6+
rr "github.com/spiral/roadrunner/cmd/rr/cmd"
7+
rrhttp "github.com/spiral/roadrunner/service/http"
8+
"github.com/spiral/roadrunner/service/metrics"
9+
"strconv"
10+
)
11+
12+
func init() {
13+
cobra.OnInitialize(func() {
14+
svc, _ := rr.Container.Get(metrics.ID)
15+
mtr, ok := svc.(*metrics.Service)
16+
if !ok || !mtr.Enabled() {
17+
return
18+
}
19+
20+
ht, _ := rr.Container.Get(rrhttp.ID)
21+
if ht, ok := ht.(*rrhttp.Service); ok {
22+
collector := newCollector()
23+
24+
// register metrics
25+
mtr.MustRegister(collector.requestCounter)
26+
mtr.MustRegister(collector.requestDuration)
27+
28+
// collect events
29+
ht.AddListener(collector.listener)
30+
}
31+
})
32+
}
33+
34+
// listener provide debug callback for system events. With colors!
35+
type metricCollector struct {
36+
requestCounter *prometheus.CounterVec
37+
requestDuration *prometheus.HistogramVec
38+
}
39+
40+
func newCollector() *metricCollector {
41+
return &metricCollector{
42+
requestCounter: prometheus.NewCounterVec(
43+
prometheus.CounterOpts{
44+
Name: "rr_http_total",
45+
Help: "Total number of handled http requests after server restart.",
46+
},
47+
[]string{"status"},
48+
),
49+
requestDuration: prometheus.NewHistogramVec(
50+
prometheus.HistogramOpts{
51+
Name: "rr_http_request_duration",
52+
Help: "HTTP request duration.",
53+
Buckets: []float64{0.25, 0.5, 1, 10, 20, 60},
54+
},
55+
[]string{"status"},
56+
),
57+
}
58+
}
59+
60+
// listener listens to http events and generates nice looking output.
61+
func (c *metricCollector) listener(event int, ctx interface{}) {
62+
// http events
63+
switch event {
64+
case rrhttp.EventResponse:
65+
e := ctx.(*rrhttp.ResponseEvent)
66+
67+
c.requestCounter.With(prometheus.Labels{
68+
"status": strconv.Itoa(e.Response.Status),
69+
}).Inc()
70+
71+
c.requestDuration.With(prometheus.Labels{
72+
"status": strconv.Itoa(e.Response.Status),
73+
}).Observe(e.Elapsed().Seconds())
74+
75+
case rrhttp.EventError:
76+
e := ctx.(*rrhttp.ErrorEvent)
77+
78+
c.requestCounter.With(prometheus.Labels{
79+
"status": "500",
80+
}).Inc()
81+
82+
c.requestDuration.With(prometheus.Labels{
83+
"status": "500",
84+
}).Observe(e.Elapsed().Seconds())
85+
}
86+
}

Diff for: cmd/rr/main.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ package main
2424

2525
import (
2626
rr "github.com/spiral/roadrunner/cmd/rr/cmd"
27-
"github.com/spiral/roadrunner/service/headers"
2827

2928
// services (plugins)
3029
"github.com/spiral/roadrunner/service/env"
30+
"github.com/spiral/roadrunner/service/headers"
3131
"github.com/spiral/roadrunner/service/http"
3232
"github.com/spiral/roadrunner/service/limit"
33+
"github.com/spiral/roadrunner/service/metrics"
3334
"github.com/spiral/roadrunner/service/rpc"
3435
"github.com/spiral/roadrunner/service/static"
3536

@@ -42,6 +43,7 @@ func main() {
4243
rr.Container.Register(env.ID, &env.Service{})
4344
rr.Container.Register(rpc.ID, &rpc.Service{})
4445
rr.Container.Register(http.ID, &http.Service{})
46+
rr.Container.Register(metrics.ID, &metrics.Service{})
4547
rr.Container.Register(headers.ID, &headers.Service{})
4648
rr.Container.Register(static.ID, &static.Service{})
4749
rr.Container.Register(limit.ID, &limit.Service{})

Diff for: go.mod

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ require (
1313
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
1414
github.com/olekukonko/tablewriter v0.0.1
1515
github.com/pkg/errors v0.8.1
16+
github.com/prometheus/client_golang v1.0.0
1617
github.com/shirou/gopsutil v2.17.12+incompatible
1718
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
1819
github.com/sirupsen/logrus v1.3.0
1920
github.com/spf13/cobra v0.0.3
2021
github.com/spf13/viper v1.3.1
2122
github.com/spiral/goridge v2.1.3+incompatible
22-
github.com/stretchr/testify v1.2.2
23+
github.com/stretchr/testify v1.3.0
2324
github.com/yookoala/gofast v0.3.0
24-
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f
25+
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
2526
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52 // indirect
2627
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
2728
)

Diff for: service/metrics/config.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package metrics
2+
3+
import "github.com/spiral/roadrunner/service"
4+
5+
type Config struct {
6+
// Address to listen
7+
Address string
8+
9+
// Metrics define application specific metrics.
10+
Metrics map[string]Metric
11+
}
12+
13+
// Metric describes single application specific metric.
14+
type Metric struct {
15+
Type string
16+
Description string
17+
Labels []string
18+
}
19+
20+
// Hydrate configuration.
21+
func (c *Config) Hydrate(cfg service.Config) error {
22+
return cfg.Unmarshal(c)
23+
}

Diff for: service/metrics/service.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package metrics
2+
3+
import (
4+
"context"
5+
"github.com/prometheus/client_golang/prometheus"
6+
"github.com/prometheus/client_golang/prometheus/promhttp"
7+
"net/http"
8+
)
9+
10+
// ID declares public service name.
11+
const ID = "metrics"
12+
13+
// Service to manage application metrics using Prometheus.
14+
type Service struct {
15+
cfg *Config
16+
http *http.Server
17+
}
18+
19+
// Init service.
20+
func (s *Service) Init(cfg *Config) (bool, error) {
21+
s.cfg = cfg
22+
return true, nil
23+
}
24+
25+
// Enabled indicates that server is able to collect metrics.
26+
func (s *Service) Enabled() bool {
27+
return s.cfg != nil
28+
}
29+
30+
// Register new prometheus collector.
31+
func (s *Service) Register(c prometheus.Collector) error {
32+
return prometheus.Register(c)
33+
}
34+
35+
// MustRegister registers new collector or fails with panic.
36+
func (s *Service) MustRegister(c prometheus.Collector) {
37+
if err := prometheus.Register(c); err != nil {
38+
panic(err)
39+
}
40+
}
41+
42+
// Serve prometheus metrics service.
43+
func (s *Service) Serve() error {
44+
s.http = &http.Server{Addr: s.cfg.Address, Handler: promhttp.Handler()}
45+
46+
return s.http.ListenAndServe()
47+
}
48+
49+
// Stop prometheus metrics service.
50+
func (s *Service) Stop() {
51+
// gracefully stop server
52+
s.http.Shutdown(context.Background())
53+
}

0 commit comments

Comments
 (0)