Skip to content

Commit 5cfb64c

Browse files
authored
Added prometheus metrics (#366)
1 parent 4287542 commit 5cfb64c

File tree

20 files changed

+887
-75
lines changed

20 files changed

+887
-75
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Improve server startup message (#358, @areveny)
88
* Introduce yaml linter. (#362, @miry)
99
* Handle slicer toxic with zero `SizeVariation` and fix slicing randomization (#359, @areveny)
10+
* Added /metrics endpoint for exposing Prometheus-compatible internal metrics (#366, @neufeldtech)
1011

1112
# [2.3.0] - 2021-12-23
1213

METRICS.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Metrics
2+
3+
- [Metrics](#metrics)
4+
- [Runtime Metrics](#runtime-metrics)
5+
- [Proxy Metrics](#proxy-metrics)
6+
- [toxiproxy_proxy_received_bytes_total / toxiproxy_proxy_sent_bytes_total](#toxiproxy_proxy_received_bytes_total--toxiproxy_proxy_sent_bytes_total)
7+
8+
### Runtime Metrics
9+
10+
To enable runtime metrics related to the state of the go runtime, build version, process info, use the `-runtime-metrics` flag.
11+
12+
For more details, see below:
13+
- [NewGoCollector](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus/collectors#NewGoCollector)
14+
- [NewBuildInfoCollector](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus/collectors#NewBuildInfoCollector)
15+
- [NewProcessCollector](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus/collectors#NewProcessCollector)
16+
17+
### Proxy Metrics
18+
19+
To enable metrics related to toxiproxy internals, use the `-proxy-metrics` flag.
20+
#### toxiproxy_proxy_received_bytes_total / toxiproxy_proxy_sent_bytes_total
21+
22+
The total number of bytes received/sent on a given proxy link in a given direction
23+
24+
```mermaid
25+
sequenceDiagram
26+
Client->>+Toxiproxy: toxiproxy_proxy_received_bytes_total{direction="upstream"}
27+
Toxiproxy->>+Server: toxiproxy_proxy_sent_bytes_total{direction="upstream"}
28+
Server->>+Toxiproxy: toxiproxy_proxy_received_bytes_total{direction="downstream"}
29+
Toxiproxy->>+Client: toxiproxy_proxy_sent_bytes_total{direction="downstream"}
30+
```
31+
32+
**Type**
33+
34+
Counter
35+
36+
**Labels**
37+
38+
| Label | Description | Example |
39+
|-----------|--------------------------------|-----------------------|
40+
| direction | Direction of the link | upstream / downstream |
41+
| listener | Listener address of this proxy | 0.0.0.0:8080 |
42+
| proxy | Proxy name | my-proxy |
43+
| upstream | Upstream address of this proxy | httpbin.org:80 |
44+

README.md

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,36 @@ stopping you from creating a client in any other language (see
4141

4242
## Table of Contents
4343

44-
1. [Why yet another chaotic TCP proxy?](#why-yet-another-chaotic-tcp-proxy)
45-
2. [Clients](#clients)
46-
3. [Example](#example)
47-
4. [Usage](#usage)
48-
1. [Installing](#1-installing-toxiproxy)
49-
1. [Upgrading from 1.x](#upgrading-from-toxiproxy-1x)
50-
2. [Populating](#2-populating-toxiproxy)
51-
3. [Using](#3-using-toxiproxy)
52-
4. [Logging](#4-logging)
53-
5. [Toxics](#toxics)
54-
1. [Latency](#latency)
55-
2. [Down](#down)
56-
3. [Bandwidth](#bandwidth)
57-
4. [Slow close](#slow_close)
58-
5. [Timeout](#timeout)
59-
6. [Reset peer](#reset_peer)
60-
7. [Slicer](#slicer)
61-
6. [HTTP API](#http-api)
62-
1. [Proxy fields](#proxy-fields)
63-
2. [Toxic fields](#toxic-fields)
64-
3. [Endpoints](#endpoints)
65-
4. [Populating Proxies](#populating-proxies)
66-
7. [CLI example](#cli-example)
67-
8. [FAQ](#frequently-asked-questions)
68-
9. [Development](#development)
44+
- [Toxiproxy](#toxiproxy)
45+
- [Table of Contents](#table-of-contents)
46+
- [Why yet another chaotic TCP proxy?](#why-yet-another-chaotic-tcp-proxy)
47+
- [Clients](#clients)
48+
- [Example](#example)
49+
- [Usage](#usage)
50+
- [1. Installing Toxiproxy](#1-installing-toxiproxy)
51+
- [Upgrading from Toxiproxy 1.x](#upgrading-from-toxiproxy-1x)
52+
- [2. Populating Toxiproxy](#2-populating-toxiproxy)
53+
- [3. Using Toxiproxy](#3-using-toxiproxy)
54+
- [4. Logging](#4-logging)
55+
- [Toxics](#toxics)
56+
- [latency](#latency)
57+
- [down](#down)
58+
- [bandwidth](#bandwidth)
59+
- [slow_close](#slow_close)
60+
- [timeout](#timeout)
61+
- [reset_peer](#reset_peer)
62+
- [slicer](#slicer)
63+
- [limit_data](#limit_data)
64+
- [HTTP API](#http-api)
65+
- [Proxy fields:](#proxy-fields)
66+
- [Toxic fields:](#toxic-fields)
67+
- [Endpoints](#endpoints)
68+
- [Populating Proxies](#populating-proxies)
69+
- [CLI Example](#cli-example)
70+
- [Metrics](#metrics)
71+
- [Frequently Asked Questions](#frequently-asked-questions)
72+
- [Development](#development)
73+
- [Release](#release)
6974

7075
## Why yet another chaotic TCP proxy?
7176

@@ -497,6 +502,7 @@ All endpoints are JSON.
497502
- **DELETE /proxies/{proxy}/toxics/{toxic}** - Remove an active toxic
498503
- **POST /reset** - Enable all proxies and remove all active toxics
499504
- **GET /version** - Returns the server version number
505+
- **GET /metrics** - Returns Prometheus-compatible metrics
500506

501507
#### Populating Proxies
502508

@@ -565,6 +571,11 @@ $ redis-cli -p 26379
565571
Could not connect to Redis at 127.0.0.1:26379: Connection refused
566572
```
567573

574+
### Metrics
575+
576+
Toxiproxy exposes Prometheus-compatible metrics via its HTTP API at /metrics.
577+
See [METRICS.md](./METRICS.md) for full descriptions
578+
568579
### Frequently Asked Questions
569580

570581
**How fast is Toxiproxy?** The speed of Toxiproxy depends largely on your hardware,

api.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ import (
1616

1717
type ApiServer struct {
1818
Collection *ProxyCollection
19+
Metrics *metricsContainer
1920
}
2021

21-
func NewServer() *ApiServer {
22+
func NewServer(m *metricsContainer) *ApiServer {
2223
return &ApiServer{
2324
Collection: NewProxyCollection(),
25+
Metrics: m,
2426
}
2527
}
2628

@@ -34,7 +36,7 @@ func (server *ApiServer) PopulateConfig(filename string) {
3436
return
3537
}
3638

37-
proxies, err := server.Collection.PopulateJson(file)
39+
proxies, err := server.Collection.PopulateJson(server, file)
3840
if err != nil {
3941
logrus.WithFields(logrus.Fields{
4042
"config": filename,
@@ -75,6 +77,10 @@ func (server *ApiServer) Listen(host string, port string) {
7577

7678
r.HandleFunc("/version", server.Version).Methods("GET")
7779

80+
if server.Metrics.anyMetricsEnabled() {
81+
r.Handle("/metrics", server.Metrics.handler())
82+
}
83+
7884
http.Handle("/", StopBrowsersMiddleware(r))
7985

8086
logrus.WithFields(logrus.Fields{
@@ -145,7 +151,7 @@ func (server *ApiServer) ProxyCreate(response http.ResponseWriter, request *http
145151
return
146152
}
147153

148-
proxy := NewProxy()
154+
proxy := NewProxy(server)
149155
proxy.Name = input.Name
150156
proxy.Listen = input.Listen
151157
proxy.Upstream = input.Upstream
@@ -169,7 +175,7 @@ func (server *ApiServer) ProxyCreate(response http.ResponseWriter, request *http
169175
}
170176

171177
func (server *ApiServer) Populate(response http.ResponseWriter, request *http.Request) {
172-
proxies, err := server.Collection.PopulateJson(request.Body)
178+
proxies, err := server.Collection.PopulateJson(server, request.Body)
173179

174180
apiErr, ok := err.(*ApiError)
175181
if !ok && err != nil {

api_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/Shopify/toxiproxy/v2"
1111
tclient "github.com/Shopify/toxiproxy/v2/client"
12+
"github.com/prometheus/client_golang/prometheus"
1213
)
1314

1415
var testServer *toxiproxy.ApiServer
@@ -19,7 +20,8 @@ func WithServer(t *testing.T, f func(string)) {
1920
// Make sure only one server is running at a time. Apparently there's no clean
2021
// way to shut it down between each test run.
2122
if testServer == nil {
22-
testServer = toxiproxy.NewServer()
23+
testServer = toxiproxy.NewServer(toxiproxy.NewMetricsContainer(prometheus.NewRegistry()))
24+
2325
go testServer.Listen("localhost", "8475")
2426

2527
// Allow server to start. There's no clean way to know when it listens.

cmd/server/server.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@ import (
1010
"syscall"
1111
"time"
1212

13+
"github.com/prometheus/client_golang/prometheus"
1314
"github.com/sirupsen/logrus"
1415

1516
"github.com/Shopify/toxiproxy/v2"
17+
"github.com/Shopify/toxiproxy/v2/collectors"
1618
)
1719

1820
type cliArguments struct {
19-
host string
20-
port string
21-
config string
22-
seed int64
23-
printVersion bool
21+
host string
22+
port string
23+
config string
24+
seed int64
25+
printVersion bool
26+
proxyMetrics bool
27+
runtimeMetrics bool
2428
}
2529

2630
func parseArguments() cliArguments {
@@ -33,6 +37,10 @@ func parseArguments() cliArguments {
3337
"JSON file containing proxies to create on startup")
3438
flag.Int64Var(&result.seed, "seed", time.Now().UTC().UnixNano(),
3539
"Seed for randomizing toxics with")
40+
flag.BoolVar(&result.runtimeMetrics, "runtime-metrics", false,
41+
`enable runtime-related prometheus metrics (default "false")`)
42+
flag.BoolVar(&result.proxyMetrics, "proxy-metrics", false,
43+
`enable toxiproxy-specific prometheus metrics (default "false")`)
3644
flag.BoolVar(&result.printVersion, "version", false,
3745
`print the version (default "false")`)
3846
flag.Parse()
@@ -63,7 +71,14 @@ func run(cli cliArguments) {
6371

6472
rand.Seed(cli.seed)
6573

66-
server := toxiproxy.NewServer()
74+
metrics := toxiproxy.NewMetricsContainer(prometheus.NewRegistry())
75+
server := toxiproxy.NewServer(metrics)
76+
if cli.proxyMetrics {
77+
server.Metrics.ProxyMetrics = collectors.NewProxyMetricCollectors()
78+
}
79+
if cli.runtimeMetrics {
80+
server.Metrics.RuntimeMetrics = collectors.NewRuntimeMetricCollectors()
81+
}
6782
if len(cli.config) > 0 {
6883
server.PopulateConfig(cli.config)
6984
}

collectors/common.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package collectors
2+
3+
const (
4+
namespace string = "toxiproxy"
5+
)

collectors/proxy.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package collectors
2+
3+
import (
4+
"github.com/prometheus/client_golang/prometheus"
5+
)
6+
7+
type ProxyMetricCollectors struct {
8+
collectors []prometheus.Collector
9+
proxyLabels []string
10+
11+
ReceivedBytesTotal *prometheus.CounterVec
12+
SentBytesTotal *prometheus.CounterVec
13+
}
14+
15+
func (c *ProxyMetricCollectors) Collectors() []prometheus.Collector {
16+
return c.collectors
17+
}
18+
19+
func NewProxyMetricCollectors() *ProxyMetricCollectors {
20+
var m ProxyMetricCollectors
21+
m.proxyLabels = []string{
22+
"direction",
23+
"proxy",
24+
"listener",
25+
"upstream",
26+
}
27+
m.ReceivedBytesTotal = prometheus.NewCounterVec(
28+
prometheus.CounterOpts{
29+
Namespace: namespace,
30+
Subsystem: "proxy",
31+
Name: "received_bytes_total",
32+
},
33+
m.proxyLabels)
34+
m.collectors = append(m.collectors, m.ReceivedBytesTotal)
35+
36+
m.SentBytesTotal = prometheus.NewCounterVec(
37+
prometheus.CounterOpts{
38+
Namespace: namespace,
39+
Subsystem: "proxy",
40+
Name: "sent_bytes_total",
41+
},
42+
m.proxyLabels)
43+
m.collectors = append(m.collectors, m.SentBytesTotal)
44+
45+
return &m
46+
}

collectors/runtime.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package collectors
2+
3+
import (
4+
"github.com/prometheus/client_golang/prometheus"
5+
"github.com/prometheus/client_golang/prometheus/collectors"
6+
)
7+
8+
type RuntimeMetricCollectors struct {
9+
collectors []prometheus.Collector
10+
}
11+
12+
func (c *RuntimeMetricCollectors) Collectors() []prometheus.Collector {
13+
return c.collectors
14+
}
15+
16+
func NewRuntimeMetricCollectors() *RuntimeMetricCollectors {
17+
var m RuntimeMetricCollectors
18+
m.collectors = append(m.collectors, collectors.NewGoCollector())
19+
m.collectors = append(m.collectors, collectors.NewBuildInfoCollector())
20+
m.collectors = append(m.collectors,
21+
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
22+
return &m
23+
}

go.mod

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,24 @@ go 1.17
44

55
require (
66
github.com/gorilla/mux v1.8.0
7+
github.com/prometheus/client_golang v1.12.1
78
github.com/sirupsen/logrus v1.8.1
89
github.com/urfave/cli/v2 v2.3.0
910
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
1011
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
1112
)
1213

1314
require (
15+
github.com/beorn7/perks v1.0.1 // indirect
16+
github.com/cespare/xxhash/v2 v2.1.2 // indirect
1417
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
18+
github.com/golang/protobuf v1.5.2 // indirect
19+
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
20+
github.com/prometheus/client_model v0.2.0 // indirect
21+
github.com/prometheus/common v0.32.1 // indirect
22+
github.com/prometheus/procfs v0.7.3 // indirect
1523
github.com/russross/blackfriday/v2 v2.0.1 // indirect
1624
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
17-
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
25+
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
26+
google.golang.org/protobuf v1.26.0 // indirect
1827
)

0 commit comments

Comments
 (0)