TypeScript 中的可观测性:从黑盒到透明系统
\n\n想象一下,您的 TypeScript 应用程序是一个由数千个齿轮组成的复杂机构。一切运行完美,直到出现问题。然后您收到一条消息:“网站变慢了”。问题出在哪里?数据库?API?还是仅仅某个微服务宕机了?
\n\n没有 可观测性,您只能像跳大神的萨满一样猜测。有了可观测性,您就能看到全局:性能在哪里下降,哪个请求导致了错误,以及为什么内存泄漏。
\n\n在本文中,我们将剖析可观测性的三大支柱——追踪、指标和日志——并通过实际示例学习如何将它们集成到 TypeScript 应用程序中。您将了解如何将您的应用程序从一个黑盒转变为一个可以在几分钟内诊断的透明系统。
\n\n为什么可观测性对 TypeScript 应用程序至关重要?
\n\nTypeScript 不仅仅是 JavaScript 的一个超集。它是一种用于编写高负载系统后端、微服务甚至 CLI 工具的语言。系统越复杂,了解其内部运行情况就越重要。
\n\n传统的日志记录很好,但还不够。当您有 50 个微服务时,日志会变成白噪声。您需要:
\n\n- \n
- 追踪——用于跟踪单个请求在所有服务中的路径。 \n
- 指标——用于查看 RAM 是否在飞速增长。 \n
- 结构化日志——用于避免大海捞针。 \n
让我们将这些集成到一个真实的应用程序中。
\n\nTypeScript 中的追踪:OpenTelemetry 实践
\n\n追踪 是一个请求的生命周期故事。您可以看到每个步骤花费了多少时间:从 HTTP 请求到数据库查询。在 TypeScript 中,事实上的标准是 OpenTelemetry。
\n\n安装和配置 OpenTelemetry
\n\nnpm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-jaeger\n\n创建一个 tracing.ts 文件来初始化追踪:
import { NodeSDK } from '@opentelemetry/sdk-node';\nimport { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport { JaegerExporter } from '@opentelemetry/exporter-jaeger';\nimport { Resource } from '@opentelemetry/resources';\nimport { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';\n\nconst jaegerExporter = new JaegerExporter({\n endpoint: 'http://localhost:14268/api/traces',\n serviceName: 'my-typescript-app',\n});\n\nconst sdk = new NodeSDK({\n resource: new Resource({\n [SemanticResourceAttributes.SERVICE_NAME]: 'my-typescript-app',\n [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',\n }),\n traceExporter: jaegerExporter,\n instrumentations: [getNodeAutoInstrumentations()],\n});\n\nsdk.start();\n\n\n现在,每个 HTTP 请求、每次数据库调用都会被自动追踪。您将在 Jaeger UI(或 Grafana Tempo)中看到完整的调用图。
\n\n手动创建 Span
\n\n自动检测很棒,但有时您需要添加自己的标记。例如,测量复杂业务逻辑的执行时间:
\n\nimport { trace } from '@opentelemetry/api';\n\nconst tracer = trace.getTracer('business-logic');\n\nasync function processOrder(orderId: string): Promise<void> {\n const span = tracer.startSpan('process-order', {\n attributes: { 'order.id': orderId },\n });\n\n try {\n // 您的订单处理逻辑\n await validateOrder(orderId);\n await chargePayment(orderId);\n await sendConfirmation(orderId);\n\n span.setStatus({ code: 1 }); // OK\n } catch (error) {\n span.recordException(error as Error);\n span.setStatus