Skip to content

Commit

Permalink
Example metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
rycus86 committed May 5, 2018
1 parent d829189 commit ee2fa79
Show file tree
Hide file tree
Showing 15 changed files with 1,626 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ info = metrics.info('dynamic_info', 'Something dynamic')
info.set(42.1)
```

## Examples

See some simple examples visualized on a Grafana dashboard by running
the demo in the [examples/sample-signals](https://github.com/rycus86/prometheus_flask_exporter/tree/master/examples/wsgi) folder.

![Example dashboard](examples/sample-signals/dashboard.png)

## Debug mode

Please note, that changes being live-reloaded, when running the Flask
Expand Down
125 changes: 125 additions & 0 deletions examples/sample-signals/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Example dashboard

![Example dashboard](dashboard.png)

With this example, you can bring up a simple Flask application that has 4 endpoints, plus one that always returns an error response. The 4 main endpoints simply return `ok` after some random delay.

The example Compose project also includes a random load generator, a Prometheus server, and a Grafana instance with preconfigured dashboards, feeding data from Prometheus, which is also configured to scrape metrics from the example application automatically. All you need to do is:

```shell
$ docker-compose up -d
```

Then open [http://localhost:3000/d/_eX4mpl3](http://localhost:3000/d/_eX4mpl3) in your browser to see the dashboard. You can edit each panel to check what metric it uses, but here is a quick rundown of what's going on in there.

### Requests per second

Number of successful Flask requests per second. Shown per path.

```
rate(
flask_http_request_duration_seconds_count{status="200"}[30s]
)
```

### Errors per second

Number of failed (non HTTP 200) responses per second.

```
sum(
rate(
flask_http_request_duration_seconds_count{status!="200"}[30s]
)
)
```

### Total requests per minute

The total number of requests measured over one minute intervals. Shown per HTTP response status code.

```
increase(
flask_http_request_total[1m]
)
```

### Average response time [30s]

The average response time measured over 30 seconds intervals for successful requests. Shown per path.

```
rate(
flask_http_request_duration_seconds_sum{status="200"}[30s]
)
/
rate(
flask_http_request_duration_seconds_count{status="200"}[30s]
)
```

### Requests under 250ms

The percentage of successful requests finished within 1/4 second. Shown per path.

```
increase(
flask_http_request_duration_seconds_bucket{status="200",le="0.25"}[30s]
)
/ ignoring (le)
increase(
flask_http_request_duration_seconds_count{status="200"}[30s]
)
```

### Request duration [s] - p50

The 50th percentile of request durations over the last 30 seconds. In other words, half of the requests finish in (min/max/avg) these times. Shown per path.

```
histogram_quantile(
0.5,
rate(
flask_http_request_duration_seconds_bucket{status="200"}[30s]
)
)
```

### Request duration [s] - p90

The 90th percentile of request durations over the last 30 seconds. In other words, 90 percent of the requests finish in (min/max/avg) these times. Shown per path.

```
histogram_quantile(
0.9,
rate(
flask_http_request_duration_seconds_bucket{status="200"}[30s]
)
)
```

### Memory usage

The memory usage of the Flask app. Based on data from the underlying Prometheus client library, not Flask specific.

```
process_resident_memory_bytes{job="example"}
```

### CPU usage

The CPU usage of the Flask app as measured over 30 seconds intervals. Based on data from the underlying Prometheus client library, not Flask specific.

```
rate(
process_cpu_seconds_total{job="example"}[30s]
)
```

## Cleaning up

Don't forget to shut the demo down, once finished:

```shell
$ docker-compose down -v
```
9 changes: 9 additions & 0 deletions examples/sample-signals/app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3-alpine

ADD requirements.txt /tmp/requirements.txt

RUN pip install -r /tmp/requirements.txt

ADD app.py /var/server/app.py

CMD python /var/server/app.py
45 changes: 45 additions & 0 deletions examples/sample-signals/app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import time
import random

from flask import Flask
from prometheus_flask_exporter import PrometheusMetrics


app = Flask(__name__)
PrometheusMetrics(app)


endpoints = ('one', 'two', 'three', 'four', 'five', 'error')


@app.route('/one')
def first_route():
time.sleep(random.random() * 0.2)
return 'ok'


@app.route('/two')
def the_second():
time.sleep(random.random() * 0.4)
return 'ok'


@app.route('/three')
def test_3rd():
time.sleep(random.random() * 0.6)
return 'ok'


@app.route('/four')
def fourth_one():
time.sleep(random.random() * 0.8)
return 'ok'


@app.route('/error')
def oops():
return ':(', 500


if __name__ == '__main__':
app.run('0.0.0.0', 5000, threaded=True)
2 changes: 2 additions & 0 deletions examples/sample-signals/app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask
prometheus_flask_exporter
Binary file added examples/sample-signals/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions examples/sample-signals/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
version: '2'
services:

# a sample app with metrics enabled
app:
build:
context: app
stop_signal: SIGKILL
ports:
- 5000:5000

# dumb, random load generator
generator:
build:
context: generator
stop_signal: SIGKILL

# the Prometheus server
prometheus:
image: prom/prometheus:v2.2.1
volumes:
- ./prometheus/config.yml:/etc/prometheus/prometheus.yml
ports:
- 9090:9090

# Grafana for visualization
grafana:
image: grafana/grafana:5.1.0
volumes:
- ./grafana/config.ini:/etc/grafana/grafana.ini
- ./grafana/datasource.yaml:/etc/grafana/provisioning/datasources/default.yaml
- ./grafana/dashboard.yaml:/etc/grafana/provisioning/dashboards/default.yaml
- ./grafana/dashboards:/var/lib/grafana/dashboards
ports:
- 3000:3000

9 changes: 9 additions & 0 deletions examples/sample-signals/generator/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3-alpine

ADD requirements.txt /tmp/requirements.txt

RUN pip install -r /tmp/requirements.txt

ADD generate_events.py /var/app/generator.py

CMD python /var/app/generator.py
27 changes: 27 additions & 0 deletions examples/sample-signals/generator/generate_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import time
import random
import threading

import requests

endpoints = ('one', 'two', 'three', 'four', 'error')


def run():
while True:
try:
target = random.choice(endpoints)
requests.get("http://app:5000/%s" % target, timeout=1)

except:
pass


if __name__ == '__main__':
for _ in range(4):
thread = threading.Thread(target=run)
thread.setDaemon(True)
thread.start()

while True:
time.sleep(1)
1 change: 1 addition & 0 deletions examples/sample-signals/generator/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
requests
Loading

0 comments on commit ee2fa79

Please sign in to comment.