欢迎参与 8 月 1 日中午 11 点的线上分享,了解 GreptimeDB 联合处理指标和日志的最新方案! 👉🏻 点击加入

Skip to content
On this page
技术
2024-9-25

数据监控入门必读 - 在 NodeJS 中使用 OpenTelemetry 进行数据埋点指南

OpenTelemetry 是一个用于统一应用程序可观测性数据收集的开源框架。这篇文章介绍了开发者如何通过使用 OpenTelemetry NodeJS SDK 对应用进行自动化监控,实现捕获指标和追踪数据。

在使用 OpenTelemetry 监控应用程序时,最基础的一步是进行 "OTel 数据埋点"。所谓的数据埋点,指的是为应用程序设置一些信号(如追踪、日志、指标等),这样你就可以对它的行为进行监控和分析。通过这些信号,你可以观察应用程序的运行状态和性能表现。

数据埋点概览

获取被监控的可观测数据的重要前提是正确的数据埋点,本文将介绍 OpenTelemetry 数据埋点的两种方式。想要了解 OpenTelemetry 的各个组成部分以及它们之间如何协同工作,请参考这篇文章《OpenTelemetry 新手入门》。

OpenTelemetry 的数据收集和跟踪功能(即 "检测" 或 "仪表化")是通过 OpenTelemetry API 来完成的。而 OpenTelemetry NodeJS SDK 实现了这个 API,提供了在 Node.js 应用中使用 OpenTelemetry 功能的方式。

这些组件可以无缝嵌入代码中,并生成所需的信号,以帮助用户全面掌握服务的运行状态。

OpenTelemetry 的设计注重模块化和灵活性,遵循单一职责原则。尽管涉及的术语较多,但主要的核心组件包括以下几个:

  1. 通过 MeterTracer 生成数据;
  2. 使用 ReaderProcessor 对数据进行采样、过滤和转换;
  3. 通过 ExporterCollector 将 OTLP 数据打包并传输到用户选择的与 OpenTelemetry 兼容的后端。

这个概述能帮助用户理解如何为应用程序进行埋点(即设置监控和收集数据的信号)。通过了解数据在不同组件之间是如何流动的,就能更好地为自己的应用程序进行仪表化设置,有效地监控和分析应用程序的性能和运行状态。

OpenTelemetry 可观测数据流向

OpenTelemetry 的设计优势在于允许用户根据 OTel API 的规范,自定义数据处理管道的各个部分。默认的实现方式能够以一致的格式生成指标和跨度(spans),并将这些数据包装成 OpenTelemetry 协议(OTLP)的格式,以便于 OTLP 后端系统进行接收和处理。

下图展示了数据如何在各组件间传输:

图 1:数据在组件中传输流程图
图 1:数据在组件中传输流程图

这些标准操作流程为可观察性工具提供了标准的指导框架,使得用户能够选择符合该标准的多种工具。更多详细信息,请参阅 OpenTelemetry 规范

在深入了解 OTel API 的作用及数据流向后,接下来我们来看看如何实际为应用程序进行埋点。埋点主要分为两类:自动植入和手动植入,下节将讨论它们之间的区别及各自适用的场景。

自动植入埋点

自动植入是使用 OpenTelemetry 的最简单方式。该过程通过接入应用程序和服务的常见接口边缘,自动捕获一些最常见的协议操作信号。只需导入一个库并实例化一个单例,或者在某些框架和语言中,仅需安装 SDK 并导出一些环境变量即可。

以下是自动植入可以捕获的常见指标示例:

  • HTTP 请求

    • 请求持续时间
    • 响应状态码
    • URL 路径
    • HTTP 方法
  • 数据库查询

    • 查询执行时间
    • SQL 语句(已清理)
    • 数据库类型(如 MySQL、PostgreSQL)
  • 消息系统

    • 消息发布/消费操作
    • 队列名称
    • 消息大小
  • 框架特定信号

    • 针对 Web 框架(如 Express.js、Flask):路由处理、 中间件执行
    • 针对 ORM(如 Sequelize、SQLAlchemy):模型操作(创建、读取、更新、删除)
  • 运行时指标

    • CPU 使用率
    • 内存分配
    • 垃圾回收统计

为应用进行自动植入非常简单,以下是一个集成了 OpenTelemetry NodeJS SDK 的 Express 应用示例:

javascript
const express = require('express');
const { UserService } = require('./services/userService')

// ---------------------------------------------------
// ---------------------------------------------------
// ---------------------------------------------------
// Import the OpenTelemetry SDK
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');

// Initialize the OpenTelemetry SDK
const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter(),
  instrumentations: [getNodeAutoInstrumentations()] 
 // sets up basic defaults for express http collection and app resource consumption
});

// Start the SDK
sdk.start();

// That's all the configuration needed to start collecting metrics
// ---------------------------------------------------
// ---------------------------------------------------
// ---------------------------------------------------

// Create and set up your Express app as usual
const app = express();

// Your routes and middleware here
app.get('/', (req, res) => {
  res.send('Hello, OpenTelemetry!');
});

app.listen(...);

通过自动植入埋点收集信号,用户可以在代码改动极少的条件下获取大量可观察性数据,帮助快速启动 OpenTelemetry。虽然自动埋点已经可以提供非常高的价值,但在某些情况下,应用程序所有者可能需要监听应用程序中发生的更多自定义信号。开发者可以通过手动植入实现对采集信号的精细控制。

