Repository navigation
feat(analytics): record API latency by endpoint and client - #2156
Conversation
49ffcb5 to
c9ba9d2
Compare
803aa9a to
3e6cda3
Compare
* Time /api/ requests on dashboard_backend_request_latency_seconds by endpoint and client * Keep /admin/ and /health/ on the Django view-latency histogram * Point total calls, average, p50, and p95 at those client series Built on kernelci#2154. Closes kernelci#2149 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
* Add a panel summed by endpoint and status_class, limited to 4xx and 5xx Part of kernelci#2150 Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
The latency tests replaced the process-wide Prometheus histogram and left it replaced, so the health-skip test saw no /api/ observation once both suites shared a worker. Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
Ruff I001 failed pre-commit because prometheus_client sat after the first-party import. Assisted-by: Cursor Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
GET and POST to the same endpoint were recorded as one series. Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
0dc78b3 to
410a7a7
Compare
| }, | ||
| "editorMode": "code", | ||
| "expr": "(sum(django_http_requests_latency_seconds_by_view_method_count{view!=\"<unnamed view>\"}) by (view, method) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"all\", \"\", \"\") or round(sum(increase(django_http_requests_latency_seconds_by_view_method_count{view!=\"<unnamed view>\"}[$__range:])) by (view, method)) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"range\", \"\", \"\"))", | ||
| "expr": "(sum by (endpoint, client) (dashboard_backend_requests_by_client_total{client=~\"$client\"}) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"all\", \"\", \"\") or round(sum by (endpoint, client) (increase(dashboard_backend_requests_by_client_total{client=~\"$client\"}[$__range:]))) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"range\", \"\", \"\"))", |
There was a problem hiding this comment.
This counter has been scraped since #2154. The histogram sum below starts when this deploys, and the table divides that time by these calls. All Time average stays low, because earlier calls are in this count and the histogram has no time for them. A fresh local Prometheus will not show that.
Could this column use dashboard_backend_request_latency_seconds_count, with the same grouping and the same all/range switch? Total Calls can keep this counter.
There was a problem hiding this comment.
#2154 is on main, but it is not in production, so dashboard_backend_requests_by_client_total is not being scraped there yet. If we merge this PR, both should reach production together.
| }, | ||
| "editorMode": "code", | ||
| "expr": "(sum(django_http_requests_latency_seconds_by_view_method_sum{view!=\"<unnamed view>\"}) by (view, method) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"all\", \"\", \"\") or sum(increase(django_http_requests_latency_seconds_by_view_method_sum{view!=\"<unnamed view>\"}[$__range:])) by (view, method) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"range\", \"\", \"\"))", | ||
| "expr": "(sum by (endpoint, client) (dashboard_backend_request_latency_seconds_sum{client=~\"$client\"}) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"all\", \"\", \"\") or sum by (endpoint, client) (increase(dashboard_backend_request_latency_seconds_sum{client=~\"$client\"}[$__range:])) and on() label_replace(vector(1), \"mode\", \"$summary_mode\", \"\", \"\")==label_replace(vector(1), \"mode\", \"range\", \"\", \"\"))", |
There was a problem hiding this comment.
This groups only by endpoint and client, so GET and POST share one average. Total Calls above does the same. The latency chart keeps method. Add method to both sums if the table should keep them apart.
There was a problem hiding this comment.
Yes. Total Calls and both summary sums now group by endpoint, method, and client, same as the latency chart. The summary table groups on Method as well, so Grafana does not add GET and POST back into one row.
| endpoint=labels["endpoint"], | ||
| method=labels["method"], | ||
| client=labels["client"], | ||
| ).observe(time.perf_counter() - started) |
There was a problem hiding this comment.
This sample includes the labeling and the counter update above. The visitor cache write below stays outside it. Save the elapsed time right after get_response returns, and pass that into observe, so the labeling stays out of the histogram.
There was a problem hiding this comment.
Changed to make time measurement only on the response generation.
Stop the histogram sample when the response returns, and group the dashboard sums by method so GET and POST stay apart. Assisted-by: Cursor Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
felipebergamin
left a comment
There was a problem hiding this comment.
approving. but please take a look on my comment about the docs/monitoring.md file
There was a problem hiding this comment.
just a small update on this doc.
it still describes the dashboard from before the method split. The feature list dropped Method, and the Client section says Total Calls is summed by endpoint and client only. The queries also group by method. The same feature list is not mentioning 4xx and 5xx by Endpoint.
What it is
/api/request ondashboard_backend_request_latency_seconds, labeled by endpoint and client./admin/and/health/stay on it. Django status and exception counters stay as they are.dashboard_backend_requests_by_client_total, summed by endpoint and client. Point Average Response Time by Endpoint at the histogram sum divided by count, and add p50 and p95 from its buckets. Endpoint Performance Summary uses the counter for calls and the histogram for total time and average.The existing Client dropdown filters those panels. New series start at deploy. API views already on the Django histogram stop receiving samples.
Built on #2154. Merge that first.
Closes #2149
Closes #2150
How to test
From
backend/:docker compose -f docker-compose.monitoring.yml up -dPROMETHEUS_METRICS_ENABLED=trueand metrics on port 8001.runserverneeds--noreload.client="kci-dev",client="script", andclient="dashboard"on bothdashboard_backend_requests_by_client_totalanddashboard_backend_request_latency_seconds_count, for endpointschema.django_http_requests_latency_seconds_by_view_method_countmust not haveview="schema". It must haveadmin:indexoradmin:login. The hardware request isendpoint="hardware",status_class="4xx".http://prometheus:9090and its uid must beprometheus. Importmonitoring/dashboard.json. Set the range to the last 15 minutes.schemasplit bydashboard,kci-dev, andscript. Average Response Time shows avg, p50, and p95 for those series after Prometheus has scraped twice. Client set to kci-dev leaves only that client. Summary Mode set to All Time shows the totals.hardware - 4xx. It does not showschema.