diff --git a/README.md b/README.md index 912c1ed..10bca4d 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ The following default metrics are exposed: - `flagsmith_build_info`: Has the labels `version` and `ci_commit_sha`. - `flagsmith_http_server_request_duration_seconds`: Histogram labeled with `method`, `route`, and `response_status`. - `flagsmith_http_server_requests_total`: Counter labeled with `method`, `route`, and `response_status`. +- `flagsmith_http_server_response_size_bytes`:Histogram labeled with `method`, `route`, and `response_status`. - `flagsmith_task_processor_enqueued_tasks_total`: Counter labeled with `task_identifier`. ##### Task Processor metrics diff --git a/src/common/gunicorn/constants.py b/src/common/gunicorn/constants.py index 23a099a..dfca6cc 100644 --- a/src/common/gunicorn/constants.py +++ b/src/common/gunicorn/constants.py @@ -1 +1,12 @@ WSGI_DJANGO_ROUTE_ENVIRON_KEY = "wsgi.django_route" +HTTP_SERVER_RESPONSE_SIZE_DEFAULT_BUCKETS = ( + # 1 kB, 10 kB, 100 kB, 500 kB, 1 MB, 5 MB, 10 MB + 1 * 1024, + 10 * 1024, + 100 * 1024, + 500 * 1024, + 1 * 1024 * 1024, + 5 * 1024 * 1024, + 10 * 1024 * 1024, + float("inf"), +) diff --git a/src/common/gunicorn/logging.py b/src/common/gunicorn/logging.py index 08a5d29..c68dff6 100644 --- a/src/common/gunicorn/logging.py +++ b/src/common/gunicorn/logging.py @@ -64,6 +64,9 @@ def access( duration_seconds ) metrics.flagsmith_http_server_requests_total.labels(**labels).inc() + metrics.flagsmith_http_server_response_size_bytes.labels(**labels).observe( + resp.response_length or 0, + ) class GunicornJsonCapableLogger(PrometheusGunicornLogger): diff --git a/src/common/gunicorn/metrics.py b/src/common/gunicorn/metrics.py index 1f541b9..071c730 100644 --- a/src/common/gunicorn/metrics.py +++ b/src/common/gunicorn/metrics.py @@ -1,5 +1,7 @@ import prometheus_client +from django.conf import settings +from common.gunicorn.constants import HTTP_SERVER_RESPONSE_SIZE_DEFAULT_BUCKETS from common.prometheus import Histogram flagsmith_http_server_requests_total = prometheus_client.Counter( @@ -12,3 +14,13 @@ "HTTP request duration in seconds", ["route", "method", "response_status"], ) +flagsmith_http_server_response_size_bytes = Histogram( + "flagsmith_http_server_response_size_bytes", + "HTTP response size in bytes", + ["route", "method", "response_status"], + buckets=getattr( + settings, + "PROMETHEUS_HTTP_SERVER_RESPONSE_SIZE_HISTOGRAM_BUCKETS", + HTTP_SERVER_RESPONSE_SIZE_DEFAULT_BUCKETS, + ), +) diff --git a/tests/unit/common/gunicorn/test_logging.py b/tests/unit/common/gunicorn/test_logging.py index c0ceccf..efc873b 100644 --- a/tests/unit/common/gunicorn/test_logging.py +++ b/tests/unit/common/gunicorn/test_logging.py @@ -1,5 +1,4 @@ import logging -import logging.config import os from datetime import datetime, timedelta @@ -88,12 +87,13 @@ def test_gunicorn_prometheus_gunicorn_logger__expected_metrics( response_mock = mocker.Mock() response_mock.status = b"200 OK" response_mock.status_code = 200 + response_mock.response_length = 42 # When logger.access( response_mock, mocker.Mock(), - {"wsgi.django_route": "^health", "REQUEST_METHOD": "GET"}, + {"wsgi.django_route": "/health", "REQUEST_METHOD": "GET"}, timedelta(milliseconds=101), ) @@ -101,12 +101,17 @@ def test_gunicorn_prometheus_gunicorn_logger__expected_metrics( assert_metric( name="flagsmith_http_server_requests_total", value=1.0, - labels={"method": "GET", "route": "^health", "response_status": "200"}, + labels={"method": "GET", "route": "/health", "response_status": "200"}, ) assert_metric( name="flagsmith_http_server_request_duration_seconds_sum", value=0.101, - labels={"method": "GET", "route": "^health", "response_status": "200"}, + labels={"method": "GET", "route": "/health", "response_status": "200"}, + ) + assert_metric( + name="flagsmith_http_server_response_size_bytes_sum", + value=42.0, + labels={"method": "GET", "route": "/health", "response_status": "200"}, )