membermatters / MemberMatters

An open source membership, access and payments portal for makerspaces and community groups.
https://membermatters.org
MIT License
40 stars 23 forks source link

Add support for Prometheus metrics endpoint #187

Closed proffalken closed 1 year ago

proffalken commented 1 year ago

This PR adds in a /metrics endpoint to the back-end, giving us an insight into how well the application is performing via https://github.com/korfuri/django-prometheus

The initial code in this PR only enables the basic metrics, however it is possible to instrument the database and cache connections, as well as showing how often models are created/updated/deleted etc. over time.

The same could be achieved for the frontend in future using something like https://github.com/siimon/prom-client

proffalken commented 1 year ago

Example output:

# HELP django_migrations_unapplied_total Count of unapplied migrations by database connection
# TYPE django_migrations_unapplied_total gauge
django_migrations_unapplied_total{connection="default"} 0.0
# HELP django_migrations_applied_total Count of applied migrations by database connection
# TYPE django_migrations_applied_total gauge
django_migrations_applied_total{connection="default"} 66.0
# HELP django_http_requests_before_middlewares_total Total count of requests before middlewares run.
# TYPE django_http_requests_before_middlewares_total counter
django_http_requests_before_middlewares_total 24.0
# HELP django_http_requests_before_middlewares_created Total count of requests before middlewares run.
# TYPE django_http_requests_before_middlewares_created gauge
django_http_requests_before_middlewares_created 1.6617506666484659e+09
# HELP django_http_responses_before_middlewares_total Total count of responses before middlewares run.
# TYPE django_http_responses_before_middlewares_total counter
django_http_responses_before_middlewares_total 23.0
# HELP django_http_responses_before_middlewares_created Total count of responses before middlewares run.
# TYPE django_http_responses_before_middlewares_created gauge
django_http_responses_before_middlewares_created 1.661750666648488e+09
# HELP django_http_requests_latency_including_middlewares_seconds Histogram of requests processing time (including middleware processing time).
# TYPE django_http_requests_latency_including_middlewares_seconds histogram
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.005"} 0.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.01"} 6.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.025"} 20.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.05"} 20.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.075"} 20.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.1"} 21.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.25"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.5"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="0.75"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="1.0"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="2.5"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="5.0"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="7.5"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="10.0"} 23.0
django_http_requests_latency_including_middlewares_seconds_bucket{le="+Inf"} 23.0
django_http_requests_latency_including_middlewares_seconds_count 23.0
django_http_requests_latency_including_middlewares_seconds_sum 0.7557479902170599
# HELP django_http_requests_latency_including_middlewares_seconds_created Histogram of requests processing time (including middleware processing time).
# TYPE django_http_requests_latency_including_middlewares_seconds_created gauge
django_http_requests_latency_including_middlewares_seconds_created 1.66175066664851e+09
# HELP django_http_requests_unknown_latency_including_middlewares_total Count of requests for which the latency was unknown (when computing django_http_requests_latency_including_middlewares_seconds).
# TYPE django_http_requests_unknown_latency_including_middlewares_total counter
django_http_requests_unknown_latency_including_middlewares_total 0.0
# HELP django_http_requests_unknown_latency_including_middlewares_created Count of requests for which the latency was unknown (when computing django_http_requests_latency_including_middlewares_seconds).
# TYPE django_http_requests_unknown_latency_including_middlewares_created gauge
django_http_requests_unknown_latency_including_middlewares_created 1.661750666648552e+09
# HELP django_http_requests_latency_seconds_by_view_method Histogram of request processing time labelled by view.
# TYPE django_http_requests_latency_seconds_by_view_method histogram
django_http_requests_latency_seconds_by_view_method_bucket{le="0.01",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.025",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.05",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.075",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.1",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.25",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.5",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="0.75",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="1.0",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="2.5",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="5.0",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="7.5",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="10.0",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="25.0",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="50.0",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="75.0",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_bucket{le="+Inf",method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_count{method="GET",view="<unnamed view>"} 2.0
django_http_requests_latency_seconds_by_view_method_sum{method="GET",view="<unnamed view>"} 0.015977503964677453
jabelone commented 1 year ago

Nice, thanks for this!

p.s. normally I'd prefer PRs go to the dev branch rather than main, but as this is a relatively minor change I've merged it straight in and will just update dev. I'll also update the contributing guidelines to make this more clear.

jabelone commented 1 year ago

I also just added django-prometheus to the requirements.txt file as it's now a dependency :)

proffalken commented 1 year ago

Amazing, thanks.

Good catch on the requirements.txt, I've moved most of my projects over to poetry so a poetry add updates the lock file as well - I'm out of practice with virtualenvs, but I'll keep it in mind for the future. Same goes for the dev branch :)