Application Insights Web TypeScript

applicationinsights-web-ts
分类编程
作者Agentic Awesome Skills 社区
许可MIT
评分4.40/5
使用13.4K

用于 TypeScript 的 Application Insights JavaScript SDK (Web)

使用场景

当你需要使用 Application Insights JavaScript SDK (@microsoft/applicationinsights-web) 对浏览器/Web 应用进行插桩时,请使用此技能。适用于真实用户监控 (RUM) —— 包括页面浏览量、点击、AJAX/fetch 依赖、异常、自定义事件,以及与后端关联的浏览器端 GenAI 代理追踪...

通过 @microsoft/applicationinsights-web 实现浏览器应用的真实用户监控 (RUM)。自动收集页面浏览量、AJAX/fetch 依赖、未处理的异常,以及(配合 Click Analytics 插件)点击事件。支持自定义事件、指标以及遵循 OpenTelemetry GenAI 语义约定并通过 W3C Trace Context 与后端 Span 关联的 GenAI 代理追踪

> azure-monitor-opentelemetry-ts 不同,后者用于 Node.js 服务器应用。本技能适用于 浏览器/Web 代码(以及 React Native)。

实现前准备

microsoft-docs MCP 中搜索当前的 API 模式:

  • 查询:"Application Insights JavaScript SDK setup"
  • 查询:"Application Insights JavaScript SDK configuration"
  • 查询:"Application Insights JavaScript framework extensions React Angular"
  • 验证包版本:npm view @microsoft/applicationinsights-web version

软件包

| 软件包 | 用途 |
| --- | --- |
| @microsoft/applicationinsights-web | 核心 RUM SDK(页面浏览量、AJAX、异常)。 |
| @microsoft/applicationinsights-clickanalytics-js | 自动收集点击遥测。 |
| @microsoft/applicationinsights-react-js | React 插件(路由插桩、hooks、HOC、ErrorBoundary)。 |
| @microsoft/applicationinsights-react-native | React Native 插件(原生崩溃、会话)。 |
| @microsoft/applicationinsights-angularplugin-js | Angular 插件(路由事件、ErrorHandler)。 |
| @microsoft/applicationinsights-debugplugin-js | 仅限开发环境的遥测检查器。 |
| @microsoft/applicationinsights-perfmarkmeasure-js | 用户计时 (performance.mark/measure) 集成。 |

安装

bash
npm i --save @microsoft/applicationinsights-web

可选插件(仅安装所需的):

npm i --save @microsoft/applicationinsights-clickanalytics-js npm i --save @microsoft/applicationinsights-react-js @microsoft/applicationinsights-react-native @microsoft/applicationinsights-angularplugin-js

类型定义随包附带 —— 无需单独安装 @types/...

连接字符串

浏览器 SDK 在初始化时需要连接字符串。该字符串会以明文形式发送给客户端 —— 浏览器遥测不支持 Microsoft Entra ID 身份验证。如果你需要将浏览器 RUM 与后端遥测隔离,请使用一个启用了本地身份验证的独立 App Insights 资源。

bash
# Vite / CRA / Next.js — 通过公共环境变量前缀暴露给客户端
VITE_APPINSIGHTS_CONNECTION_STRING="InstrumentationKey=...;IngestionEnd
point=https://...;LiveEndpoint=https://..." NEXT_PUBLIC_APPINSIGHTS_CONNECTION_STRING="InstrumentationKey=..."
code
## 快速上手 (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 路由变更 -> 页面浏览量
enableCorsCorrelation: true, // 将 Request-Id / traceparent 传递给跨域 AJAX
enableRequestHeaderTracking: true,
enableResponseHeaderTracking: true,
distributedTracingMode: 2, // DistributedTracingModes.AI_AND_W3C — 发送 traceparent 以进行后端关联
autoTrackPageVisitTime: true,
disableFetchTracking: false, // 默认自动监测 fetch()
excludeRequestFromAutoTrackingPatterns: [/livemetrics\.azure\.com/i]
}
});

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

code
请确保 loadAppInsights() 仅被调用一次,且尽可能早地调用(在需要追踪的用户交互之前)。然后调用 trackPageView() 记录初始加载 —— 当 enableAutoRouteTracking 开启时,随后的路由变更将自动记录。

