GitHub Copilot ChatGPT Claude Codex CLI Cursor opencode Skill Text

applicationinsights-web-ts

Instrument browser/web apps with the Application Insights JavaScript SDK (@microsoft/applicationinsights-web). Use for Real User Monitoring (RUM) — page views, clicks, AJAX/fetch dependencies, exceptions, custom events, and browser-side GenAI agent traces correlated to backend Op

Ciza · 0 points · 23 views 1 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-sdk-typescript_skills_applicationinsights-web-ts-e58528d.zip · 15 KB
Part of microsoft/skills — 195 skills

Install

skills CLI npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git git clone https://github.com/microsoft/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Application Insights JavaScript SDK (Web) for TypeScript

Real User Monitoring (RUM) for browser apps with @microsoft/applicationinsights-web. Auto-collects page views, AJAX/fetch dependencies, unhandled exceptions, and (with the Click Analytics plugin) clicks. Supports custom events, metrics, and GenAI agent traces that follow OpenTelemetry GenAI semantic conventions and correlate to backend spans via W3C Trace Context.

Distinct from azure-monitor-opentelemetry-ts, which is for Node.js server apps. This skill is for browser/web code (and React Native).

Before Implementation

Search microsoft-docs MCP for current API patterns:

  • Query: "Application Insights JavaScript SDK setup"
  • Query: "Application Insights JavaScript SDK configuration"
  • Query: "Application Insights JavaScript framework extensions React Angular"
  • Verify package version: npm view @microsoft/applicationinsights-web version

Packages

Package Purpose
@microsoft/applicationinsights-web Core RUM SDK (page views, AJAX, exceptions).
@microsoft/applicationinsights-clickanalytics-js Auto-collect click telemetry.
@microsoft/applicationinsights-react-js React plugin (router instrumentation, hooks, HOC, ErrorBoundary).
@microsoft/applicationinsights-react-native React Native plugin (native crashes, sessions).
@microsoft/applicationinsights-angularplugin-js Angular plugin (router events, ErrorHandler).
@microsoft/applicationinsights-debugplugin-js Dev-only telemetry inspector.
@microsoft/applicationinsights-perfmarkmeasure-js User Timing (performance.mark/measure) integration.

Installation

npm i --save @microsoft/applicationinsights-web
# Optional plugins (install only what you use):
npm i --save @microsoft/applicationinsights-clickanalytics-js
npm i --save @microsoft/applicationinsights-react-js @microsoft/applicationinsights-react-native @microsoft/applicationinsights-angularplugin-js

Typings ship with the package — no separate @types/... install needed.

Connection String

The browser SDK requires a connection string at init time. It ships in plaintext to clients — Microsoft Entra ID auth is not supported for browser telemetry. Use a separate App Insights resource with local auth enabled for browser RUM if you need to isolate it from backend telemetry.

# Vite / CRA / Next.js — expose to client via the public env prefix
VITE_APPINSIGHTS_CONNECTION_STRING="InstrumentationKey=...;IngestionEndpoint=https://...;LiveEndpoint=https://..."
NEXT_PUBLIC_APPINSIGHTS_CONNECTION_STRING="InstrumentationKey=..."

Quick Start (npm)

import { ApplicationInsights } from "@microsoft/applicationinsights-web";

export const appInsights = new ApplicationInsights({
  config: {
    connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
    enableAutoRouteTracking: true,        // SPA route changes -> page views
    enableCorsCorrelation: true,          // propagate Request-Id / traceparent to cross-origin AJAX
    enableRequestHeaderTracking: true,
    enableResponseHeaderTracking: true,
    distributedTracingMode: 2,            // DistributedTracingModes.AI_AND_W3C — emit traceparent for backend correlation
    autoTrackPageVisitTime: true,
    disableFetchTracking: false,          // fetch() is auto-instrumented by default
    excludeRequestFromAutoTrackingPatterns: [/livemetrics\.azure\.com/i]
  }
});

appInsights.loadAppInsights();
appInsights.trackPageView();

Call loadAppInsights() exactly once, as early as possible (before user interactions you want tracked). Then trackPageView() for the initial load — when enableAutoRouteTracking is on, subsequent route changes are automatic.

Quick Start (SDK Loader Script)

Recommended when you want auto-updating SDK and zero build pipeline. Paste this as the first <script> in <head>:

<script type="text/javascript" src="https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js" crossorigin="anonymous"></script>
<script type="text/javascript">
  var appInsights = window.appInsights || function (cfg) {
    /* See: https://learn.microsoft.com/azure/azure-monitor/app/javascript-sdk
       Use the latest snippet from the Microsoft Learn page above — it includes
       backup-CDN failover (cr), SDK-load-failure reporting, and the queue shim
       so calls before SDK ready are not lost. */
  }({ src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
      crossOrigin: "anonymous",
      cfg: { connectionString: "YOUR_CONNECTION_STRING" } });
</script>

Loader-only API (queued until SDK loads): trackEvent, trackPageView, trackException, trackTrace, trackDependencyData, trackMetric, trackPageViewPerformance, startTrackPage, stopTrackPage, startTrackEvent, stopTrackEvent, addTelemetryInitializer, setAuthenticatedUserContext, clearAuthenticatedUserContext, flush.

Core Tracking APIs

// Page views (SPAs that disable enableAutoRouteTracking)
appInsights.trackPageView({ name: "Checkout", uri: "/checkout", properties: { cartSize: 3 } });

// Custom events (user actions, business events)
appInsights.trackEvent({ name: "PurchaseCompleted" }, { orderId: "ord_123", amountUsd: 49.95 });

// Exceptions (caught errors)
try {
  await pay(order);
} catch (err) {
  appInsights.trackException({ exception: err as Error, severityLevel: 3, properties: { orderId: order.id } });
}

// Traces (logs, severity 0=Verbose, 1=Info, 2=Warning, 3=Error, 4=Critical)
appInsights.trackTrace({ message: "Cart hydrated from local storage", severityLevel: 1 });

// Custom metrics (numeric)
appInsights.trackMetric({ name: "checkout.duration_ms", average: 1234 });

// Dependencies (manually-tracked outbound calls — fetch/XHR are auto-tracked)
appInsights.trackDependencyData({
  id: crypto.randomUUID(),
  name: "GET /api/orders",
  duration: 87, success: true, responseCode: 200,
  data: "https://api.example.com/api/orders", target: "api.example.com", type: "Fetch"
});

// User identity (set ONCE per authenticated session — values are PII; do not pass emails)
appInsights.setAuthenticatedUserContext("user-id-123", "tenant-456", /*storeInCookie*/ true);
appInsights.clearAuthenticatedUserContext(); // on logout

// Force send before unload
appInsights.flush();

Telemetry Initializers (enrichment & filtering)

Run for every envelope before send. Return false to drop.

import type { ITelemetryItem } from "@microsoft/applicationinsights-web";

appInsights.addTelemetryInitializer((item: ITelemetryItem) => {
  item.tags ??= {};
  item.tags["ai.cloud.role"] = "web-shop";
  item.tags["ai.cloud.roleInstance"] = window.location.hostname;
  item.data ??= {};
  item.data["app.version"] = import.meta.env.VITE_APP_VERSION;
  item.data["app.build"] = import.meta.env.VITE_BUILD_SHA;

  // Drop noisy health-check page views
  if (item.baseType === "PageviewData" && item.baseData?.uri?.endsWith("/healthz")) return false;

  // Scrub query-string secrets
  if (item.baseData?.uri) {
    item.baseData.uri = item.baseData.uri.replace(/([?&](token|sig|key)=)[^&]+/gi, "$1REDACTED");
  }
});

Click Analytics

import { ClickAnalyticsPlugin } from "@microsoft/applicationinsights-clickanalytics-js";

const clickPlugin = new ClickAnalyticsPlugin();
const appInsights = new ApplicationInsights({
  config: {
    connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
    extensions: [clickPlugin],
    extensionConfig: {
      [clickPlugin.identifier]: {
        autoCapture: true,
        dataTags: { useDefaultContentNameOrId: true, customDataPrefix: "data-ai-" },
        urlCollectHash: false,
        behaviorValidator: (b: string) => /^[a-z0-9_]+$/.test(b) ? b : ""
      }
    }
  }
});
appInsights.loadAppInsights();

