Skip to main content

Investigate: "Active Clients" dashboard panel for the operator persona

A dashboard panel showing how many distinct frontends/clients (via client_name) are actively logging across the fleet — for the operator persona managing all APIs and all registered clients, distinct from a single API's own maintainer. First raised right after client_name shipped in PLAN-context-propagation.md; this investigation replaces an earlier informal spot-check in 1PRIORITY.md's Tier 5 notes that turned out to be incomplete — re-verifying it against realistic data found two real bugs the original check missed entirely.

IMPLEMENTATION RULES: Before implementing this plan, read and follow:

Status: Backlog​

Goal: A correct, dashboard-ready LogQL query and panel definition for "how many distinct clients are actively logging," validated against realistic data (present and absent client_name), not just a clean happy-path case.

Last Updated: 2026-07-13

Why this needed re-investigating, not just re-confirming: the original check (noted in 1PRIORITY.md's Tier 5) tested count(count by (client_name) (count_over_time({service_name=~".+"}[10m]))) against test data where every single log line had client_name set, and called it "confirmed feasible." That's not what real data looks like — client_name is optional, and most log lines (system/internal logging, services that never call sovdev_set_context(), Python's E2E test which doesn't support the field at all yet) never set it. Once tested against this session's actual accumulated UIS data — a realistic mix of tagged and untagged log lines — the same query returned 2 instead of the correct 1. A validation that only holds on cherry-picked data isn't a validation.


What was actually tested (all against real, live UIS data — not assumed, not mocked)​

Bug 1: naive count treats "no client set" as a distinct client​

count(count by (client_name) (count_over_time({service_name=~".+"}[6h]))) against this session's real mixed data (21,648 log lines with no client_name, 119 with client_name="company-lookup-e2e-client") returns:

[{"metric": {}, "value": [..., "2"]}]

Wrong — there is exactly one distinct client, not two. The inner count by (client_name) produces a series for the empty-label group (client_name absent) and a series for client_name="company-lookup-e2e-client"; the outer count() counts both, treating "no client" as if it were itself a client.

Fix, verified: filter out the empty-label case before aggregating:

count(count by (client_name) (count_over_time({service_name=~".+"} | client_name != "" [6h])))

Re-tested against the same real data: returns 1 — correct.

Bug 2: zero real clients returns empty, not 0​

Tested against sovdev-test-company-lookup-python specifically — a real service that genuinely never sets client_name (Python doesn't support the field yet, confirmed elsewhere in this repo's compare-log-files.py exclusions). With Bug 1's fix applied:

count(count by (client_name) (count_over_time({service_name="sovdev-test-company-lookup-python"} | client_name != "" [6h])))

returns "result": [] — an empty result vector, not 0. In a Grafana stat panel, this renders as "No data" (a blank/grey state), not a clean 0 — misleading for an operator persona, since "zero clients logging right now" is a completely normal, common state for many services (most don't use client_name at all), not an error condition or a broken panel.

Fix, verified: append or vector(0) to force a 0 when the inner aggregation produces no series:

count(count by (client_name) (count_over_time({service_name="sovdev-test-company-lookup-python"} | client_name != "" [6h]))) or vector(0)

Re-tested: returns 0 — correct, and re-confirmed the fleet-wide non-zero case (Bug 1's fix) still correctly returns 1 with the same or vector(0) suffix added — the fallback doesn't corrupt the real-data case.

Final query, both fixes composed and both cases re-verified together​

count(count by (client_name) (count_over_time({service_name=~"$service_name"} | client_name != "" [$__range]))) or vector(0)

($service_name and $__range are the dashboard's own existing template variable and time-picker range — not yet substituted/tested through an actual live Grafana panel render, see [Q1].)

Panel shape, confirmed by reading the real existing panel, not guessed​

tools/dashboards/sovdev-logger-overview.json's "Active Integrations" panel (id 1) is a stat panel: count(count by (service_name) (sovdev_operations_total{service_name=~"$service_name"})) on the prometheus datasource. A client_name version mirrors this exactly — same panel type, same $service_name template variable (already multi: true, includeAll: true, defaulting to fleet-wide .+ — confirmed by reading templating.list[0] directly, no changes needed there) — just the loki datasource and the query above instead.


What was NOT tested — stated explicitly, not rounded up to "confirmed"​

  • The actual rendered panel in a live Grafana dashboard. Every query above was run directly against Loki's HTTP API, not through an actual dashboard panel — $service_name/$__range substitution, panel color thresholds, and how the "No data" vs. 0 distinction actually looks to a viewer haven't been visually confirmed.
  • Whitespace-only or garbage client_name values (e.g. a caller setting client_name: " ") — the != "" filter only catches a truly empty string, not whitespace. Not treated as a bug to fix — the library's own zero-opinion, pass-through design (established in PLAN-context-propagation.md) means it never validates what callers put in client_name, and a caller setting garbage values is a caller problem, not something this panel should try to paper over.
  • Query cost/performance at real fleet scale. The service_name=~".+" wildcard selector's cost caveat is already documented in INVESTIGATE-context-propagation.md's Q8 (scans every stream in the window, not just matching ones) — not re-derived here, still applies unchanged.
  • Whether service_principal/acting_user (added since this idea was first raised) should get similar panels. Out of scope for this investigation — client_name is the concrete ask that started this; the other two fields' dashboard treatment is separately tracked as its own deferred Tier 5 item.

Options​

Option A: Ship the panel as designed above​

Add one new stat panel to sovdev-logger-overview.json, mirroring "Active Integrations"' exact shape, using the fully-composed query above.

Pros: small, well-scoped, both real bugs already found and fixed before writing any panel JSON — not discovered after shipping. Cons: still hasn't been visually confirmed in an actual rendered dashboard (see "not tested" above) — first real render is the actual proof, not this investigation's API-level checks alone.

Option B: Also add a per-service breakdown (not just the fleet-wide count)​

Beyond the single "how many clients total" stat, add a table/graph showing which clients are calling which services — closer to a full drill-down view.

Pros: more useful for an operator actually investigating something, not just glancing at a number. Cons: bigger scope, no concrete ask for this yet (the original ask was specifically "how many," not "which") — better as its own follow-up once the simple count panel is live and an operator actually asks for more.


Recommendation​

Option A. Matches the original concrete ask exactly, both real bugs are already fixed, and it's small enough to implement and validate (including the still-missing live-render check) in one pass.


Open Questions​

  1. [Q1] Confirm via an actual live dashboard render (UIS, then Grafana Cloud) that $service_name/$__range substitute correctly into this query and the panel displays 0/correct-count/No data exactly as expected — the one thing this investigation's API-level checks couldn't cover.
  2. [Q2] — Resolved. "Active Clients" — matches "Active Integrations"' naming pattern for UI consistency, and unlike "Active Integrations" (found misleading by INVESTIGATE-terminology-review.md — it counts service_name, not integrations), this title is actually accurate: client_name literally represents clients by design.
  3. [Q3] — Resolved. Same top row as "Active Integrations." Checked the actual grid layout directly: the row is exactly full today (Active Integrations w:6 + Total Operations (cumulative) w:18 = 24). Fits with zero disruption elsewhere: place "Active Clients" at x:6, w:6, y:0, h:8 (immediately right of "Active Integrations"), narrow "Total Operations (cumulative)" from w:18 to w:12 and shift it to x:12 — still a plenty-wide timeseries panel. No other panel needs to move.

Next Steps​

  • Maintainer answers [Q2]/[Q3] — resolved 2026-07-13
  • Create PLAN-operator-dashboard-panel.md — small scope: add the panel JSON, validate via [Q1]'s live render against both real backends, update tools/dashboards/README.md if it documents panel-by-panel purpose