快速上手 (SDK 加载脚本)

推荐用于需要 SDK 自动更新且无需构建流水线的场景。请将此代码作为 <head> 中的第一个 <script> 标签粘贴:

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) {
/* 详见: https://learn.microsoft.com/azure/azure-monitor/app/javascript-sdk
请使用上述 Microsoft Learn 页面上的最新代码片段 —— 它包含了
备份 CDN 故障转移 (cr)、SDK 加载失败报告以及队列垫片 (queue shim),
以确保在 SDK 就绪之前的调用不会丢失。 */
}({ src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
crossOrigin: "anonymous",
cfg: { connectionString: "YOUR_CONNECTION_STRING" } });
</script>
code
仅限加载器的 API(在 SDK 加载前进入队列):trackEvent, trackPageView, trackException, trackTrace, trackDependencyData, trackMetric, trackPageViewPerformance, startTrackPage, stopTrackPage, startTrackEvent, stopTrackEvent, addTelemetryInitializer, setAuthenticatedUserContext, clearAuthenticatedUserContext, flush

核心追踪 API

typescript // 页面浏览量 (适用于禁用 enableAutoRouteTracking 的 SPA) appInsights.trackPageView({ name: "Checkout", uri: "/checkout", properties: { cartSize: 3 } });

// 自定义事件 (用户操作、业务事件)
appInsights.trackEvent({ name: "PurchaseCompleted" }, { orderId: "ord_123", amountUsd: 49.95 });

// 异常 (捕获的错误)
try {
await pay(order);
} catch (err) {
appInsights.trackException({ exception: err as Error, severityLevel: 3, properties: { orderId: order.id } });
}

// 跟踪日志 (级别 0=详细, 1=信息, 2=警告, 3=错误, 4=严重)
appInsights.trackTrace({ message: "Cart hydrated from local storage", severityLevel: 1 });

// 自定义指标 (数值)
appInsights.trackMetric({ name: "checkout.duration_ms", average: 1234 });

// 依赖项 (手动追踪的出站调用 —— fetch/XHR 已自动追踪)
appInsights.trackDependencyData({
id: crypto.randomUUID(),
name: "GET /api/orders",
duration: 87, success: true, responseCode: 200,
data: "https://api.example.com/api/orders", tar

code
get: "api.example.com", type: "Fetch"
});

// 用户身份(每个认证会话仅设置一次 —— 值为 PII;请勿传递电子邮件)
appInsights.setAuthenticatedUserContext("user-id-123", "tenant-456", /*storeInCookie*/ true);
appInsights.clearAuthenticatedUserContext(); // 登出时调用

// 在卸载前强制发送
appInsights.flush();

遥测初始化程序 (Telemetry Initializers)(增强与过滤)

在发送每个信封 (envelope) 之前运行。返回 false 则丢弃。

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_APP_BUILD_SHA;

// 丢弃嘈杂的健康检查页面浏览量
if (item.baseType === "PageviewData" && item.baseData?.uri?.endsWith("/healthz")) return false;