Mark elements with data-ai-* attributes; clicks are emitted as Custom Events with parent-content metadata.

SPA Route Tracking

  • Built-in: set enableAutoRouteTracking: true. Hooks history.pushState/replaceState and popstate.
  • React Router: use @microsoft/applicationinsights-react-js withAITracking HOC (see references/framework-extensions.md).
  • Manual: call appInsights.trackPageView({ name, uri }) in your router's useEffect on route change. Disable enableAutoRouteTracking to avoid double counting.

Distributed Tracing (correlate to backend)

Set distributedTracingMode: 2 (DistributedTracingModes.AI_AND_W3C). The SDK adds traceparent (and legacy Request-Id) to outbound fetch/XHR. Backends instrumented with OpenTelemetry (e.g. @azure/monitor-opentelemetry) auto-link to the browser's operation_Id.

For cross-origin calls, also set enableCorsCorrelation: true and add the calling origin to the CORS exposed headers on the API.

GenAI Agent Traces (OTel semantic conventions)

When the browser invokes an AI agent (function-calling, tool-use, model calls direct from the client), emit App Insights Dependency telemetry whose attributes follow the OpenTelemetry GenAI semantic conventions so they are queryable alongside backend agent spans in App Insights / Log Analytics.

Set the opt-in env first so backend instrumentations agree on the same schema version:

OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental

Required attribute keys (use the OTel names verbatim)

Span / op Required attributes
invoke_agent {agent.name} gen_ai.operation.name=invoke_agent, gen_ai.provider.name, gen_ai.agent.name, gen_ai.agent.id (when known)
create_agent {agent.name} gen_ai.operation.name=create_agent, gen_ai.provider.name, gen_ai.agent.name, gen_ai.request.model
chat {model} gen_ai.operation.name=chat, gen_ai.provider.name, gen_ai.request.model, gen_ai.response.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
execute_tool {tool.name} gen_ai.operation.name=execute_tool, gen_ai.tool.name, gen_ai.tool.type (function | extension | datastore), gen_ai.tool.call.id

gen_ai.provider.name well-known values: openai, azure.ai.openai, azure.ai.inference, anthropic, aws.bedrock, gcp.gemini, gcp.vertex_ai, cohere, mistral_ai, groq, deepseek, perplexity, x_ai, ibm.watsonx.ai.

Sensitive content opt-in. gen_ai.system_instructions, gen_ai.input.messages, gen_ai.output.messages, gen_ai.tool.call.arguments, gen_ai.tool.call.result are Opt-In by default. Gate them behind a runtime flag and avoid them in production unless you have approved data handling.

Pattern: invoke_agent + nested tool/model spans

import { ApplicationInsights, SeverityLevel } from "@microsoft/applicationinsights-web";

type GenAiAttrs = Record<string, string | number | boolean | undefined>;

function startGenAiSpan(name: string, attrs: GenAiAttrs) {
  const id = crypto.randomUUID();
  const start = performance.now();
  const baseProps: GenAiAttrs = { "gen_ai.span.id": id, ...attrs };
  return {
    end(success: boolean, extra: GenAiAttrs = {}, error?: Error) {
      const duration = Math.round(performance.now() - start);
      const properties = { ...baseProps, ...extra };
      appInsights.trackDependencyData({
        id, name, duration, success,
        responseCode: error ? 500 : 200,
        type: "GenAI",
        target: String(attrs["gen_ai.provider.name"] ?? "genai"),
        properties: properties as Record<string, string>
      });
      if (error) {
        appInsights.trackException({
          exception: error,
          severityLevel: SeverityLevel.Error,
          properties: { ...properties, "error.type": error.name } as Record<string, string>
        });
      }
    }
  };
}

// Agent invocation
const agentSpan = startGenAiSpan("invoke_agent ResearchAssistant", {
  "gen_ai.operation.name": "invoke_agent",
  "gen_ai.provider.name": "azure.ai.openai",
  "gen_ai.agent.name": "ResearchAssistant",
  "gen_ai.agent.id": "asst_5j66UpCpwteGg4YSxUnt7lPY",
  "gen_ai.request.model": "gpt-4o-mini",
  "server.address": "myresource.openai.azure.com"
});

try {
  // Nested chat completion span
  const chat = startGenAiSpan("chat gpt-4o-mini", {
    "gen_ai.operation.name": "chat",
    "gen_ai.provider.name": "azure.ai.openai",
    "gen_ai.request.model": "gpt-4o-mini"
  });
  const res = await callAzureOpenAi(/* ... */);
  chat.end(true, {
    "gen_ai.response.model": res.model,
    "gen_ai.response.id": res.id,
    "gen_ai.response.finish_reasons": JSON.stringify(res.choices.map(c => c.finish_reason)),
    "gen_ai.usage.input_tokens": res.usage.prompt_tokens,
    "gen_ai.usage.output_tokens": res.usage.completion_tokens,
    "gen_ai.output.type": "text"
  });

  // Nested tool execution span
  const tool = startGenAiSpan("execute_tool getWeather", {
    "gen_ai.operation.name": "execute_tool",
    "gen_ai.tool.name": "getWeather",
    "gen_ai.tool.type": "function",
    "gen_ai.tool.call.id": "call_abc123"
  });
  const toolResult = await runGetWeather({ location: "SF" });
  tool.end(true);

  agentSpan.end(true, {
    "gen_ai.usage.input_tokens": res.usage.prompt_tokens,
    "gen_ai.usage.output_tokens": res.usage.completion_tokens
  });
} catch (err) {
  agentSpan.end(false, { "error.type": (err as Error).name }, err as Error);
}

The browser's traceparent is automatically attached to outbound fetch (when distributedTracingMode: 2), so downstream Azure OpenAI / agent backend spans hang under the same operation_Id in App Insights.

For the full attribute reference, well-known values, and content-capture guidance, see references/agent-traces.md.

KQL: query GenAI traces in App Insights

dependencies
| where type == "GenAI"
| extend op   = tostring(customDimensions["gen_ai.operation.name"]),
         agent = tostring(customDimensions["gen_ai.agent.name"]),
         model = tostring(customDimensions["gen_ai.request.model"]),
         tin   = toint(customDimensions["gen_ai.usage.input_tokens"]),
         tout  = toint(customDimensions["gen_ai.usage.output_tokens"])
| summarize calls=count(), p95_ms=percentile(duration, 95),
            avg_in=avg(tin), avg_out=avg(tout) by op, agent, model, bin(timestamp, 5m)

React (TypeScript)

See references/framework-extensions.md for full React, React Native, Angular, Next.js, and Vite recipes.

import { ApplicationInsights } from "@microsoft/applicationinsights-web";
import { ReactPlugin, withAITracking } from "@microsoft/applicationinsights-react-js";
import { createBrowserHistory } from "history";

const reactPlugin = new ReactPlugin();
const browserHistory = createBrowserHistory();

export const appInsights = new ApplicationInsights({
  config: {
    connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
    extensions: [reactPlugin],
    extensionConfig: { [reactPlugin.identifier]: { history: browserHistory } }
  }
});
appInsights.loadAppInsights();

export const TrackedCheckout = withAITracking(reactPlugin, Checkout, "Checkout");

React Native

import { ApplicationInsights } from "@microsoft/applicationinsights-web";
import { ReactNativePlugin } from "@microsoft/applicationinsights-react-native";

const rnPlugin = new ReactNativePlugin();
const appInsights = new ApplicationInsights({
  config: {
    connectionString: process.env.EXPO_PUBLIC_APPINSIGHTS_CONNECTION_STRING,
    extensions: [rnPlugin],
    disableFetchTracking: false
  }
});
appInsights.loadAppInsights();

Performance — Web Vitals

Auto-collected: page-load timings via PerformanceTiming / PerformanceNavigationTiming. To add Core Web Vitals:

import { onCLS, onLCP, onINP, type Metric } from "web-vitals";

function send(m: Metric) {
  appInsights.trackMetric(
    { name: `web_vitals.${m.name.toLowerCase()}`, average: m.value },
    { rating: m.rating, navigationType: m.navigationType, id: m.id }
  );
}
onCLS(send); onLCP(send); onINP(send);

Cookies & Privacy

new ApplicationInsights({ config: {
  connectionString,
  isCookieUseDisabled: true,         // hard-disable all cookies
  cookieCfg: { enabled: true, domain: ".example.com", path: "/", expiry: 365 }
}});

