Skip to content

Commit 39d64d5

Browse files
committed
Monitoring with Prometheus - Instrument Project API
1 parent b696eb9 commit 39d64d5

File tree

11 files changed

+1004
-4
lines changed

11 files changed

+1004
-4
lines changed

packages/ml_api/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ run-service-development:
2525
@echo "+ $@"
2626
python run.py
2727

28+
run-service-wsgi:
29+
@echo "+ $@"
30+
gunicorn --workers=1 --bind 0.0.0.0:5000 run:application
31+
2832
db-migrations:
2933
@echo "+ $@"
3034
PYTHONPATH=. alembic -c alembic.ini upgrade head

packages/ml_api/api/app.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from sqlalchemy.orm import scoped_session
55

66
from api.config import Config
7+
from api.monitoring.middleware import setup_metrics
78
from api.persistence.core import init_database
89

910
_logger = logging.getLogger(__name__)
@@ -23,6 +24,9 @@ def create_app(
2324
# Setup database
2425
init_database(flask_app, config=config_object, db_session=db_session)
2526

27+
# Setup prometheus monitoring
28+
setup_metrics(flask_app)
29+
2630
connexion_app.add_api("api.yaml")
2731
_logger.info("Application instance created")
2832

packages/ml_api/api/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
# Project Directories
1515
ROOT = pathlib.Path(api.__file__).resolve().parent.parent
1616

17+
APP_NAME = 'ml_api'
18+
1719

1820
class Config:
1921
DEBUG = False

packages/ml_api/api/monitoring/__init__.py

Whitespace-only changes.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from flask import request, Flask
2+
from flask.wrappers import Response
3+
from prometheus_client import Counter, Histogram
4+
import time
5+
6+
from api.config import APP_NAME
7+
8+
9+
# Counter and Histogram are examples of default metrics
10+
# available from the prometheus Python client.
11+
REQUEST_COUNT = Counter(
12+
name='http_request_count',
13+
documentation='App Request Count',
14+
labelnames=['app_name', 'method', 'endpoint', 'http_status']
15+
)
16+
REQUEST_LATENCY = Histogram(
17+
name='http_request_latency_seconds',
18+
documentation='Request latency',
19+
labelnames=['app_name', 'endpoint']
20+
)
21+
22+
23+
def start_timer() -> None:
24+
"""Get start time of a request."""
25+
request._prometheus_metrics_request_start_time = time.time()
26+
27+
28+
def stop_timer(response: Response) -> Response:
29+
"""Get stop time of a request.."""
30+
request_latency = time.time() - request._prometheus_metrics_request_start_time
31+
REQUEST_LATENCY.labels(
32+
app_name=APP_NAME,
33+
endpoint=request.path).observe(request_latency)
34+
return response
35+
36+
37+
def record_request_data(response: Response) -> Response:
38+
"""Capture request data.
39+
40+
Uses the flask request object to extract information such as
41+
the HTTP request method, endpoint and HTTP status.
42+
"""
43+
REQUEST_COUNT.labels(
44+
app_name=APP_NAME,
45+
method=request.method,
46+
endpoint=request.path,
47+
http_status=response.status_code).inc()
48+
return response
49+
50+
51+
def setup_metrics(app: Flask) -> None:
52+
"""Setup Prometheus metrics.
53+
54+
This function uses the flask before_request
55+
and after_request hooks to capture metrics
56+
with each HTTP request to the application.
57+
"""
58+
app.before_request(start_timer)
59+
app.after_request(record_request_data)
60+
app.after_request(stop_timer)

0 commit comments

Comments
 (0)