// 清洗查询字符串中的机密信息
if (item.baseData?.uri) {
item.baseData.uri = item.baseData.uri.replace(/(?&=)[^&]+/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();

使用 data-ai-* 属性标记元素;点击事件将作为带有父级内容元数据的自定义事件 (Custom Events) 发出。

SPA 路由跟踪

  • 内置: 设置 enableAutoRouteTracking: true。它会挂钩 history.pushState/replaceStatepopstate
  • 手动: 在路由器的 useEffect 中于路由更改时调用 appInsights.trackPageView({ name, uri })。禁用 enableAutoRouteTracking 以避免重复计数。

分布式追踪 (Distributed Tracing)(与后端关联)

设置 distributedTracingMode: 2 (DistributedTracingModes.AI_AND_W3C)。SDK 会在发出的 fetch/XHR 请求中添加 traceparent(以及旧版的 Request-Id)。使用 OpenTelemetry(例如 @azure/monitor-opentelemetry)进行插桩的后端会自动链接到浏览器的 operation_Id

对于跨域调用,还需设置 enableCorsCorrelation: true,并在 API 的 CORS 暴露头 (exposed headers) 中添加调用源。

GenAI Agent 追踪 (OTel 语义约定)

当浏览器调用 AI Agent(函数调用、工具使用、直接从客户端调用模型)时,发出符合 OpenTelemetry GenAI 语义约定 的 App Insights Dependency 遥测,以便在 App Insights / Log Analytics 中与后端 Agent 的 span 一起进行查询。

设置选择性加入的端...
首先,请确保后端 instrumentation 统一使用相同的 schema 版本:

bash
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental

必需的属性键(请原样使用 OTel 名称)

| Span / 操作 | 必需属性 |
| --- | --- |
| invoke_agent {agent.name} | gen_ai.operation.name=invoke_agent, gen_ai.provider.name, gen_ai.agent.name, gen_ai.agent.id (已知时) |
| 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 的常用值: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

> 敏感内容启用。 gen_ai.system_instructions, gen_ai.input.messages, gen_ai.output.messages, gen_ai.tool.call.arguments, gen_ai.tool.call.result 默认是 Opt-In(需手动启用) 的。请通过运行时标志(runtime flag)进行控制,除非已通过数据处理审核,否则请避免在生产环境中使用。

模式:invoke_agent + 嵌套的 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 调用
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 {
// 嵌套的聊天补全 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_token


s": res.usage.completion_tokens,
"gen_ai.output.type": "text"
});

// 嵌套工具执行 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);
}

code
浏览器的 traceparent 会自动附加到出站 fetch 请求中(当 distributedTracingMode: 2 时),因此下游的 Azure OpenAI / agent 后端 span 将在 App Insights 中挂在同一个 operation_Id 之下。

有关完整的属性参考、常用值和内容捕获指南,请参阅 references/agent-traces.md

KQL:在 App Insights 中查询 GenAI 追踪

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)
code
## React (TypeScript)

有关 React, React Native, Angular, Next.js 和 Vite 的完整方案,请参阅 references/framework-extensions.md

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");

code
## 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();

code
## 性能 — Web Vitals

自动收集:通过 PerformanceTiming / PerformanceNavigationTiming 收集页面加载时间。如需添加 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.

code
value },
{ rating: m.rating, navigationType: m.navigationType, id: m.id }
);
}
onCLS(send); onLCP(send); onINP(send);

Cookie 与隐私

typescript
new ApplicationInsights({ config: {
  connectionString,
  isCookieUseDisabled: true,         // 强制禁用所有 Cookie
  cookieCfg: { enabled: true, domain: ".example.com", path: "/", expiry: 365 }
}});

动态处理用户同意:

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

采样 (Sampling)

服务端接收采样(推荐)在 App Insights 资源中配置。SDK 端采样可减少网络带宽占用:

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

可通过遥测初始化程序 (telemetry initializer) 实现分类型采样:根据 item.baseType 返回 false 以丢弃数据。

离线 / 卸载时发送

SDK 使用 sendBeacon(默认 onunloadDisableBeacon: false)在 pagehide / unload 时刷新数据。对于单页应用 (SPA),在执行破坏性跳转(如登出、强制刷新)前,请调用 appInsights.flush()

常见陷阱

1. 避免重复初始化:在不同 bundle 中重复导入模块会导致页面浏览量 (page views) 重复统计。请使用统一的共享模块导出。
2. 在首次用户输入前初始化:避免丢失早期的点击或异常记录。
3. 连接字符串是公开的:切勿将同一个 App Insights 资源用于存储后端机密。
4. enableAutoRouteTracking 与手动 trackPageView 共存:会导致重复统计。请二选一。
5. CORS 分布式追踪:要求 API 允许 Request-IdRequest-Contexttraceparenttracestate 请求头,并公开 Request-Context 响应头。
6. GenAI 敏感内容(如 gen_ai.input.messages 等)采用 Opt-In 机制:在没有明确的运行时标志和经批准的数据处理流程前,请勿记录。
7. Agent Token 使用量记录在 chat span 而非 invoke_agent:仅在确定时才将聚合使用量复制到父级 agent span。
8. React StrictMode:在开发环境下会双次调用 effect —— 请使用模块级单例来保护 loadAppInsights()

包体积

