Skip to main content

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:

ScreenAnswersData source
System Healthinfra logs & data-layer loadAzure Monitor Logs (KQL) + Cosmos RU metrics
Fleet Monitoringwhich cameras are up / detectingthe monitoring mirror + AI fleet-health
Device Audit / AI Detection / Movement Logwhat happened to this cameraSQL history tables
User Monitoring / Frontend Analyticswho's active, how the app is usedsessions + click tracking

System Health: querying Azure Monitor​

The System Health screen is two Azure Monitor lenses:

  • Log Analytics (KQL). ExecuteLogAnalyticsQuery passes a KQL query plus a time range to ILogAnalyticsService.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. GetCosmosRuConsumptionQuery calls ICosmosMetricsService.GetNormalizedRuConsumptionAsync(start, end) and returns a List<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 the web-vitals library. 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.