To honor consent dynamically:

appInsights.getCookieMgr().setEnabled(userGaveConsent);
appInsights.config.disableTelemetry = !userGaveConsent;

Sampling

Server-side ingestion sampling (recommended) is configured on the App Insights resource. SDK-side sampling reduces network use:

new ApplicationInsights({ config: { connectionString, samplingPercentage: 50 } });

Per-type sampling via telemetry initializer: drop with return false based on item.baseType.

Offline / Send-on-Unload

The SDK uses sendBeacon (default onunloadDisableBeacon: false) to flush on pagehide / unload. For SPAs, also call appInsights.flush() before destructive transitions (logout, hard reload).

Common Pitfalls

  1. Do not initialize twice. Re-importing the module under different bundles produces duplicate page views. Use a single shared module export.
  2. Initialize before first user input to avoid losing early clicks/exceptions.
  3. Connection string is public — never reuse the same App Insights resource for backend secrets.
  4. enableAutoRouteTracking + manual trackPageView = duplicates. Pick one.
  5. CORS distributed tracing requires the API to allow Request-Id, Request-Context, traceparent, tracestate request headers and expose Request-Context response header.
  6. GenAI sensitive content (gen_ai.input.messages etc.) is Opt-In — never log without an explicit runtime flag and approved data handling.
  7. Agent token usage is on chat spans, not invoke_agent — copy aggregated usage to the parent agent span only if you know it.
  8. React StrictMode double-invokes effects in dev — guard loadAppInsights() with a module-level singleton.

Bundle Size

The full web SDK is ~110 KB minified (~36 KB gzipped). For aggressive budgets, use the Loader Script path so the SDK loads asynchronously off the critical path, or tree-shake unused plugins.

Key Types

import {
  ApplicationInsights,
  SeverityLevel,
  DistributedTracingModes,
  type IConfiguration,
  type IConfig,
  type ITelemetryItem,
  type ITelemetryPlugin,
  type ICustomProperties,
  type IPageViewTelemetry,
  type IEventTelemetry,
  type IExceptionTelemetry,
  type ITraceTelemetry,
  type IMetricTelemetry,
  type IDependencyTelemetry
} from "@microsoft/applicationinsights-web";

Best Practices

  1. One singleton instance exported from a single module.
  2. Initialize early in the app entrypoint, before router setup.
  3. Use telemetry initializers to attach app.version, tenantId, and to scrub PII / query-string secrets.
  4. Set distributedTracingMode: 2 and ensure your APIs accept/expose W3C trace context headers.
  5. For GenAI, follow OTel gen_ai.* attribute names verbatim — they are queryable across browser and backend telemetry uniformly.
  6. Gate sensitive content capture (gen_ai.input.messages / gen_ai.output.messages) behind a build-time or runtime opt-in.
  7. Flush on logout / sensitive navigation so in-flight telemetry isn't dropped.

References