完整的 Web SDK 压缩后约为 110 KB(Gzip 后约为 36 KB)。对于预算严格的项目,请使用 Loader Script 方案使 SDK 在非关键路径异步加载,或通过 Tree-shaking 剔除未使用的插件。

关键类型

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";

最佳实践

1. 单例模式:从单个模块导出唯一的单例实例。
2. 尽早初始化:在应用入口处、路由设置之前进行初始化。
3. 使用遥测初始化程序:用于附加 app.versiontenantId 以及清洗 PII(个人可识别信息)或查询字符串中的机密。
4. 设置 distributedTracingMode: 2:并确保 API 接受/公开 W3C 追踪上下文头。
5. 针对 GenAI:严格遵守 OTel gen_ai.* 属性命名,以便在浏览器和后端遥测中统一查询。
6. 敏感内容捕获门控:将 gen_ai.input.messages / gen_ai.output.messages 的捕获置于构建时或运行时的 Opt-in 标志之后。
7. 在登出/敏感导航时刷新:确保在途的遥测数据不会丢失。

参考资料

  • [agent-traces.md — 完整的 OTel GenAI 语义约定精简版(包含 agent / model / tool span、属性及内容捕获)。
  • Microsoft Learn: <https://learn.microsoft.com/azure/azure-monitor/app/javascript-sdk>
  • ApplicationInsights-JS 源码: <https://github.com/microsoft/ApplicationInsights-JS>
  • OTel GenAI 语义约定: <https://opentelemetry.io/docs/specs/semconv/gen-ai/>

局限性

  • 仅在任务与上游源码及本地项目上下文明确匹配时使用此技能。
  • 在应用更改前,请验证命令、生成的代码、依赖项、凭据以及外部服务的行为。
  • 不要将示例视为环境特定测试、安全审查或破坏性/高成本操作用户确认的替代方案。
\n\n```\n\n仅限加载器的 API(在 SDK 加载前进入队列):`trackEvent`, `trackPageView`, `trackException`, `trackTrace`, `trackDependencyData`, `trackMetric`, `trackPageViewPerformance`, `startTrackPage`, `stopTrackPage`, `startTrackEvent`, `stopTrackEvent`, `addTelemetryInitializer`, `setAuthenticatedUserContext`, `clearAuthenticatedUserContext`, `flush`。\n\n## 核心追踪 API\n\n```typescript\n// 页面浏览量 (适用于禁用 enableAutoRouteTracking 的 SPA)\nappInsights.trackPageView({ name: \"Checkout\", uri: \"/checkout\", properties: { cartSize: 3 } });\n\n// 自定义事件 (用户操作、业务事件)\nappInsights.trackEvent({ name: \"PurchaseCompleted\" }, { orderId: \"ord_123\", amountUsd: 49.95 });\n\n// 异常 (捕获的错误)\ntry {\n await pay(order);\n} catch (err) {\n appInsights.trackException({ exception: err as Error, severityLevel: 3, properties: { orderId: order.id } });\n}\n\n// 跟踪日志 (级别 0=详细, 1=信息, 2=警告, 3=错误, 4=严重)\nappInsights.trackTrace({ message: \"Cart hydrated from local storage\", severityLevel: 1 });\n\n// 自定义指标 (数值)\nappInsights.trackMetric({ name: \"checkout.duration_ms\", average: 1234 });\n\n// 依赖项 (手动追踪的出站调用 —— fetch/XHR 已自动追踪)\nappInsights.trackDependencyData({\n id: crypto.randomUUID(),\n name: \"GET /api/orders\",\n duration: 87, success: true, responseCode: 200,\n data: \"https://api.example.com/api/orders\", tar\n```\nget: \"api.example.com\", type: \"Fetch\"\n});\n\n// 用户身份(每个认证会话仅设置一次 —— 值为 PII;请勿传递电子邮件)\nappInsights.setAuthenticatedUserContext(\"user-id-123\", \"tenant-456\", /*storeInCookie*/ true);\nappInsights.clearAuthenticatedUserContext(); // 登出时调用\n\n// 在卸载前强制发送\nappInsights.flush();\n```\n\n## 遥测初始化程序 (Telemetry Initializers)(增强与过滤)\n\n在发送每个信封 (envelope) 之前运行。返回 `false` 则丢弃。\n\n```typescript\nimport type { ITelemetryItem } from \"@microsoft/applicationinsights-web\";\n\nappInsights.addTelemetryInitializer((item: ITelemetryItem) => {\n item.tags ??= {};\n item.tags[\"ai.cloud.role\"] = \"web-shop\";\n item.tags[\"ai.cloud.roleInstance\"] = window.location.hostname;\n item.data ??= {};\n item.data[\"app.version\"] = import.meta.env.VITE_APP_VERSION;\n item.data[\"app.build\"] = import.meta.env.VITE_APP_BUILD_SHA;\n\n // 丢弃嘈杂的健康检查页面浏览量\n if (item.baseType === \"PageviewData\" && item.baseData?.uri?.endsWith(\"/healthz\")) return false;\n\n // 清洗查询字符串中的机密信息\n if (item.baseData?.uri) {\n item.baseData.uri = item.baseData.uri.replace(/([?&](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/token|sig|key)=)[^&]+/gi, \"$1REDACTED\");\n }\n});\n```\n\n## 点击分析 (Click Analytics)\n\n```typescript\nimport { ClickAnalyticsPlugin } from \"@microsoft/applicationinsights-clickanalytics-js\";\n\nconst clickPlugin = new ClickAnalyticsPlugin();\nconst appInsights = new ApplicationInsights({\n config: {\n connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,\n extensions: [clickPlugin],\n extensionConfig: {\n [clickPlugin.identifier]: {\n autoCapture: true,\n dataTags: { useDefaultContentNameOrId: true, customDataPrefix: \"data-ai-\" },\n urlCollectHash: false,\n behaviorValidator: (b: string) => /^[a-z0-9_]+$/.test(b) ? b : \"\"\n }\n }\n }\n});\nappInsights.loadAppInsights();\n```\n\n使用 `data-ai-*` 属性标记元素;点击事件将作为带有父级内容元数据的自定义事件 (Custom Events) 发出。\n\n## SPA 路由跟踪\n\n- **内置:** 设置 `enableAutoRouteTracking: true`。它会挂钩 `history.pushState/replaceState` 和 `popstate`。\n- **React Router:** 使用 `@microsoft/applicationinsights-react-js` 的 `withAITracking` HOC(参见 [references/framework-extensions.md](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/references/framework-extensions.md))。\n- **手动:** 在路由器的 `useEffect` 中于路由更改时调用 `appInsights.trackPageView({ name, uri })`。禁用 `enableAutoRouteTracking` 以避免重复计数。\n\n## 分布式追踪 (Distributed Tracing)(与后端关联)\n\n设置 `distributedTracingMode: 2` (`DistributedTracingModes.AI_AND_W3C`)。SDK 会在发出的 `fetch`/`XHR` 请求中添加 `traceparent`(以及旧版的 `Request-Id`)。使用 **OpenTelemetry**(例如 `@azure/monitor-opentelemetry`)进行插桩的后端会自动链接到浏览器的 `operation_Id`。\n\n对于跨域调用,还需设置 `enableCorsCorrelation: true`,并在 API 的 **CORS 暴露头 (exposed headers)** 中添加调用源。\n\n## GenAI Agent 追踪 (OTel 语义约定)\n\n当浏览器调用 AI Agent(函数调用、工具使用、直接从客户端调用模型)时,发出符合 OpenTelemetry **GenAI 语义约定** 的 App Insights **Dependency** 遥测,以便在 App Insights / Log Analytics 中与后端 Agent 的 span 一起进行查询。\n\n**设置选择性加入的端...**\n首先,请确保后端 instrumentation 统一使用相同的 schema 版本:\n\n```bash\nOTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental\n```\n\n### 必需的属性键(请原样使用 OTel 名称)\n\n| Span / 操作 | 必需属性 |\n| --- | --- |\n| `invoke_agent {agent.name}` | `gen_ai.operation.name=invoke_agent`, `gen_ai.provider.name`, `gen_ai.agent.name`, `gen_ai.agent.id` (已知时) |\n| `create_agent {agent.name}` | `gen_ai.operation.name=create_agent`, `gen_ai.provider.name`, `gen_ai.agent.name`, `gen_ai.request.model` |\n| `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` |\n| `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` |\n\n`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`。\n\n> **敏感内容启用。** `gen_ai.system_instructions`, `gen_ai.input.messages`, `gen_ai.output.messages`, `gen_ai.tool.call.arguments`, `gen_ai.tool.call.result` 默认是 **Opt-In(需手动启用)** 的。请通过运行时标志(runtime flag)进行控制,除非已通过数据处理审核,否则请避免在生产环境中使用。\n\n### 模式:invoke_agent + 嵌套的 tool/model spans\n\n```typescript\nimport { ApplicationInsights, SeverityLevel } from \"@microsoft/applicationinsights-web\";\n\ntype GenAiAttrs = Record;\n\nfunction startGenAiSpan(name: string, attrs: GenAiAttrs) {\n const id = crypto.randomUUID();\n const start = performance.now();\n const baseProps: GenAiAttrs = { \"gen_ai.span.id\": id, ...attrs };\n return {\n end(success: boolean, extra: GenAiAttrs = {}, error?: Error) {\n const duration = Math.round(performance.now() - start);\n const properties = { ...baseProps, ...extra };\n appInsights.trackDependencyData({\n id, name, duration, success,\n responseCode: error ? 500 : 200,\n type: \"GenAI\",\n target: String(attrs[\"gen_ai.provider.name\"] ?? \"genai\"),\n properties: properties as Record\n });\n if (error) {\n appInsights.trackException({\n exception: error,\n severityLevel: SeverityLevel.Error,\n properties: { ...properties, \"error.type\": error.name } as Record\n });\n }\n }\n };\n}\n\n// Agent 调用\nconst agentSpan = startGenAiSpan(\"invoke_agent ResearchAssistant\", {\n \"gen_ai.operation.name\": \"invoke_agent\",\n \"gen_ai.provider.name\": \"azure.ai.openai\",\n \"gen_ai.agent.name\": \"ResearchAssistant\",\n \"gen_ai.agent.id\": \"asst_5j66UpCpwteGg4YSxUnt7lPY\",\n \"gen_ai.request.model\": \"gpt-4o-mini\",\n \"server.address\": \"myresource.openai.azure.com\"\n});\n\ntry {\n // 嵌套的聊天补全 span\n const chat = startGenAiSpan(\"chat gpt-4o-mini\", {\n \"gen_ai.operation.name\": \"chat\",\n \"gen_ai.provider.name\": \"azure.ai.openai\",\n \"gen_ai.request.model\": \"gpt-4o-mini\"\n });\n const res = await callAzureOpenAi(/* ... */);\n chat.end(true, {\n \"gen_ai.response.model\": res.model,\n \"gen_ai.response.id\": res.id,\n \"gen_ai.response.finish_reasons\": JSON.stringify(res.choices.map(c => c.finish_reason)),\n \"gen_ai.usage.input_tokens\": res.usage.prompt_tokens,\n \"gen_ai.usage.output_token\n```\ns\": res.usage.completion_tokens,\n \"gen_ai.output.type\": \"text\"\n });\n\n // 嵌套工具执行 span\n const tool = startGenAiSpan(\"execute_tool getWeather\", {\n \"gen_ai.operation.name\": \"execute_tool\",\n \"gen_ai.tool.name\": \"getWeather\",\n \"gen_ai.tool.type\": \"function\",\n \"gen_ai.tool.call.id\": \"call_abc123\"\n });\n const toolResult = await runGetWeather({ location: \"SF\" });\n tool.end(true);\n\n agentSpan.end(true, {\n \"gen_ai.usage.input_tokens\": res.usage.prompt_tokens,\n \"gen_ai.usage.output_tokens\": res.usage.completion_tokens\n });\n} catch (err) {\n agentSpan.end(false, { \"error.type\": (err as Error).name }, err as Error);\n}\n```\n\n浏览器的 `traceparent` 会自动附加到出站 `fetch` 请求中(当 `distributedTracingMode: 2` 时),因此下游的 Azure OpenAI / agent 后端 span 将在 App Insights 中挂在同一个 operation_Id 之下。\n\n有关完整的属性参考、常用值和内容捕获指南,请参阅 [references/agent-traces.md](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/references/agent-traces.md)。\n\n### KQL:在 App Insights 中查询 GenAI 追踪\n\n```kusto\ndependencies\n| where type == \"GenAI\"\n| extend op = tostring(customDimensions[\"gen_ai.operation.name\"]),\n agent = tostring(customDimensions[\"gen_ai.agent.name\"]),\n model = tostring(customDimensions[\"gen_ai.request.model\"]),\n tin = toint(customDimensions[\"gen_ai.usage.input_tokens\"]),\n tout = toint(customDimensions[\"gen_ai.usage.output_tokens\"])\n| summarize calls=count(), p95_ms=percentile(duration, 95),\n avg_in=avg(tin), avg_out=avg(tout) by op, agent, model, bin(timestamp, 5m)\n```\n\n## React (TypeScript)\n\n有关 React, React Native, Angular, Next.js 和 Vite 的完整方案,请参阅 [references/framework-extensions.md](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/references/framework-extensions.md)。\n\n```typescript\nimport { ApplicationInsights } from \"@microsoft/applicationinsights-web\";\nimport { ReactPlugin, withAITracking } from \"@microsoft/applicationinsights-react-js\";\nimport { createBrowserHistory } from \"history\";\n\nconst reactPlugin = new ReactPlugin();\nconst browserHistory = createBrowserHistory();\n\nexport const appInsights = new ApplicationInsights({\n config: {\n connectionString: import.meta.env.VITE_APPINSIGHTS_CONNECTION_STRING,\n extensions: [reactPlugin],\n extensionConfig: { [reactPlugin.identifier]: { history: browserHistory } }\n }\n});\nappInsights.loadAppInsights();\n\nexport const TrackedCheckout = withAITracking(reactPlugin, Checkout, \"Checkout\");\n```\n\n## React Native\n\n```typescript\nimport { ApplicationInsights } from \"@microsoft/applicationinsights-web\";\nimport { ReactNativePlugin } from \"@microsoft/applicationinsights-react-native\";\n\nconst rnPlugin = new ReactNativePlugin();\nconst appInsights = new ApplicationInsights({\n config: {\n connectionString: process.env.EXPO_PUBLIC_APPINSIGHTS_CONNECTION_STRING,\n extensions: [rnPlugin],\n disableFetchTracking: false\n }\n});\nappInsights.loadAppInsights();\n```\n\n## 性能 — Web Vitals\n\n自动收集:通过 `PerformanceTiming` / `PerformanceNavigationTiming` 收集页面加载时间。如需添加 Core Web Vitals:\n\n```typescript\nimport { onCLS, onLCP, onINP, type Metric } from \"web-vitals\";\n\nfunction send(m: Metric) {\n appInsights.trackMetric(\n { name: `web_vitals.${m.name.toLowerCase()}`, average: m.\n```\nvalue },\n { rating: m.rating, navigationType: m.navigationType, id: m.id }\n );\n}\nonCLS(send); onLCP(send); onINP(send);\n```\n\n## Cookie 与隐私\n\n```typescript\nnew ApplicationInsights({ config: {\n connectionString,\n isCookieUseDisabled: true, // 强制禁用所有 Cookie\n cookieCfg: { enabled: true, domain: \".example.com\", path: \"/\", expiry: 365 }\n}});\n```\n\n动态处理用户同意:\n\n```typescript\nappInsights.getCookieMgr().setEnabled(userGaveConsent);\nappInsights.config.disableTelemetry = !userGaveConsent;\n```\n\n## 采样 (Sampling)\n\n服务端接收采样(推荐)在 App Insights 资源中配置。SDK 端采样可减少网络带宽占用:\n\n```typescript\nnew ApplicationInsights({ config: { connectionString, samplingPercentage: 50 } });\n```\n\n可通过遥测初始化程序 (telemetry initializer) 实现分类型采样:根据 `item.baseType` 返回 `false` 以丢弃数据。\n\n## 离线 / 卸载时发送\n\nSDK 使用 `sendBeacon`(默认 `onunloadDisableBeacon: false`)在 `pagehide` / `unload` 时刷新数据。对于单页应用 (SPA),在执行破坏性跳转(如登出、强制刷新)前,请调用 `appInsights.flush()`。\n\n## 常见陷阱\n\n1. **避免重复初始化**:在不同 bundle 中重复导入模块会导致页面浏览量 (page views) 重复统计。请使用统一的共享模块导出。\n2. **在首次用户输入前初始化**:避免丢失早期的点击或异常记录。\n3. **连接字符串是公开的**:切勿将同一个 App Insights 资源用于存储后端机密。\n4. **`enableAutoRouteTracking` 与手动 `trackPageView` 共存**:会导致重复统计。请二选一。\n5. **CORS 分布式追踪**:要求 API 允许 `Request-Id`、`Request-Context`、`traceparent`、`tracestate` 请求头,并公开 `Request-Context` 响应头。\n6. **GenAI 敏感内容**(如 `gen_ai.input.messages` 等)采用 Opt-In 机制:在没有明确的运行时标志和经批准的数据处理流程前,请勿记录。\n7. **Agent Token 使用量记录在 `chat` span 而非 `invoke_agent`**:仅在确定时才将聚合使用量复制到父级 agent span。\n8. **React StrictMode**:在开发环境下会双次调用 effect —— 请使用模块级单例来保护 `loadAppInsights()`。\n\n## 包体积\n\n完整的 Web SDK 压缩后约为 110 KB(Gzip 后约为 36 KB)。对于预算严格的项目,请使用 **Loader Script** 方案使 SDK 在非关键路径异步加载,或通过 Tree-shaking 剔除未使用的插件。\n\n## 关键类型\n\n```typescript\nimport {\n ApplicationInsights,\n SeverityLevel,\n DistributedTracingModes,\n type IConfiguration,\n type IConfig,\n type ITelemetryItem,\n type ITelemetryPlugin,\n type ICustomProperties,\n type IPageViewTelemetry,\n type IEventTelemetry,\n type IExceptionTelemetry,\n type ITraceTelemetry,\n type IMetricTelemetry,\n type IDependencyTelemetry\n} from \"@microsoft/applicationinsights-web\";\n```\n\n## 最佳实践\n\n1. **单例模式**:从单个模块导出唯一的单例实例。\n2. **尽早初始化**:在应用入口处、路由设置之前进行初始化。\n3. **使用遥测初始化程序**:用于附加 `app.version`、`tenantId` 以及清洗 PII(个人可识别信息)或查询字符串中的机密。\n4. **设置 `distributedTracingMode: 2`**:并确保 API 接受/公开 W3C 追踪上下文头。\n5. **针对 GenAI**:严格遵守 OTel `gen_ai.*` 属性命名,以便在浏览器和后端遥测中统一查询。\n6. **敏感内容捕获门控**:将 `gen_ai.input.messages` / `gen_ai.output.messages` 的捕获置于构建时或运行时的 Opt-in 标志之后。\n7. **在登出/敏感导航时刷新**:确保在途的遥测数据不会丢失。\n\n## 参考资料\n\n- [references/agent-traces.\n- [agent-traces.md](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/references/agent-traces.md) — 完整的 OTel GenAI 语义约定精简版(包含 agent / model / tool span、属性及内容捕获)。\n- [references/framework-extensions.md](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/references/framework-extensions.md) — React, React Native, Angular, Next.js, Vite 配置方案。\n- [references/configuration.md](https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/applicationinsights-web-ts/references/configuration.md) — 完整的 `IConfiguration` 参考及调优指南。\n- Microsoft Learn: \n- ApplicationInsights-JS 源码: \n- OTel GenAI 语义约定: \n\n## 局限性\n\n- 仅在任务与上游源码及本地项目上下文明确匹配时使用此技能。\n- 在应用更改前,请验证命令、生成的代码、依赖项、凭据以及外部服务的行为。\n- 不要将示例视为环境特定测试、安全审查或破坏性/高成本操作用户确认的替代方案。"; var blob = new Blob([md], {type:'text/markdown;charset=utf-8'}); var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = "applicationinsights-web-ts.md"; document.body.appendChild(a); a.click(); setTimeout(function(){URL.revokeObjectURL(a.href); a.remove();}, 100); }); } })();