React 前端應用中快速實踐 OpenTelemetry 云原生可觀測性(SigNoz/K8S)
OpenTelemetry 可用于跟蹤 React
應用程序的性能問題和錯誤。您可以跟蹤從前端 web 應用程序到下游服務的用戶請求。OpenTelemetry
是云原生計算基金會(CNCF)下的一個開源項目,旨在標準化遙測數(shù)據(jù)的生成和收集。已成為下一代可觀測平臺的事實標準。
React
(也稱為 React.js
或 ReactJS
)是一個免費的開源前端 JavaScript
庫,用于基于 UI
組件構建用戶界面。它是由 Meta
(以前的 Facebook
)和一個由個人開發(fā)者和公司組成的社區(qū)維護的。React
可以作為使用 Next.js
等框架開發(fā)單頁、移動或服務器渲染應用程序的基礎。
然而,React
只關心狀態(tài)管理和將狀態(tài)呈現(xiàn)給 DOM
,因此創(chuàng)建 React
應用程序通常需要使用額外的庫進行路由,以及某些客戶端功能。
使用 opentelemetry-js 庫,你可以讓你的 React
應用生成跟蹤數(shù)據(jù)。您可以跟蹤從前端 web
應用程序到下游服務的用戶請求。
在演示如何實現(xiàn) OpenTelemetry
庫之前,讓我們簡要概述一下 OpenTelmetry
。
什么是 OpenTelemetry?
OpenTelemetry 是一套與第三方廠商無關的開源工具、API 和 SDK,用于檢測應用程序,以創(chuàng)建和管理遙測數(shù)據(jù)(日志、指標和跟蹤)。
圖片
OpenTelemetry 庫的 instrument(采集程序) 應用程序代碼生成遙測數(shù)據(jù),然后發(fā)送到可觀察性工具進行存儲和可視化
OpenTelemetry 是建立可觀測性框架的基礎。它還為您提供了選擇后端分析工具的自由。
OpenTelemetry 與 SigNoz
在本文中,我們將使用 SigNoz 作為后端分析工具。SigNoz 是一個全棧開源 APM 工具,可用于存儲和可視化 OpenTelemetry 收集的遙測數(shù)據(jù)。它是在 OpenTelemetry 上原生構建的,并適用于 OTLP 數(shù)據(jù)格式。
SigNoz 為最終用戶提供了查詢和可視化功能,并附帶了用于應用程序度量和跟蹤的開箱即用圖表。
現(xiàn)在,讓我們開始了解如何使用 opentelemetry-js 庫,然后在 SigNoz 中可視化收集的數(shù)據(jù)。
快速實踐實驗環(huán)境
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
示例應用
測試地址:
https://apm-demo.react-admin.com
倉庫:
https://github.com/SigNoz/sample-reactjs-app.git
OpenTelemetry-JS
倉庫:
https://github.com/open-telemetry/opentelemetry-js
官方文檔:
https://opentelemetry.io/docs/instrumentation/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"}
notallow={onClick}
>
{props.label}
</Button>
</div>
);
};
測試 React 應用上報
轉到 https://apm-demo.react-admin.com , 單擊 FETCH LINKS
。
圖片
SigNoz 后臺面板查看,聚合指標等
圖片
圖片
圖片
總結
本篇文章側重于快速實踐,OpenTelemetry 本身很復雜,涉及很多基礎概念,大家自行翻閱文檔。
https://opentelemetry.io/docs/
SigNoz 作為后端分析與可視化工具。雖相對于 ELK Stack 還有很多不足,但它號稱是基于 OpenTelemetry 生態(tài)原生構建的下一代開源可觀測平臺,期待它后續(xù)發(fā)展。
有興趣的朋友,也可以二次開發(fā) SigNoz,增加自身項目需求。