Files (skills)
  • references
    • agent-traces.md 7.6 KB
      # GenAI Agent Traces — OpenTelemetry Semantic Conventions (distilled)
      
      Reference for the `gen_ai.*` attributes the App Insights JavaScript SDK emits as Dependency telemetry so browser-side AI agent calls correlate with backend OpenTelemetry spans.
      
      > **Stability opt-in.** Set `OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental` in any backend that emits or reads these attributes. Without it, instrumentations may emit older/different attribute names.
      
      ## Operation names (`gen_ai.operation.name`)
      
      | Value | When |
      | --- | --- |
      | `chat` | Chat-completion call to a model. |
      | `generate_content` | Multimodal content generation (Gemini-style). |
      | `text_completion` | Legacy single-prompt completion. |
      | `embeddings` | Embedding generation. |
      | `create_agent` | Agent definition created/registered. |
      | `invoke_agent` | Agent run/invocation (turn). |
      | `execute_tool` | Tool/function call executed by an agent. |
      
      Span name MUST follow `{operation.name} {target}` — e.g. `chat gpt-4o-mini`, `invoke_agent ResearchAssistant`, `execute_tool getWeather`.
      
      ## Provider names (`gen_ai.provider.name`)
      
      `openai`, `azure.ai.openai`, `azure.ai.inference`, `anthropic`, `aws.bedrock`, `gcp.gemini`, `gcp.vertex_ai`, `cohere`, `mistral_ai`, `groq`, `deepseek`, `perplexity`, `x_ai`, `ibm.watsonx.ai`. Use lower-case, dotted form. New providers: use a stable lower-case identifier; no spaces.
      
      ## Common request / response attributes (all spans)
      
      | Attribute | Type | Notes |
      | --- | --- | --- |
      | `gen_ai.request.model` | string | Requested model name. |
      | `gen_ai.response.model` | string | Returned model name (may differ — alias / version pinning). |
      | `gen_ai.response.id` | string | Provider-side response/request id. |
      | `gen_ai.response.finish_reasons` | string[] | One per choice: `stop`, `length`, `tool_calls`, `content_filter`, `error`, … |
      | `gen_ai.request.temperature` | double | |
      | `gen_ai.request.top_p` | double | |
      | `gen_ai.request.top_k` | double | |
      | `gen_ai.request.max_tokens` | int | |
      | `gen_ai.request.presence_penalty` | double | |
      | `gen_ai.request.frequency_penalty` | double | |
      | `gen_ai.request.stop_sequences` | string[] | |
      | `gen_ai.request.seed` | int | |
      | `gen_ai.request.choice.count` | int | Number of choices requested (omit if 1). |
      | `gen_ai.request.encoding_formats` | string[] | Embeddings only: `base64`, `float`, … |
      | `gen_ai.output.type` | enum | `text` \| `json` \| `image` \| `speech`. |
      | `gen_ai.usage.input_tokens` | int | Prompt tokens. |
      | `gen_ai.usage.output_tokens` | int | Completion tokens. |
      | `gen_ai.conversation.id` | string | App-defined conversation/session id (also use as `session.id`). |
      | `server.address` | string | Provider host (e.g. `myresource.openai.azure.com`). |
      | `server.port` | int | Omit for default 443. |
      | `error.type` | string | Error class / status code on failure. |
      
      ## Agent attributes
      
      | Attribute | Type | Notes |
      | --- | --- | --- |
      | `gen_ai.agent.id` | string | Stable agent id (provider-assigned, e.g. `asst_…`). |
      | `gen_ai.agent.name` | string | Human-readable agent name. |
      | `gen_ai.agent.description` | string | Short purpose statement. |
      | `gen_ai.agent.version` | string | Agent version, when versioned. |
      
      `create_agent` and `invoke_agent` spans are required to set `gen_ai.agent.name`. `gen_ai.agent.id` is required when known.
      
      ## Tool attributes (`execute_tool`)
      
      | Attribute | Type | Notes |
      | --- | --- | --- |
      | `gen_ai.tool.name` | string | Tool/function name. |
      | `gen_ai.tool.type` | enum | `function` \| `extension` \| `datastore`. |
      | `gen_ai.tool.description` | string | Optional. |
      | `gen_ai.tool.call.id` | string | Provider-issued tool-call id (matches the `tool_calls[*].id` in the model output). |
      | `gen_ai.tool.call.arguments` | string | **Opt-In.** JSON-serialized arguments. May contain user data. |
      | `gen_ai.tool.call.result` | string | **Opt-In.** Tool result. May contain user data. |
      
      ## Sensitive content (Opt-In only)
      
      | Attribute / event | Notes |
      | --- | --- |
      | `gen_ai.system_instructions` | System prompt(s). Span attribute. |
      | `gen_ai.input.messages` | Full input message array (role + content). Span attribute. |
      | `gen_ai.output.messages` | Full output message array. Span attribute. |
      | `gen_ai.client.inference.operation.details` (event) | Carries request/response messages + tool definitions when the span attribute path is too large. |
      
      **Capture rules:**
      
      1. Off by default. Gate behind a runtime/config flag.
      2. Truncate large content (e.g. > 8 KB) and set `gen_ai.truncated=true`.
      3. Redact PII where required by policy before emitting.
      4. For very large or sensitive payloads, emit a reference (e.g. blob URL) instead of inline content.
      
      ## Token usage
      
      - Set `gen_ai.usage.input_tokens` and `gen_ai.usage.output_tokens` on **every** model span (`chat`, `text_completion`, `generate_content`, `embeddings` — embeddings have no output tokens).
      - For agents, sum tokens from child model spans onto the parent `invoke_agent` only when you have the totals. Never double-count by also summing nested agent invocations the parent didn't directly own.
      
      ## Streaming
      
      - Span ends when the stream completes (last chunk received OR client closes).
      - `gen_ai.usage.*` and `gen_ai.response.finish_reasons` are set at end-of-stream.
      - Optional event `gen_ai.choice` may be emitted per choice with index + finish reason for live dashboards.
      
      ## Errors
      
      - Set span status to ERROR.
      - Set `error.type` to a stable identifier (HTTP status, provider error code, or exception class). Avoid free-form messages.
      - For tool errors, the `execute_tool` span carries the error; the parent `invoke_agent` span only fails if the agent run aborted.
      
      ## Mapping to App Insights
      
      When emitting from the browser via the App Insights JS SDK, use **Dependency** telemetry with `type: "GenAI"` and put all `gen_ai.*` keys in `properties`. App Insights flattens `properties` into `customDimensions`, where they're queryable in KQL with `tostring(customDimensions["gen_ai.operation.name"])`.
      
      | OTel concept | App Insights field |
      | --- | --- |
      | Span name | `name` |
      | Span kind=CLIENT, target | `target` (e.g. provider host) |
      | Duration | `duration` (ms) |
      | Span status | `success` + `resultCode` |
      | Span attributes | `properties` → `customDimensions` |
      | Trace id / Span id | `operation_Id` / `id` (set automatically when `distributedTracingMode: 2`) |
      | Span events | Emit as `trackTrace` with the same `operation_Id` |
      
      ## Useful KQL
      
      ```kusto
      // Top agents by p95 latency
      dependencies
      | where type == "GenAI" and tostring(customDimensions["gen_ai.operation.name"]) == "invoke_agent"
      | summarize p95=percentile(duration,95), runs=count()
          by agent=tostring(customDimensions["gen_ai.agent.name"])
      | order by p95 desc
      
      // Token usage by model
      dependencies
      | where type == "GenAI" and tostring(customDimensions["gen_ai.operation.name"]) == "chat"
      | extend tin=toint(customDimensions["gen_ai.usage.input_tokens"]),
               tout=toint(customDimensions["gen_ai.usage.output_tokens"]),
               model=tostring(customDimensions["gen_ai.response.model"])
      | summarize sum_in=sum(tin), sum_out=sum(tout), calls=count() by model
      
      // Failed tool calls
      dependencies
      | where type == "GenAI" and tostring(customDimensions["gen_ai.operation.name"]) == "execute_tool"
      | where success == false
      | project timestamp, tool=tostring(customDimensions["gen_ai.tool.name"]),
                err=tostring(customDimensions["error.type"]), operation_Id
      ```
      
      ## See also
      
      - OTel GenAI spec: <https://opentelemetry.io/docs/specs/semconv/gen-ai/>
      - Azure AI inference semconv: <https://opentelemetry.io/docs/specs/semconv/gen-ai/azure-ai-inference/>
      - App Insights data model (Dependency): <https://learn.microsoft.com/azure/azure-monitor/app/data-model-complete>
      
    • configuration.md 6.5 KB
      # Configuration Reference
      
      Complete reference for `IConfiguration` / `IConfig` passed to `new ApplicationInsights({ config })`.
      
      ## Identity & Endpoints
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `connectionString` | string | — | **Required.** Includes ingestion + live-metrics endpoints. Public — never reuse with backend secrets. |
      | `instrumentationKey` | string | — | Legacy. Prefer `connectionString`. |
      | `endpointUrl` | string | regional | Override ingestion endpoint (sovereign clouds, proxies). |
      | `userOverrideEndpointUrl` | string | — | Force a specific ingestion URL bypassing connection-string parsing. |
      | `appId` | string | — | App identifier for cross-component correlation. |
      
      ## Auto-Collection
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `disableAjaxTracking` | bool | `false` | Stops XHR auto-instrumentation. |
      | `disableFetchTracking` | bool | `false` | Stops `fetch` auto-instrumentation. |
      | `disableExceptionTracking` | bool | `false` | Stops `window.onerror` / unhandled-rejection capture. |
      | `autoTrackPageVisitTime` | bool | `false` | Emit time-on-page when leaving. |
      | `enableAutoRouteTracking` | bool | `false` | Hook `history.pushState/replaceState`. SPA only. |
      | `enableUnhandledPromiseRejectionTracking` | bool | `false` | Emits `unhandledRejection` as exceptions. |
      | `excludeRequestFromAutoTrackingPatterns` | RegExp[] | — | Skip URLs (e.g. health checks, telemetry endpoints). |
      
      ## Distributed Tracing
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `distributedTracingMode` | enum | `AI` (1) | `AI_AND_W3C` (2) is required for OTel-instrumented backends. |
      | `enableCorsCorrelation` | bool | `false` | Add correlation headers on cross-origin requests (requires server CORS allow-list). |
      | `correlationHeaderExcludePatterns` | RegExp[] | — | Skip correlation for matching URLs (e.g. third-party CDNs that reject unknown headers). |
      | `correlationHeaderDomains` | string[] | — | Limit correlation to specific domains. |
      | `correlationHeaderExcludedDomains` | string[] | — | Hard-exclude domains. |
      | `enableRequestHeaderTracking` | bool | `false` | Capture outbound request headers (PII risk). |
      | `enableResponseHeaderTracking` | bool | `false` | Capture inbound response headers. |
      | `enableAjaxErrorStatusText` | bool | `false` | Include `statusText` on failed AJAX. |
      | `enableAjaxPerfTracking` | bool | `false` | Use `PerformanceResourceTiming` for richer AJAX timing. |
      | `maxAjaxCallsPerView` | number | 500 | Limit AJAX deps per page view (-1 = unlimited). |
      
      ## Sampling & Throttling
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `samplingPercentage` | number | 100 | Client-side ingestion sampling. Whole numbers only. |
      | `maxBatchSizeInBytes` | number | 102400 | Batch send threshold. |
      | `maxBatchInterval` | number | 15000 | ms between sends. |
      
      ## Cookies & Storage
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `isCookieUseDisabled` | bool | `false` | Hard-disable cookies (no `ai_user`/`ai_session`). |
      | `cookieCfg` | object | — | `{ enabled, domain, path, expiry, secure, ... }`. |
      | `isStorageUseDisabled` | bool | `false` | Disable `localStorage` for buffer/retry. |
      | `disableUserInitMessage` | bool | `false` | Suppress noisy diagnostic init message. |
      
      ## Send Behaviour
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `onunloadDisableBeacon` | bool | `false` | If `true`, do not use `sendBeacon` on unload. |
      | `disableXhr` | bool | `false` | Force-disable XHR transport (sendBeacon/fetch only). |
      | `disableFlushOnBeforeUnload` | bool | `false` | Skip auto-flush on `beforeunload`. |
      | `disableFlushOnUnload` | bool | `false` | Skip auto-flush on `unload`/`pagehide`. |
      | `enableSessionStorageBuffer` | bool | `true` | Persist queued telemetry across reloads. |
      
      ## Retry & Failover
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `maxRetries` | number | 10 | Per-batch retry budget. |
      | `disableInstrumentationKeyValidation` | bool | `false` | Skip GUID validation (sovereign clouds). |
      | `loggingLevelConsole` | 0–2 | 0 | 0=off, 1=critical, 2=warn+critical. |
      | `loggingLevelTelemetry` | 0–2 | 1 | Same scale; sends SDK self-diagnostics as Trace. |
      
      ## Plugins
      
      | Key | Type | Default | Notes |
      | --- | --- | --- | --- |
      | `extensions` | `ITelemetryPlugin[]` | `[]` | E.g. `ReactPlugin`, `ClickAnalyticsPlugin`. |
      | `extensionConfig` | `Record<string, any>` | `{}` | Keyed by `plugin.identifier`. |
      | `disableTelemetry` | bool | `false` | Master kill-switch (set after consent flow). |
      
      ## Recommended Production Config
      
      ```typescript
      import { ApplicationInsights, DistributedTracingModes } from "@microsoft/applicationinsights-web";
      
      new ApplicationInsights({
        config: {
          connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
      
          // Auto-collection
          enableAutoRouteTracking: true,
          autoTrackPageVisitTime: true,
          enableUnhandledPromiseRejectionTracking: true,
          excludeRequestFromAutoTrackingPatterns: [
            /\/healthz$/i,
            /dc\.services\.visualstudio\.com/i,
            /js\.monitor\.azure\.com/i
          ],
      
          // Distributed tracing
          distributedTracingMode: DistributedTracingModes.AI_AND_W3C,
          enableCorsCorrelation: true,
          correlationHeaderExcludedDomains: ["*.googleapis.com", "*.facebook.com"],
      
          // Privacy
          isCookieUseDisabled: false,
          cookieCfg: { domain: ".example.com" },
      
          // Throughput
          samplingPercentage: 100,        // tune down if egress is high
          maxBatchInterval: 5000,
      
          // Diagnostics (set 0 in prod after stable)
          loggingLevelConsole: 0,
          loggingLevelTelemetry: 1
        }
      });
      ```
      
      ## Tuning notes
      
      - **Mobile networks:** lower `maxBatchInterval` to 3–5 s and `maxBatchSizeInBytes` to 32 KB so unload flushes are smaller.
      - **High-traffic SPAs:** turn on `samplingPercentage` (e.g. 25) before paying for ingestion overage. Server-side ingestion sampling on the App Insights resource is preferred when available.
      - **Strict CSP:** Loader Script needs `script-src https://js.monitor.azure.com 'self'`. The npm import path requires no extra CSP.
      - **Tracking opt-out:** set `disableTelemetry = true` and call `getCookieMgr().setEnabled(false)` after the user revokes consent. Do not destroy the SDK instance — re-enable by toggling `disableTelemetry = false`.
      
      ## See also
      
      - Microsoft Learn — JavaScript SDK configuration: <https://learn.microsoft.com/azure/azure-monitor/app/javascript-sdk-configuration>
      - IConfiguration source: <https://github.com/microsoft/ApplicationInsights-JS/blob/main/shared/AppInsightsCommon/src/Interfaces/IConfig.ts>
      
    • framework-extensions.md 7.7 KB
      # Framework Extensions
      
      ## React (`@microsoft/applicationinsights-react-js`)
      
      ```bash
      npm i @microsoft/applicationinsights-react-js history
      ```
      
      ```typescript
      // app-insights.ts
      import { ApplicationInsights } from "@microsoft/applicationinsights-web";
      import { ReactPlugin } from "@microsoft/applicationinsights-react-js";
      import { createBrowserHistory } from "history";
      
      export const reactPlugin = new ReactPlugin();
      export const browserHistory = createBrowserHistory();
      
      export const appInsights = new ApplicationInsights({
        config: {
          connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
          extensions: [reactPlugin],
          extensionConfig: { [reactPlugin.identifier]: { history: browserHistory } },
          enableAutoRouteTracking: false  // ReactPlugin handles routes
        }
      });
      appInsights.loadAppInsights();
      ```
      
      ### Tracking a component (HOC)
      
      ```typescript
      import { withAITracking } from "@microsoft/applicationinsights-react-js";
      import { reactPlugin } from "./app-insights";
      
      function Checkout() { /* ... */ }
      export default withAITracking(reactPlugin, Checkout, "Checkout");
      ```
      
      `withAITracking` measures the time the component is mounted and emits a metric `React Component Engaged Time (seconds)`.
      
      ### Hooks
      
      ```typescript
      import {
        useTrackEvent, useTrackMetric, useAppInsightsContext
      } from "@microsoft/applicationinsights-react-js";
      
      function PayButton() {
        const ai = useAppInsightsContext();
        const trackPay = useTrackEvent(ai, "PayClicked", { /* extra props */ });
      
        return <button onClick={() => trackPay({ amount: 49.95 })}>Pay</button>;
      }
      ```
      
      ### Error boundary
      
      ```typescript
      import { AppInsightsErrorBoundary } from "@microsoft/applicationinsights-react-js";
      import { reactPlugin } from "./app-insights";
      
      <AppInsightsErrorBoundary appInsights={reactPlugin} onError={() => <FallbackUI />}>
        <App />
      </AppInsightsErrorBoundary>
      ```
      
      ### React Router v6+ (no `history` prop)
      
      `history v5+` doesn't ship with React Router v6. Bridge manually:
      
      ```typescript
      import { useEffect } from "react";
      import { useLocation } from "react-router-dom";
      import { appInsights } from "./app-insights";
      
      export function RouteTracker() {
        const loc = useLocation();
        useEffect(() => {
          appInsights.trackPageView({ name: loc.pathname, uri: loc.pathname + loc.search });
        }, [loc.pathname]);
        return null;
      }
      ```
      
      Set `enableAutoRouteTracking: false` so the SDK doesn't double-count.
      
      ## React Native (`@microsoft/applicationinsights-react-native`)
      
      ```bash
      npm i @microsoft/applicationinsights-web @microsoft/applicationinsights-react-native
      ```
      
      ```typescript
      import { ApplicationInsights } from "@microsoft/applicationinsights-web";
      import { ReactNativePlugin } from "@microsoft/applicationinsights-react-native";
      
      const rnPlugin = new ReactNativePlugin();
      export const appInsights = new ApplicationInsights({
        config: {
          connectionString: process.env.EXPO_PUBLIC_APPINSIGHTS_CONNECTION_STRING,
          extensions: [rnPlugin],
          disableFetchTracking: false,
          disableExceptionTracking: false
        }
      });
      appInsights.loadAppInsights();
      ```
      
      The plugin auto-collects device metadata (model, OS version, locale, network type) and unhandled JS exceptions. For native crashes, also use App Center / native Crashlytics — App Insights JS only sees the JS layer.
      
      ## Angular (`@microsoft/applicationinsights-angularplugin-js`)
      
      ```bash
      npm i @microsoft/applicationinsights-web @microsoft/applicationinsights-angularplugin-js
      ```
      
      ```typescript
      // app.module.ts
      import { NgModule, ErrorHandler } from "@angular/core";
      import { Router } from "@angular/router";
      import { ApplicationInsights } from "@microsoft/applicationinsights-web";
      import {
        AngularPlugin, ApplicationinsightsAngularpluginErrorService
      } from "@microsoft/applicationinsights-angularplugin-js";
      
      const angularPlugin = new AngularPlugin();
      const appInsights = new ApplicationInsights({
        config: {
          connectionString: environment.appInsightsConnectionString,
          extensions: [angularPlugin],
          extensionConfig: { [angularPlugin.identifier]: { router: undefined /* set in APP_INITIALIZER */ } }
        }
      });
      
      @NgModule({
        providers: [
          {
            provide: ErrorHandler,
            useClass: ApplicationinsightsAngularpluginErrorService
          },
          {
            provide: "APP_INSIGHTS_INIT", multi: true,
            useFactory: (router: Router) => () => {
              angularPlugin["_extensionConfig"]!.router = router;
              appInsights.loadAppInsights();
            },
            deps: [Router]
          }
        ]
      })
      export class AppModule {}
      ```
      
      Routes emit page views automatically via the `Router` reference; the custom `ErrorHandler` forwards uncaught Angular errors to `trackException`.
      
      ## Next.js (App Router, TypeScript)
      
      App Insights JS is browser-only. Initialize in a client component mounted at the root layout.
      
      ```typescript
      // app/_lib/app-insights.ts
      "use client";
      import { ApplicationInsights } from "@microsoft/applicationinsights-web";
      
      let _ai: ApplicationInsights | undefined;
      export function getAppInsights() {
        if (typeof window === "undefined") return undefined;
        if (_ai) return _ai;
        _ai = new ApplicationInsights({
          config: {
            connectionString: process.env.NEXT_PUBLIC_APPINSIGHTS_CONNECTION_STRING!,
            enableAutoRouteTracking: true
          }
        });
        _ai.loadAppInsights();
        _ai.trackPageView();
        return _ai;
      }
      ```
      
      ```typescript
      // app/_components/AppInsightsBootstrap.tsx
      "use client";
      import { useEffect } from "react";
      import { getAppInsights } from "@/app/_lib/app-insights";
      export function AppInsightsBootstrap() {
        useEffect(() => { getAppInsights(); }, []);
        return null;
      }
      ```
      
      ```typescript
      // app/layout.tsx
      import { AppInsightsBootstrap } from "./_components/AppInsightsBootstrap";
      export default function RootLayout({ children }: { children: React.ReactNode }) {
        return (
          <html lang="en">
            <body>
              <AppInsightsBootstrap />
              {children}
            </body>
          </html>
        );
      }
      ```
      
      Server-side telemetry in Next.js routes/server actions belongs in `@azure/monitor-opentelemetry` (Node), not here.
      
      ## Vite
      
      ```bash
      npm i @microsoft/applicationinsights-web
      ```
      
      ```typescript
      // src/lib/app-insights.ts
      import { ApplicationInsights } from "@microsoft/applicationinsights-web";
      export const appInsights = new ApplicationInsights({
        config: {
          connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
          enableAutoRouteTracking: true
        }
      });
      appInsights.loadAppInsights();
      ```
      
      ```typescript
      // src/main.tsx
      import "./lib/app-insights";  // side-effect import — initializes once
      import React from "react";
      import ReactDOM from "react-dom/client";
      import App from "./App";
      ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
      ```
      
      ## Click Analytics quick recipe
      
      ```bash
      npm i @microsoft/applicationinsights-clickanalytics-js
      ```
      
      ```typescript
      import { ClickAnalyticsPlugin } from "@microsoft/applicationinsights-clickanalytics-js";
      
      const click = new ClickAnalyticsPlugin();
      const appInsights = new ApplicationInsights({
        config: {
          connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
          extensions: [click],
          extensionConfig: {
            [click.identifier]: {
              autoCapture: true,
              dataTags: { useDefaultContentNameOrId: true, customDataPrefix: "data-ai-" }
            }
          }
        }
      });
      appInsights.loadAppInsights();
      ```
      
      Tag interactive elements:
      
      ```html
      <button data-ai-name="checkout-pay" data-ai-content-name="Pay $49.95">Pay</button>
      ```
      
      ## Debug plugin (dev only)
      
      ```bash
      npm i -D @microsoft/applicationinsights-debugplugin-js
      ```
      
      ```typescript
      import { DebugPlugin } from "@microsoft/applicationinsights-debugplugin-js";
      
      const extensions = [];
      if (import.meta.env.DEV) extensions.push(new DebugPlugin());
      new ApplicationInsights({ config: { connectionString, extensions } });
      ```
      
      Renders an in-page panel listing every telemetry envelope before send.
      
  • SKILL.md 20.4 KB
    ---
    name: applicationinsights-web-ts
    description: Instrument browser/web apps with the Application Insights JavaScript SDK (@microsoft/applicationinsights-web). Use for Real User Monitoring (RUM) — page views, clicks, AJAX/fetch dependencies, exceptions, custom events, and browser-side GenAI agent traces correlated to backend OpenTelemetry traces. Covers SDK Loader Script and npm setup, framework extensions (React, React Native, Angular), Click Analytics, telemetry initializers, and OTel GenAI semantic conventions for agent/tool/model spans emitted from the browser.
    license: MIT
    metadata:
      author: Microsoft
      version: "1.0.0"
      package: "@microsoft/applicationinsights-web"
    ---
    
    # Application Insights JavaScript SDK (Web) for TypeScript
    
    Real User Monitoring (RUM) for browser apps with `@microsoft/applicationinsights-web`. Auto-collects page views, AJAX/fetch dependencies, unhandled exceptions, and (with the Click Analytics plugin) clicks. Supports custom events, metrics, and **GenAI agent traces** that follow OpenTelemetry GenAI semantic conventions and correlate to backend spans via W3C Trace Context.
    
    > **Distinct from `azure-monitor-opentelemetry-ts`**, which is for Node.js server apps. This skill is for **browser/web** code (and React Native).
    
    ## Before Implementation
    
    Search `microsoft-docs` MCP for current API patterns:
    
    - Query: "Application Insights JavaScript SDK setup"
    - Query: "Application Insights JavaScript SDK configuration"
    - Query: "Application Insights JavaScript framework extensions React Angular"
    - Verify package version: `npm view @microsoft/applicationinsights-web version`
    
    ## Packages
    
    | Package | Purpose |
    | --- | --- |
    | `@microsoft/applicationinsights-web` | Core RUM SDK (page views, AJAX, exceptions). |
    | `@microsoft/applicationinsights-clickanalytics-js` | Auto-collect click telemetry. |
    | `@microsoft/applicationinsights-react-js` | React plugin (router instrumentation, hooks, HOC, ErrorBoundary). |
    | `@microsoft/applicationinsights-react-native` | React Native plugin (native crashes, sessions). |
    | `@microsoft/applicationinsights-angularplugin-js` | Angular plugin (router events, ErrorHandler). |
    | `@microsoft/applicationinsights-debugplugin-js` | Dev-only telemetry inspector. |
    | `@microsoft/applicationinsights-perfmarkmeasure-js` | User Timing (`performance.mark/measure`) integration. |
    
    ## Installation
    
    ```bash
    npm i --save @microsoft/applicationinsights-web
    # Optional plugins (install only what you use):
    npm i --save @microsoft/applicationinsights-clickanalytics-js
    npm i --save @microsoft/applicationinsights-react-js @microsoft/applicationinsights-react-native @microsoft/applicationinsights-angularplugin-js
    ```
    
    Typings ship with the package — no separate `@types/...` install needed.
    
    ## Connection String
    
    The browser SDK requires a connection string at init time. **It ships in plaintext to clients** — Microsoft Entra ID auth is not supported for browser telemetry. Use a separate App Insights resource with local auth enabled for browser RUM if you need to isolate it from backend telemetry.
    
    ```bash
    # Vite / CRA / Next.js — expose to client via the public env prefix
    VITE_APPINSIGHTS_CONNECTION_STRING="InstrumentationKey=...;IngestionEndpoint=https://...;LiveEndpoint=https://..."
    NEXT_PUBLIC_APPINSIGHTS_CONNECTION_STRING="InstrumentationKey=..."
    ```
    
    ## Quick Start (npm)
    
    ```typescript
    import { ApplicationInsights } from "@microsoft/applicationinsights-web";
    
    export const appInsights = new ApplicationInsights({
      config: {
        connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
        enableAutoRouteTracking: true,        // SPA route changes -> page views
        enableCorsCorrelation: true,          // propagate Request-Id / traceparent to cross-origin AJAX
        enableRequestHeaderTracking: true,
        enableResponseHeaderTracking: true,
        distributedTracingMode: 2,            // DistributedTracingModes.AI_AND_W3C — emit traceparent for backend correlation
        autoTrackPageVisitTime: true,
        disableFetchTracking: false,          // fetch() is auto-instrumented by default
        excludeRequestFromAutoTrackingPatterns: [/livemetrics\.azure\.com/i]
      }
    });
    
    appInsights.loadAppInsights();
    appInsights.trackPageView();
    ```
    
    Call `loadAppInsights()` exactly once, as early as possible (before user interactions you want tracked). Then `trackPageView()` for the initial load — when `enableAutoRouteTracking` is on, subsequent route changes are automatic.
    
    ## Quick Start (SDK Loader Script)
    
    Recommended when you want auto-updating SDK and zero build pipeline. Paste this as the **first** `<script>` in `<head>`:
    
    ```html
    <script type="text/javascript" src="https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js" crossorigin="anonymous"></script>
    <script type="text/javascript">
      var appInsights = window.appInsights || function (cfg) {
        /* See: https://learn.microsoft.com/azure/azure-monitor/app/javascript-sdk
           Use the latest snippet from the Microsoft Learn page above — it includes
           backup-CDN failover (cr), SDK-load-failure reporting, and the queue shim
           so calls before SDK ready are not lost. */
      }({ src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
          crossOrigin: "anonymous",
          cfg: { connectionString: "YOUR_CONNECTION_STRING" } });
    </script>
    ```
    
    Loader-only API (queued until SDK loads): `trackEvent`, `trackPageView`, `trackException`, `trackTrace`, `trackDependencyData`, `trackMetric`, `trackPageViewPerformance`, `startTrackPage`, `stopTrackPage`, `startTrackEvent`, `stopTrackEvent`, `addTelemetryInitializer`, `setAuthenticatedUserContext`, `clearAuthenticatedUserContext`, `flush`.
    
    ## Core Tracking APIs
    
    ```typescript
    // Page views (SPAs that disable enableAutoRouteTracking)
    appInsights.trackPageView({ name: "Checkout", uri: "/checkout", properties: { cartSize: 3 } });
    
    // Custom events (user actions, business events)
    appInsights.trackEvent({ name: "PurchaseCompleted" }, { orderId: "ord_123", amountUsd: 49.95 });
    
    // Exceptions (caught errors)
    try {
      await pay(order);
    } catch (err) {
      appInsights.trackException({ exception: err as Error, severityLevel: 3, properties: { orderId: order.id } });
    }
    
    // Traces (logs, severity 0=Verbose, 1=Info, 2=Warning, 3=Error, 4=Critical)
    appInsights.trackTrace({ message: "Cart hydrated from local storage", severityLevel: 1 });
    
    // Custom metrics (numeric)
    appInsights.trackMetric({ name: "checkout.duration_ms", average: 1234 });
    
    // Dependencies (manually-tracked outbound calls — fetch/XHR are auto-tracked)
    appInsights.trackDependencyData({
      id: crypto.randomUUID(),
      name: "GET /api/orders",
      duration: 87, success: true, responseCode: 200,
      data: "https://api.example.com/api/orders", target: "api.example.com", type: "Fetch"
    });
    
    // User identity (set ONCE per authenticated session — values are PII; do not pass emails)
    appInsights.setAuthenticatedUserContext("user-id-123", "tenant-456", /*storeInCookie*/ true);
    appInsights.clearAuthenticatedUserContext(); // on logout
    
    // Force send before unload
    appInsights.flush();
    ```
    
    ## Telemetry Initializers (enrichment & filtering)
    
    Run for every envelope before send. Return `false` to drop.
    
    ```typescript
    import type { ITelemetryItem } from "@microsoft/applicationinsights-web";
    
    appInsights.addTelemetryInitializer((item: ITelemetryItem) => {
      item.tags ??= {};
      item.tags["ai.cloud.role"] = "web-shop";
      item.tags["ai.cloud.roleInstance"] = window.location.hostname;
      item.data ??= {};
      item.data["app.version"] = import.meta.env.VITE_APP_VERSION;
      item.data["app.build"] = import.meta.env.VITE_BUILD_SHA;
    
      // Drop noisy health-check page views
      if (item.baseType === "PageviewData" && item.baseData?.uri?.endsWith("/healthz")) return false;
    
      // Scrub query-string secrets
      if (item.baseData?.uri) {
        item.baseData.uri = item.baseData.uri.replace(/([?&](token|sig|key)=)[^&]+/gi, "$1REDACTED");
      }
    });
    ```
    
    ## Click Analytics
    
    ```typescript
    import { ClickAnalyticsPlugin } from "@microsoft/applicationinsights-clickanalytics-js";
    
    const clickPlugin = new ClickAnalyticsPlugin();
    const appInsights = new ApplicationInsights({
      config: {
        connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
        extensions: [clickPlugin],
        extensionConfig: {
          [clickPlugin.identifier]: {
            autoCapture: true,
            dataTags: { useDefaultContentNameOrId: true, customDataPrefix: "data-ai-" },
            urlCollectHash: false,
            behaviorValidator: (b: string) => /^[a-z0-9_]+$/.test(b) ? b : ""
          }
        }
      }
    });
    appInsights.loadAppInsights();
    ```
    
    Mark elements with `data-ai-*` attributes; clicks are emitted as Custom Events with parent-content metadata.
    
    ## SPA Route Tracking
    
    - **Built-in:** set `enableAutoRouteTracking: true`. Hooks `history.pushState/replaceState` and `popstate`.
    - **React Router:** use `@microsoft/applicationinsights-react-js` `withAITracking` HOC (see [references/framework-extensions.md](references/framework-extensions.md)).
    - **Manual:** call `appInsights.trackPageView({ name, uri })` in your router's `useEffect` on route change. Disable `enableAutoRouteTracking` to avoid double counting.
    
    ## Distributed Tracing (correlate to backend)
    
    Set `distributedTracingMode: 2` (`DistributedTracingModes.AI_AND_W3C`). The SDK adds `traceparent` (and legacy `Request-Id`) to outbound `fetch`/`XHR`. Backends instrumented with **OpenTelemetry** (e.g. `@azure/monitor-opentelemetry`) auto-link to the browser's operation_Id.
    
    For cross-origin calls, also set `enableCorsCorrelation: true` and add the calling origin to the **CORS exposed headers** on the API.
    
    ## GenAI Agent Traces (OTel semantic conventions)
    
    When the browser invokes an AI agent (function-calling, tool-use, model calls direct from the client), emit App Insights **Dependency** telemetry whose attributes follow the OpenTelemetry **GenAI semantic conventions** so they are queryable alongside backend agent spans in App Insights / Log Analytics.
    
    **Set the opt-in env first** so backend instrumentations agree on the same schema version:
    
    ```bash
    OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
    ```
    
    ### Required attribute keys (use the OTel names verbatim)
    
    | Span / op | Required attributes |
    | --- | --- |
    | `invoke_agent {agent.name}` | `gen_ai.operation.name=invoke_agent`, `gen_ai.provider.name`, `gen_ai.agent.name`, `gen_ai.agent.id` (when known) |
    | `create_agent {agent.name}` | `gen_ai.operation.name=create_agent`, `gen_ai.provider.name`, `gen_ai.agent.name`, `gen_ai.request.model` |
    | `chat {model}` | `gen_ai.operation.name=chat`, `gen_ai.provider.name`, `gen_ai.request.model`, `gen_ai.response.model`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens` |
    | `execute_tool {tool.name}` | `gen_ai.operation.name=execute_tool`, `gen_ai.tool.name`, `gen_ai.tool.type` (`function` \| `extension` \| `datastore`), `gen_ai.tool.call.id` |
    
    `gen_ai.provider.name` well-known values: `openai`, `azure.ai.openai`, `azure.ai.inference`, `anthropic`, `aws.bedrock`, `gcp.gemini`, `gcp.vertex_ai`, `cohere`, `mistral_ai`, `groq`, `deepseek`, `perplexity`, `x_ai`, `ibm.watsonx.ai`.
    
    > **Sensitive content opt-in.** `gen_ai.system_instructions`, `gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result` are **Opt-In** by default. Gate them behind a runtime flag and avoid them in production unless you have approved data handling.
    
    ### Pattern: invoke_agent + nested tool/model spans
    
    ```typescript
    import { ApplicationInsights, SeverityLevel } from "@microsoft/applicationinsights-web";
    
    type GenAiAttrs = Record<string, string | number | boolean | undefined>;
    
    function startGenAiSpan(name: string, attrs: GenAiAttrs) {
      const id = crypto.randomUUID();
      const start = performance.now();
      const baseProps: GenAiAttrs = { "gen_ai.span.id": id, ...attrs };
      return {
        end(success: boolean, extra: GenAiAttrs = {}, error?: Error) {
          const duration = Math.round(performance.now() - start);
          const properties = { ...baseProps, ...extra };
          appInsights.trackDependencyData({
            id, name, duration, success,
            responseCode: error ? 500 : 200,
            type: "GenAI",
            target: String(attrs["gen_ai.provider.name"] ?? "genai"),
            properties: properties as Record<string, string>
          });
          if (error) {
            appInsights.trackException({
              exception: error,
              severityLevel: SeverityLevel.Error,
              properties: { ...properties, "error.type": error.name } as Record<string, string>
            });
          }
        }
      };
    }
    
    // Agent invocation
    const agentSpan = startGenAiSpan("invoke_agent ResearchAssistant", {
      "gen_ai.operation.name": "invoke_agent",
      "gen_ai.provider.name": "azure.ai.openai",
      "gen_ai.agent.name": "ResearchAssistant",
      "gen_ai.agent.id": "asst_5j66UpCpwteGg4YSxUnt7lPY",
      "gen_ai.request.model": "gpt-4o-mini",
      "server.address": "myresource.openai.azure.com"
    });
    
    try {
      // Nested chat completion span
      const chat = startGenAiSpan("chat gpt-4o-mini", {
        "gen_ai.operation.name": "chat",
        "gen_ai.provider.name": "azure.ai.openai",
        "gen_ai.request.model": "gpt-4o-mini"
      });
      const res = await callAzureOpenAi(/* ... */);
      chat.end(true, {
        "gen_ai.response.model": res.model,
        "gen_ai.response.id": res.id,
        "gen_ai.response.finish_reasons": JSON.stringify(res.choices.map(c => c.finish_reason)),
        "gen_ai.usage.input_tokens": res.usage.prompt_tokens,
        "gen_ai.usage.output_tokens": res.usage.completion_tokens,
        "gen_ai.output.type": "text"
      });
    
      // Nested tool execution span
      const tool = startGenAiSpan("execute_tool getWeather", {
        "gen_ai.operation.name": "execute_tool",
        "gen_ai.tool.name": "getWeather",
        "gen_ai.tool.type": "function",
        "gen_ai.tool.call.id": "call_abc123"
      });
      const toolResult = await runGetWeather({ location: "SF" });
      tool.end(true);
    
      agentSpan.end(true, {
        "gen_ai.usage.input_tokens": res.usage.prompt_tokens,
        "gen_ai.usage.output_tokens": res.usage.completion_tokens
      });
    } catch (err) {
      agentSpan.end(false, { "error.type": (err as Error).name }, err as Error);
    }
    ```
    
    The browser's `traceparent` is automatically attached to outbound `fetch` (when `distributedTracingMode: 2`), so downstream Azure OpenAI / agent backend spans hang under the same operation_Id in App Insights.
    
    For the full attribute reference, well-known values, and content-capture guidance, see [references/agent-traces.md](references/agent-traces.md).
    
    ### KQL: query GenAI traces in App Insights
    
    ```kusto
    dependencies
    | where type == "GenAI"
    | extend op   = tostring(customDimensions["gen_ai.operation.name"]),
             agent = tostring(customDimensions["gen_ai.agent.name"]),
             model = tostring(customDimensions["gen_ai.request.model"]),
             tin   = toint(customDimensions["gen_ai.usage.input_tokens"]),
             tout  = toint(customDimensions["gen_ai.usage.output_tokens"])
    | summarize calls=count(), p95_ms=percentile(duration, 95),
                avg_in=avg(tin), avg_out=avg(tout) by op, agent, model, bin(timestamp, 5m)
    ```
    
    ## React (TypeScript)
    
    See [references/framework-extensions.md](references/framework-extensions.md) for full React, React Native, Angular, Next.js, and Vite recipes.
    
    ```typescript
    import { ApplicationInsights } from "@microsoft/applicationinsights-web";
    import { ReactPlugin, withAITracking } from "@microsoft/applicationinsights-react-js";
    import { createBrowserHistory } from "history";
    
    const reactPlugin = new ReactPlugin();
    const browserHistory = createBrowserHistory();
    
    export const appInsights = new ApplicationInsights({
      config: {
        connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,
        extensions: [reactPlugin],
        extensionConfig: { [reactPlugin.identifier]: { history: browserHistory } }
      }
    });
    appInsights.loadAppInsights();
    
    export const TrackedCheckout = withAITracking(reactPlugin, Checkout, "Checkout");
    ```
    
    ## React Native
    
    ```typescript
    import { ApplicationInsights } from "@microsoft/applicationinsights-web";
    import { ReactNativePlugin } from "@microsoft/applicationinsights-react-native";
    
    const rnPlugin = new ReactNativePlugin();
    const appInsights = new ApplicationInsights({
      config: {
        connectionString: process.env.EXPO_PUBLIC_APPINSIGHTS_CONNECTION_STRING,
        extensions: [rnPlugin],
        disableFetchTracking: false
      }
    });
    appInsights.loadAppInsights();
    ```
    
    ## Performance — Web Vitals
    
    Auto-collected: page-load timings via `PerformanceTiming` / `PerformanceNavigationTiming`. To add Core Web Vitals:
    
    ```typescript
    import { onCLS, onLCP, onINP, type Metric } from "web-vitals";
    
    function send(m: Metric) {
      appInsights.trackMetric(
        { name: `web_vitals.${m.name.toLowerCase()}`, average: m.value },
        { rating: m.rating, navigationType: m.navigationType, id: m.id }
      );
    }
    onCLS(send); onLCP(send); onINP(send);
    ```
    
    ## Cookies & Privacy
    
    ```typescript
    new ApplicationInsights({ config: {
      connectionString,
      isCookieUseDisabled: true,         // hard-disable all cookies
      cookieCfg: { enabled: true, domain: ".example.com", path: "/", expiry: 365 }
    }});
    ```
    
    To honor consent dynamically:
    
    ```typescript
    appInsights.getCookieMgr().setEnabled(userGaveConsent);
    appInsights.config.disableTelemetry = !userGaveConsent;
    ```
    
    ## Sampling
    
    Server-side ingestion sampling (recommended) is configured on the App Insights resource. SDK-side sampling reduces network use:
    
    ```typescript
    new ApplicationInsights({ config: { connectionString, samplingPercentage: 50 } });
    ```
    
    Per-type sampling via telemetry initializer: drop with `return false` based on `item.baseType`.
    
    ## Offline / Send-on-Unload
    
    The SDK uses `sendBeacon` (default `onunloadDisableBeacon: false`) to flush on `pagehide` / `unload`. For SPAs, also call `appInsights.flush()` before destructive transitions (logout, hard reload).
    
    ## Common Pitfalls
    
    1. **Do not initialize twice.** Re-importing the module under different bundles produces duplicate page views. Use a single shared module export.
    2. **Initialize before first user input** to avoid losing early clicks/exceptions.
    3. **Connection string is public** — never reuse the same App Insights resource for backend secrets.
    4. **`enableAutoRouteTracking` + manual `trackPageView`** = duplicates. Pick one.
    5. **CORS distributed tracing** requires the API to allow `Request-Id`, `Request-Context`, `traceparent`, `tracestate` request headers and expose `Request-Context` response header.
    6. **GenAI sensitive content** (`gen_ai.input.messages` etc.) is Opt-In — never log without an explicit runtime flag and approved data handling.
    7. **Agent token usage is on `chat` spans, not `invoke_agent`** — copy aggregated usage to the parent agent span only if you know it.
    8. **React StrictMode** double-invokes effects in dev — guard `loadAppInsights()` with a module-level singleton.
    
    ## Bundle Size
    
    The full web SDK is ~110 KB minified (~36 KB gzipped). For aggressive budgets, use the **Loader Script** path so the SDK loads asynchronously off the critical path, or tree-shake unused plugins.
    
    ## Key Types
    
    ```typescript
    import {
      ApplicationInsights,
      SeverityLevel,
      DistributedTracingModes,
      type IConfiguration,
      type IConfig,
      type ITelemetryItem,
      type ITelemetryPlugin,
      type ICustomProperties,
      type IPageViewTelemetry,
      type IEventTelemetry,
      type IExceptionTelemetry,
      type ITraceTelemetry,
      type IMetricTelemetry,
      type IDependencyTelemetry
    } from "@microsoft/applicationinsights-web";
    ```
    
    ## Best Practices
    
    1. **One singleton instance** exported from a single module.
    2. **Initialize early** in the app entrypoint, before router setup.
    3. **Use telemetry initializers** to attach `app.version`, `tenantId`, and to scrub PII / query-string secrets.
    4. **Set `distributedTracingMode: 2`** and ensure your APIs accept/expose W3C trace context headers.
    5. **For GenAI**, follow OTel `gen_ai.*` attribute names verbatim — they are queryable across browser and backend telemetry uniformly.
    6. **Gate sensitive content capture** (`gen_ai.input.messages` / `gen_ai.output.messages`) behind a build-time or runtime opt-in.
    7. **Flush on logout / sensitive navigation** so in-flight telemetry isn't dropped.
    
    ## References
    
    - [references/agent-traces.md](references/agent-traces.md) — Full OTel GenAI semconv distilled (agent / model / tool spans, attributes, content capture).
    - [references/framework-extensions.md](references/framework-extensions.md) — React, React Native, Angular, Next.js, Vite recipes.
    - [references/configuration.md](references/configuration.md) — Full `IConfiguration` reference and tuning guide.
    - Microsoft Learn: <https://learn.microsoft.com/azure/azure-monitor/app/javascript-sdk>
    - ApplicationInsights-JS source: <https://github.com/microsoft/ApplicationInsights-JS>
    - OTel GenAI semantic conventions: <https://opentelemetry.io/docs/specs/semconv/gen-ai/>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related