Skip to main content

Knowing What Operators Do

At 2 AM during a fire, nobody files a bug report. If a dispatcher fumbles with a control, clicks the wrong thing three times, or hits an error and just… works around it, the only witness is telemetry. That's what this chapter is about: the app streams every click, session, and error to Azure Application Insights, where we can replay what operators actually did — not what we hoped they'd do.

This lesson covers what gets tracked and how the pipeline works. Anatomy of a Click goes deep on the click tracker, and Asking Questions of the Data is the query cookbook.

What gets tracked

Five streams, from broadest to most specific:

Every click. A global listener captures each click with rich context — what was clicked, what it said, where it sits in the component hierarchy, and every data-* attribute it carried. This is Dynatrace-style ambient capture: nobody has to remember to instrument a button for it to show up in the data.

Sessions. Start (browser, platform, screen size, timezone) and end (duration, page views, last action) — the envelope around everything else.

Identity. After sign-in, the user's id, organization, and role are attached to all subsequent telemetry, so "what did agency X's operators struggle with" is an answerable question.

Errors. Console errors, unhandled promise rejections, and React component crashes — with one deliberate filter: ArcGIS abort errors are excluded, because the map fires them constantly during normal pan/zoom and they'd drown everything else.

Domain events. Explicit calls for the things clicks can't express: camera interactions (view, PTZ, snapshot), map interactions (pan, zoom, layer toggles), alert interactions (create, acknowledge).

How a click becomes a queryable row

Three things in that picture matter in practice. Events are batched, so don't expect one-network-request-per-click in the DevTools network tab. There's a 2–5 minute lag before events are queryable — patience before declaring your instrumentation broken. And retention is 90 days by default, so long-term analyses need their conclusions exported, not re-run.

Where the code lives

The whole system is two files plus a wiring point:

  • lib/applicationInsights.ts — the SDK setup and every track* function: initializeAppInsights, setUserContext/clearUserContext, trackClick, trackSessionStart/End, and the domain trackers (trackCameraInteraction, trackMapInteraction, trackAlertInteraction).
  • hooks/useAppInsightsTracking.ts — the React hooks: useClickTracking (the global listener), useSessionTracking (beforeunload), useUserTracking (auto-links auth state).
  • pages/_app.tsx — where the hooks are mounted, once, for the whole app.

Initialization is guarded by typeof window !== 'undefined' — the SDK never runs during SSR — and requires one env var, NEXT_PUBLIC_APPLICATIONINSIGHTS_CONNECTION_STRING. No connection string, no telemetry, no error: it fails quiet.

Wrapping up

Every click, session, identity, error, and domain event flows through two files into Azure, batched and queryable within minutes. The system is ambient — you get click data for free — but the quality of that data depends on how components are written, which is exactly where the next lesson picks up: Anatomy of a Click.