Skip to content

feat(analytics): split kci-dev, scripts, and bots from dashboard traffic - #2154

Merged
alanpeixinho merged 3 commits into
kernelci:mainfrom
profusion:feat/analytics-client-label
Oct 7, 2026
Merged

alanpeixinho merged 3 commits into
kernelci:mainfrom
profusion:feat/analytics-client-label

Conversation

@alanpeixinho

Copy link
Copy Markdown
Contributor

What it is

  • Classify User-Agents into client dashboard, kci-dev, script, or bot
  • Record kci-dev/<version> as the browser, and Linux, macOS, or Windows from (OS) in the User-Agent
  • Add a client label to request counts and daily unique visitors
  • Add a Client control on the Grafana API dashboard and filter the analytics panels with it
    New client series start at deploy. Older samples without that label stay in Prometheus until they age out of the selected range.
    Closes Count kci-dev as its own analytics client #2144

How to test

From backend/:

poetry run pytest -m unit kernelCI_app/tests/unitTests/middleware/backendRequestMetricsMiddleware_test.py -q
  1. docker compose -f docker-compose.monitoring.yml up -d
  2. Start the backend with PROMETHEUS_METRICS_ENABLED=true and metrics on port 8001. runserver needs --noreload.
  3. Send a few /api/ requests:
curl -H 'User-Agent: kci-dev/0.1.11 (Linux)' http://localhost:8000/api/schema/
curl -H 'User-Agent: curl/8.5.0' http://localhost:8000/api/schema/
curl -H 'User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1)' http://localhost:8000/api/schema/
curl -A 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/131.0.0.0 Safari/537.36' http://localhost:8000/api/schema/
  1. On http://localhost:8001/metrics/ confirm client="kci-dev", client="script", client="bot", and client="dashboard". The kci-dev request series should show browser="kci-dev/0.1.11", os="Linux",
    device="cli".
  2. In Grafana at http://localhost:3000, import monitoring/dashboard.json. Set the range to the last 15 minutes.
  3. Client: All clients shows one unique-visitor total and one slice per client on Requests by Client. Client: kci-dev limits the analytics panels to that client. Response time and total calls
    stay unchanged, because those series have no client label.

* Classify kci-dev, curl/wget/python-requests, and crawlers as their own client
* Keep the kci-dev version on the browser label and read OS from the User-Agent
* Add a client label to request and unique-visitor counters
* Filter the Grafana analytics panels with a Client dropdown

Part of kernelci#2144

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
browser=UNKNOWN,
os=UNKNOWN,
device=UNKNOWN,
client=Client.DASHBOARD,

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.

Empty User-Agent is counted as dashboard. Those requests are not from the web app, so they inflate the dashboard visitors.

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.

Empty User-Agent is now client=unknown, along with any User-Agent that is not a recognized browser, kci-dev, curl, wget, python-requests, or a bot.

alanpeixinho added a commit to profusion/dashboard that referenced this pull request Oct 2, 2026
* 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>
Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
@alanpeixinho

Copy link
Copy Markdown
Contributor Author

dashboard example

split-client-dashboard-web

kcidev example

split-client-dashboard-kcidev

all clients example

split-client-dashboard

if os_family:
os = KCI_DEV_OS.get(os_family.strip().casefold(), UNKNOWN)
return ClientInfo(
browser=f"kci-dev/{version}" if version else "kci-dev",

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.

I made a comment for this in another PR in the stack (#2156)

this would create a series for every release of kci-dev

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.

Changed to use a separate counter for browser, and the kci-dev version.

kci-dev-version-analytics

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.

Awesome to see kci-dev versions!

* Keep the browser label at kci-dev
* Count releases on dashboard_kci_dev_requests_by_version_total
* Treat a missing or non-release token as unknown
* Show versions on a donut filtered by the Client dropdown

Part of kernelci#2144

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
r"^kci-dev(?:/([^\s/()]+))?(?:\s+\(([^)]+)\))?\s*$",
re.IGNORECASE,
)
KCI_DEV_VERSION = re.compile(r"^\d+\.\d+\.\d+(?:[a-z0-9.]{0,16})?$", re.IGNORECASE)

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.

just a note: I see this matches all the kci-dev versions on https://pypi.org/project/kci-dev/#history

but it is easy to have problems with any rc or non stable versions. eg. 0.1.3.dev1 matches with the regex. But 0.1.3.dev-1 does not. Depends on how the kci-dev decides its version labels

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.

nice catch

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.

kci-dev sends the PEP 440 version from Poetry. 0.1.3.dev1 already matches. 0.1.3.dev-1 is not valid PEP 440, so it stays unknown.

alanpeixinho added a commit to profusion/dashboard that referenced this pull request Oct 6, 2026
* 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>
@alanpeixinho
alanpeixinho added this pull request to the merge queue Oct 7, 2026
Merged via the queue into kernelci:main with commit a68436c Oct 7, 2026
7 checks passed
alanpeixinho added a commit to profusion/dashboard that referenced this pull request Oct 7, 2026
* 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>
alanpeixinho added a commit to profusion/dashboard that referenced this pull request Oct 8, 2026
…2156)

* feat(analytics): record API latency by endpoint and client

* 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>

* feat(analytics): show 4xx and 5xx by endpoint

* 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>

* test(analytics): stop latency tests clobbering the Django histogram

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>

* fix(analytics): sort request metrics middleware imports

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>

* fix(analytics): split API latency by HTTP method

GET and POST to the same endpoint were recorded as one series.

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

* fix(analytics): keep latency on the response and split methods

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>

---------

Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
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.

Count kci-dev as its own analytics client

3 participants