Skip to content

feat(analytics): record view time per dashboard path - #2218

Open
alanpeixinho wants to merge 3 commits into
kernelci:mainfrom
profusion:feat/2153/route-visible-time
Open

alanpeixinho wants to merge 3 commits into
kernelci:mainfrom
profusion:feat/2153/route-visible-time

Conversation

@alanpeixinho

Copy link
Copy Markdown
Contributor

What it is

Records how long a dashboard URL stays in the foreground. A visit ends when the path changes or the tab is hidden. Showing the tab again starts a new visit. Changing only the query string does not end the visit.

The browser sends one POST to /api/analytics/visible/ when the visit ends. The JSON body is route and time_ms. route is the URL path, such as /tree/mainline/master. The handler divides time_ms by 1000 and observes dashboard_route_visible_seconds. Buckets are 1, 5, 15, 30, 60, 120, 300, 600, 1800, and +Inf. A stay longer than 30 minutes lands in +Inf.

A path that matches no dashboard route, or a time_ms outside 0 to 86400000, returns 400 and is not recorded. The /$ not-found route is rejected because it would match every path. This POST does not increment the client-request counter, the latency histogram, or the unique-visitor count. A visit that never closes is missing.

route is the full path, so each commit, build, or test id is its own series. Bounding that label is left for a follow-up.

monitoring/dashboard.json is now the dashboard app board from mon.kernelci.org, with a View time by route table added.

Part of #2153

How to test

  • cd dashboard && pnpm exec vitest run src/utils/visibleTime.test.ts
  • cd backend && poetry run pytest kernelCI_app/tests/unitTests/views/visibleTimeView_test.py kernelCI_app/tests/unitTests/middleware/backendRequestMetricsMiddleware_test.py

With the app running and PROMETHEUS_METRICS_ENABLED=true:

  • Open /tree/mainline/master, wait a few seconds, then open another path or hide the tab. The network panel shows one POST to /api/analytics/visible/ with that path and time_ms. Changing only the search string sends nothing. A stay under one second still sends.
  • curl -s localhost:8001/metrics/ | grep dashboard_route_visible_seconds shows a count for that path. A POST with {"route":"/nope","time_ms":1000} returns 400 and adds no series.
  • Import monitoring/dashboard.json. The View time by route table lists the path over the selected range, including a path visited once.

Made with Cursor

* Report foreground time for each URL path.
* End a visit when the path changes or the tab is hidden.
* Ignore paths that match no dashboard route.
* Keep this request out of the client, latency, and visitor metrics.

Part of kernelci#2153

Assisted-by: Grok 4.7
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
* Use the dashboard app board from mon.kernelci.org.
* Add a View time by route table.

Assisted-by: Grok 4.7
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>

@felipebergamin felipebergamin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments are on the changed lines.

if not body.is_valid() or not is_dashboard_path(body.validated_data["route"]):
return Response(status=HTTPStatus.BAD_REQUEST)
route = body.validated_data["route"]
get_metrics().route_visible_seconds.labels(route=route).observe(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

route is the concrete path, this view sets authentication_classes = [], and settings has no DRF throttle. Each /build/<id> and /test/<id> adds histogram series, and the multiprocess files keep them until the process restarts. Observe the matched route pattern, for example /build/$buildId, instead of the path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label keeps the tree name, branch, and hardware id, and stores a build, test, boot, issue, or commit id as its placeholder. /build/maestro:abc and /build/maestro:def share /build/$buildId.

Replacing every segment with the route pattern would drop which tree or board was open. The view still accepts the posted path and maps it before observing.

Comment thread monitoring/dashboard.json Outdated
},
"enable": true,
"expr": "max by (version) (dashboard_build_info{job=\"dashboard_metrics\"}) unless max by (version) (dashboard_build_info{job=\"dashboard_metrics\"} offset 5m)",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every query in this file filters job="dashboard_metrics". monitoring/prometheus.yml scrapes job_name: kernelci-backend, so this dashboard is empty on the stack in docs/monitoring.md. The previous file did not filter by job. Use a Grafana variable for the job, with the default set to the job this repo scrapes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept the filter hardcoded and set it to kernelci-backend, the job in monitoring/prometheus.yml.

docs/monitoring.md imports this file, and that scrape does not use dashboard_metrics, so the panels were empty. A $job variable would also serve mon.kernelci.org. Hardcoding the job this repo scrapes is the smaller change.

Comment thread PRIVACY.md
`direct_or_internal`. The full referring URL is never stored.
- **Unique visitor estimates**: daily de-duplicated visit counts, in total and
per endpoint.
- **View time**: while you use the dashboard in a browser, the page reports

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A path that only one person opens, such as /build/<id>, plus the scrape timestamp, identifies that visit and when it happened. Line 21 says metrics are never per-user records or per-user timestamps. Recording the route pattern instead of the path fixes that. This bullet should describe the pattern, not the concrete path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A build, test, boot, issue, or commit id is stored as its placeholder, so /build/<id> is no longer its own series. The bullet says that.

Tree name, branch, and hardware id stay in the label, so those pages can still be ranked. The bullet names those too, for example /tree/mainline/master.

* Keep tree, branch, and hardware in the view-time label and collapse object ids
* Point the Grafana board at kernelci-backend
* Describe the stored route in PRIVACY.md and the monitoring docs

Assisted-by: Grok 4.7
Part of kernelci#2153.

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants