• React 前端应用中快速实践 OpenTelemetry 云原生可观测性(SigNoz/K8S)


    image

    OpenTelemetry 可用于跟踪 React 应用程序的性能问题和错误。您可以跟踪从前端 web 应用程序到下游服务的用户请求。OpenTelemetry 是云原生计算基金会(CNCF)下的一个开源项目,旨在标准化遥测数据的生成和收集。已成为下一代可观测平台的事实标准。

    React(也称为 React.jsReactJS )是一个免费的开源前端 JavaScript 库,用于基于 UI 组件构建用户界面。它是由 Meta (以前的 Facebook)和一个由个人开发者和公司组成的社区维护的。React 可以作为使用 Next.js 等框架开发单页、移动或服务器渲染应用程序的基础。

    然而,React 只关心状态管理和将状态呈现给 DOM,因此创建 React 应用程序通常需要使用额外的库进行路由,以及某些客户端功能。

    使用 opentelemetry-js 库,你可以让你的 React 应用生成跟踪数据。您可以跟踪从前端 web 应用程序到下游服务的用户请求。

    在演示如何实现 OpenTelemetry 库之前,让我们简要概述一下 OpenTelmetry

    什么是 OpenTelemetry?

    OpenTelemetry 是一套与第三方厂商无关的开源工具、API 和 SDK,用于检测应用程序,以创建和管理遥测数据(日志、指标和跟踪)。

    image

    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 traceSpanextends (...args: any) => ReturnType>(
      name: string,
      func: F
    ): ReturnType {
      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

    image

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

    image

    image

    image

    总结

    本篇文章侧重于快速实践,OpenTelemetry 本身很复杂,涉及很多基础概念,大家自行翻阅文档。

    SigNoz 作为后端分析与可视化工具。虽相对于 ELK Stack 还有很多不足,但它号称是基于 OpenTelemetry 生态原生构建的下一代开源可观测平台,期待它后续发展。

    有兴趣的朋友,也可以二次开发 SigNoz,增加自身项目需求。目前也还比较容易的。

  • 相关阅读:
    多个文件合成一个bin文件(将uboot/kernel/rootfs合成一个bin文件烧录)(UBin工具下载)
    【云原生】Kubernetes网络管理实操
    Logstash filter grok正则的使用及介绍
    vue2不同版本下如何分环境打包
    CF1899B 250 Thousand Tons of TNT
    理解 ByteBuffer
    网站制作定制标准有哪些?什么是网站制作定制?
    力扣刷题-二叉树-完全二叉树的节点个数
    论文回顾:Playful Palette: An Interactive Parametric Color Mixer for Artists
    一线大厂研发流程
  • 原文地址:https://www.cnblogs.com/hacker-linner/p/17581720.html