
OpenTelemetry 可用于跟踪 React 应用程序的性能问题和错误。您可以跟踪从前端 web 应用程序到下游服务的用户请求。OpenTelemetry 是云原生计算基金会(CNCF)下的一个开源项目,旨在标准化遥测数据的生成和收集。已成为下一代可观测平台的事实标准。
React(也称为 React.js 或 ReactJS )是一个免费的开源前端 JavaScript 库,用于基于 UI 组件构建用户界面。它是由 Meta (以前的 Facebook)和一个由个人开发者和公司组成的社区维护的。React 可以作为使用 Next.js 等框架开发单页、移动或服务器渲染应用程序的基础。
然而,React 只关心状态管理和将状态呈现给 DOM,因此创建 React 应用程序通常需要使用额外的库进行路由,以及某些客户端功能。
使用 opentelemetry-js 库,你可以让你的 React 应用生成跟踪数据。您可以跟踪从前端 web 应用程序到下游服务的用户请求。
在演示如何实现 OpenTelemetry 库之前,让我们简要概述一下 OpenTelmetry。
什么是 OpenTelemetry?
OpenTelemetry 是一套与第三方厂商无关的开源工具、API 和 SDK,用于检测应用程序,以创建和管理遥测数据(日志、指标和跟踪)。

OpenTelemetry 库的 instrument(采集程序) 应用程序代码生成遥测数据,然后发送到可观察性工具进行存储和可视化
OpenTelemetry 是建立可观测性框架的基础。它还为您提供了选择后端分析工具的自由。
OpenTelemetry 与 SigNoz
在本文中,我们将使用 SigNoz 作为后端分析工具。SigNoz 是一个全栈开源 APM 工具,可用于存储和可视化 OpenTelemetry 收集的遥测数据。它是在 OpenTelemetry 上原生构建的,并适用于 OTLP 数据格式。
SigNoz 为最终用户提供了查询和可视化功能,并附带了用于应用程序度量和跟踪的开箱即用图表。
现在,让我们开始了解如何使用 opentelemetry-js 库,然后在 SigNoz 中可视化收集的数据。
快速实践
实验环境
DigitalOcean 托管集群(k8s v1.24.13)。
Helm 一键安装 SigNoz
helm repo add signoz https://charts.signoz.io helm install signoz signoz/signoz -n apm --create-namespace --set otelCollector.config.receivers.otlp.protocols.http.include_metadata=true --set otelCollector.config.receivers.otlp.protocols.http.cors.allowed_origins='https://apm-demo.react-admin.com'
注意:cors 跨域设置,我这里 React 应用域名是 https://apm-demo.react-admin.com 。
查看 Pod
kubectl get po -n apm NAME READY STATUS RESTARTS AGE chi-signoz-clickhouse-cluster-0-0-0 1/1 Running 0 3m51s signoz-alertmanager-0 1/1 Running 0 4m5s signoz-clickhouse-operator-54b6d79f58-b47ff 2/2 Running 2 (4m2s ago) 4m5s signoz-frontend-564b8c4868-88grm 1/1 Running 0 4m5s signoz-k8s-infra-otel-agent-dqh5c 1/1 Running 0 4m6s signoz-k8s-infra-otel-agent-jdvnh 1/1 Running 0 4m6s signoz-k8s-infra-otel-agent-tb8sp 1/1 Running 0 4m6s signoz-k8s-infra-otel-deployment-dc85b496f-n6dhm 1/1 Running 0 4m5s signoz-otel-collector-655cff46d8-7z5wn 1/1 Running 0 4m5s signoz-otel-collector-metrics-7775fc9857-mb8wv 1/1 Running 0 4m5s signoz-query-service-0 1/1 Running 0 4m5s signoz-zookeeper-0 1/1 Running 0 4m5s
暴露采集器 Server
此集群 Ingress Controller 是 Traefik,配置如下:
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: ingest namespace: apm spec: entryPoints: - web routes: - match: PathPrefix(`/`) && Host(`ingest.doge-data.com`) kind: Rule services: - name: signoz-otel-collector port: 4318
示例应用
测试地址:
仓库:
OpenTelemetry-JS
仓库:
官方文档:
Tracing 示例核心源码
位于示例仓库:src/helpers/tracing/index.ts
import { context, trace, Span, SpanStatusCode } from "@opentelemetry/api"; import { WebTracerProvider } from "@opentelemetry/sdk-trace-web"; import { Resource } from "@opentelemetry/resources"; import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; import { ZoneContextManager } from "@opentelemetry/context-zone"; import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch"; import { FetchError } from "@opentelemetry/instrumentation-fetch/build/src/types"; import { registerInstrumentations } from "@opentelemetry/instrumentation"; const serviceName = "link-frontend"; const resource = new Resource({ "service.name": serviceName }); const provider = new WebTracerProvider({ resource }); const collector = new OTLPTraceExporter({ url: "https://ingest.doge-data.com/v1/traces", // headers: { // "signoz-access-token": "SigNoz-Cloud-Ingestion-Token-HERE" // } }); provider.addSpanProcessor(new SimpleSpanProcessor(collector)); provider.register({ contextManager: new ZoneContextManager() }); const webTracerWithZone = provider.getTracer(serviceName); declare const window: any; var bindingSpan: Span | undefined; window.startBindingSpan = ( traceId: string, spanId: string, traceFlags: number ) => { bindingSpan = webTracerWithZone.startSpan(""); bindingSpan.spanContext().traceId = traceId; bindingSpan.spanContext().spanId = spanId; bindingSpan.spanContext().traceFlags = traceFlags; }; registerInstrumentations({ instrumentations: [ new FetchInstrumentation({ propagateTraceHeaderCorsUrls: ["/.*/g"], clearTimingResources: true, applyCustomAttributesOnSpan: ( span: Span, request: Request | RequestInit, result: Response | FetchError ) => { const attributes = (span as any).attributes; if (attributes.component === "fetch") { span.updateName( `${attributes["http.method"]} ${attributes["http.url"]}` ); } if (result instanceof Error) { span.setStatus({ code: SpanStatusCode.ERROR, message: result.message, }); span.recordException(result.stack || result.name); } }, }), ], }); export function traceSpan<F extends (...args: any) => ReturnType<F>>( name: string, func: F ): ReturnType<F> { var singleSpan: Span; if (bindingSpan) { const ctx = trace.setSpan(context.active(), bindingSpan); singleSpan = webTracerWithZone.startSpan(name, undefined, ctx); bindingSpan = undefined; } else { singleSpan = webTracerWithZone.startSpan(name); } return context.with(trace.setSpan(context.active(), singleSpan), () => { try { const result = func(); singleSpan.end(); return result; } catch (error) { singleSpan.setStatus({ code: SpanStatusCode.ERROR }); singleSpan.end(); throw error; } }); }
在 React 组件中使用
位于示例仓库:src/components/TracingButton/index.tsx
import { Button } from '@mui/material' import { traceSpan } from 'helpers/tracing' interface Props { label: string; id?: string; secondary?: boolean; onClick: () => void; } export default (props: Props) => { const onClick = (): void => traceSpan(`'${props.label}' button clicked`, props.onClick); return ( <div> <Button id={props.id} variant={"contained"} color={props.secondary ? "secondary" : "primary"} onClick={onClick} > {props.label} </Button> </div> ); };
测试 React 应用上报
转到 https://apm-demo.react-admin.com , 单击 FETCH LINKS。

SigNoz 后台面板查看,聚合指标等



总结
本篇文章侧重于快速实践,OpenTelemetry 本身很复杂,涉及很多基础概念,大家自行翻阅文档。
SigNoz 作为后端分析与可视化工具。虽相对于 ELK Stack 还有很多不足,但它号称是基于 OpenTelemetry 生态原生构建的下一代开源可观测平台,期待它后续发展。
有兴趣的朋友,也可以二次开发 SigNoz,增加自身项目需求。目前也还比较容易的。