手动植入埋点

手动植入的核心配置在于 provider,它作为用于创建输出信号对象的生成器。在这些生成器中,用户可以配置如下对象:

  • resource:标识生成遥测数据的实体。通常包括服务名称、主机信息、容器或 Pod 标识符、云提供商详细信息或描述遥测来源的其他元数据,resource 提供了数据来源的上下文信息。
  • meter / tracer:(如 ObservableGauge):实际捕获指标和 Trace 的工具。这些对象将用于生成遥测数据。
  • reader:(如 PeriodicExportingMetricReader):可以用于每 n 秒导出一次指标。
  • sampler: (如 TraceIdRatioBasedSampler):可用于捕获 r% 的请求。
  • exporter:(如 OTLPTraceExporter):用于将数据发送到 OTLP 兼容后端,如 GreptimeCloud。

以下示例展示了如何通过手动植入从 Tesla API 收集电池电量数据,并将指标发送到 GreptimeCloud 托管的 OTLP 后端:

javascript
const { MeterProvider } = require('@opentelemetry/sdk-metrics');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { SimpleSpanProcessor, BatchSpanProcessor, ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-base');
const { trace, metrics, context } = require('@opentelemetry/api');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const teslajs = require('teslajs');
const options = {
  authToken: 'your-access-token',
  vehicleID: 'your-vehicle-id'
};
// Configure the resource naming the given service
const resource = new Resource({
  [SemanticResourceAttributes.SERVICE_NAME]: 'tesla-monitoring-app',
});

// Configure the exporter to be sent to Greptime cloud
const otlpExporter = new OTLPMetricExporter({
  url: 'https://6qr5e68t5wpn.us-west-2.aws.greptime.cloud/v1/otlp/v1/metrics',
  headers: {
    'Authorization': 'Basic WxN5prHHwx1ATXMvduBlgy4K:xxxxxxx',
    'X-Greptime-DB-Name': 'my-greptime-cloud-instance@tesla-monitoring',
  },
});

// Set this MeterProvider to be global to the app being instrumented.
const meterProvider = new MeterProvider({ resource });
opentelemetry.metrics.setGlobalMeterProvider(meterProvider);
const metricReader = new PeriodicExportingMetricReader({
  exporter: otlpExporter,
  exportIntervalMillis:   exportIntervalMillis: 5 * 60 * 1000
});

meterProvider.addMetricReader(metricReader);

// Create and register the tracer
const tracerProvider = new NodeTracerProvider();

// Configure span processor to send spans to simply log the results
tracerProvider.addSpanProcessor(new BatchSpanProcessor(new ConsoleSpanExporter()));
tracerProvider.register();

const tracer = trace.getTracer('example-basic-tracer-node');

const teslaMeter = metrics.getMeter(
  "tesla-instrumentation",
  '1.0',
);

const batteryGauge = teslaMeter.createObservableGauge("tesla.battery");

async function pollTeslaData() {
  const span = tracer.startSpan('pollTeslaData');
  const ctx = trace.setSpan(context.active(), span);

  try {
    await context.with(ctx, async () => {
      const vehicleData = await teslajs.vehicleData()
vehicleData.charge_state.battery_level
      batteryGauge.addCallback((result) => {
        result.observe(batteryLevel);
      });
    });
  } catch (error) {
    span.recordException(error);
  } finally {
    span.end();
  }
}

// Poll Tesla data every 5 minutes
setInterval(pollTeslaData, 5 * 60 * 1000);

该示例展示了 OpenTelemetry 在捕获可观察性数据方面的广泛支持,并展示了如何快速启动。

分析埋点所捕获的数据

通过 GreptimeDB 的 Grafana 插件,你可以在 Grafana 中直接可视化从 OpenTelemetry 捕获的所有数据。几分钟内即可完成帐户设置,利用 OpenTelemetry 为你的服务提供新的见解。

图 2:Grafana Dashboard 监控数据展示
图 2:Grafana Dashboard 监控数据展示

在后续文章中,我们将介绍如何将这些 OpenTelemetry 数据连接到 Grafana 进行可视化。如果你正在寻找性能卓越、可靠且开源的 OpenTelemetry 后端,欢迎了解 GreptimeDB。

关于 Greptime

Greptime 格睿科技专注于为可观测、物联网及车联网等领域提供实时、高效的数据存储和分析服务,帮助客户挖掘数据的深层价值。目前基于云原生的时序数据库 GreptimeDB 已经衍生出多款适合不同用户的解决方案,更多信息或 demo 展示请联系下方小助手(微信号:greptime)。

欢迎对开源感兴趣的朋友们参与贡献和讨论,从带有 good first issue 标签的 issue 开始你的开源之旅吧~期待在开源社群里遇见你!添加小助手微信即可加入“技术交流群”与志同道合的朋友们面对面交流哦~

Star us on GitHub Now: https://github.com/GreptimeTeam/greptimedb

官网:https://greptime.cn/

文档:https://docs.greptime.cn/

Twitter: https://twitter.com/Greptime

Slack: https://greptime.com/slack

LinkedIn: https://www.linkedin.com/company/greptime/

监控
埋点

加入我们的社区

获取 Greptime 最新更新,并与其他用户讨论。