Monitoring & Analytics
Fourteen hundred cameras run themselves; an operator's job is to watch them, and no single view does that. Is the fleet healthy? Is the data layer near its throughput ceiling? What did the AI see overnight? What happened on this one camera? Who's logged in right now? The portal answers each with its own screen, and the useful thing to know is which data source each one reads — because that's what makes a screen fast, honest, or expensive.
The one idea to hold on to
The monitoring and analytics screens are read-only lenses, each over a different data source. None of them queries the running engine. They read Azure Monitor, the monitoring mirror, or SQL — whichever answers their question cheaply — and present it as a screen:
| Screen | Answers | Data source |
|---|---|---|
| System Health | infra logs & data-layer load | Azure Monitor Logs (KQL) + Cosmos RU metrics |
| Fleet Monitoring | which cameras are up / detecting | the monitoring mirror + AI fleet-health |
| Device Audit / AI Detection / Movement Log | what happened to this camera | SQL history tables |
| User Monitoring / Frontend Analytics | who's active, how the app is used | sessions + click tracking |
System Health: querying Azure Monitor
The System Health screen is two Azure Monitor lenses:
- Log Analytics (KQL).
ExecuteLogAnalyticsQuerypasses a KQL query plus a time range toILogAnalyticsService.ExecuteQueryAsync(query, startTime, endTime), which runs it against the Log Analytics workspace — the same telemetry the engine and functions emit. This is how an operator slices logs by function, error, or camera without leaving the portal. - Cosmos RU consumption.
GetCosmosRuConsumptionQuerycallsICosmosMetricsService.GetNormalizedRuConsumptionAsync(start, end)and returns aList<CosmosContainerRuPoint>— a normalized per-container time series (consumption as a fraction of provisioned throughput). That normalization is the point: it turns raw RU numbers into a "how close to the limit" chart you can read at a glance.
Fleet Monitoring: health without touching the engine
Fleet Monitoring answers "which cameras are running and online" and "which are actively detecting" — for the whole fleet, continuously. It does not query orchestrations (that would be the load the monitoring mirror exists to prevent). Running/online status comes from the mirror; detection status from the AI fleet-health queries.
Per-device logs: the SQL history tables
Zoom into one camera and the portal offers three chronological records, each a straight SQL read of a history table:
- Audit Log — administrative actions on the device (from Auditing).
- AI Detection Log — Condor detections for the camera (
GetAiDetectionLogQuery). - Movement Log — the PTZ movement history with reason codes.
Together they answer "what has happened to this camera," from three angles — who changed it, what the AI flagged, and how it moved.
Diagnostics and levers
Vitals: the live app as users feel it
App Analytics has a Vitals tab that reads the Frontend's browser telemetry on demand for the selected window and answers one question: is the live app slow for real users right now, and where? Every number is rated against a published or measured budget (Google's Core Web Vitals boundaries; the responsiveness budgets from the fleet-scale measurements of September 2026), compared with the previous window of the same length, and the tab opens with a plain list of what is concerning.
- Core Web Vitals — LCP, INP, CLS, TTFB, FCP at p75 from the
webvitals.*custom metrics the Frontend reports with theweb-vitalslibrary. INP is the number that corresponds to "the cursor feels slow". - Responsiveness — long tasks per visible minute, blocked time, worst
task, JS heap, from the Frontend's one-minute
ui.*aggregate (the old long-task event only fired above 1 s, which is why a 300 ms stall every 10 s never showed). - Data, errors & PeakFinder — the operational poll's p95 and failure share, browser exceptions per 100 sessions, PeakFinder engine loads per session and out-of-memory sessions.
- By release, view, device, browser — every vital per app build, newest first, so a regression is a red row the day it deploys.
- Worst sessions — ranked by blocked time, worst interaction and errors; a row with a signed-in user opens that user's analytics.
All of it goes through the same Monitoring/query proxy as the other tabs
(ClientApp/src/queries/vitals-queries.ts); the ratings and the concern rules
live in ClientApp/src/utils/vitals-health.ts. Core Web Vitals and the
responsiveness aggregate appear once the Frontend build that ships
lib/vitals.ts is live in the environment; the page-load, poll, error and
PeakFinder signals are available for every build.
Further reading
Wrapping up
The portal's monitoring and analytics are a set of read-only screens, each over the cheapest source that answers it: System Health over Azure Monitor logs and Cosmos RU metrics, Fleet Monitoring over the monitoring mirror and AI fleet-health, the per-device Audit/Detection/Movement logs over SQL history, and usage analytics over sessions and click tracking. None reads the engine; the destructive purge and feature-flag levers sit alongside, to be used deliberately.
That completes the Admin API — the platform's control plane, its device management, its record of every change, and its windows into the running system. Next, the outbound side of the platform: Notifications.