本文介绍了如何通过OpenTelemetry将Node.js Express应用接入链路追踪。

前提条件

已在package.json中配置对OpenTelemetry的依赖。
 "dependencies": {
    "@opentelemetry/api": "^1.0.4",
    "@opentelemetry/exporter-trace-otlp-grpc": "^0.27.0",
    "@opentelemetry/instrumentation": "^0.27.0",
    "@opentelemetry/instrumentation-express": "^0.27.0",
    "@opentelemetry/resources": "^1.0.1",
    "@opentelemetry/sdk-trace-base": "^1.0.1",
    "@opentelemetry/sdk-trace-node": "^1.0.1",
    "instrumentation-http": "^0.27.0",
  }

操作步骤

  1. 获取接入点信息。
    1. 登录ARMS控制台
    2. 在左侧导航栏单击接入中心,然后在开源监控系统区域单击OpenTelemetry
    3. 接入OpenTelemetry面板单击Node.js页签,然后在右上角选择应用需要接入的地域。
    4. 复制并保存Endpoint和Authentication信息。
      说明 如果需要将应用部署于阿里云生产环境,则接入点选阿里云VPC网络下的Endpoint,否则选择公网Endpoint。
      Node.js接入点信息
  2. 创建Provider。
    const { Resource } = require("@opentelemetry/resources");
    const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
    const {
      SemanticResourceAttributes,
    } = require("@opentelemetry/semantic-conventions");
    
    const provider = new NodeTracerProvider({
      resource: new Resource({
        [SemanticResourceAttributes.HOST_NAME]: require("os").hostname(),
        [SemanticResourceAttributes.SERVICE_NAME]: "opentelemetry-express",    //opentelemetry-express可替换为任意名称。
      }),
    });
  3. 通过Provider注册HTTP和Express框架,自动监测并拦截HTTP和Express。
    说明 如需监测其他框架下的Node.js应用,请参见OpenTelemetry官方文档
    const { registerInstrumentations } = require("@opentelemetry/instrumentation");
    const { HttpInstrumentation } = require("instrumentation-http");
    const {
      ExpressInstrumentation,
    } = require("@opentelemetry/instrumentation-express");
    
    registerInstrumentations({
      tracerProvider: provider,
      instrumentations: [HttpInstrumentation, ExpressInstrumentation],
    });
  4. 配置Exporter,导出数据到ARMS链路追踪。
    请将下面代码中的<ENDPOINT><AUTHENTICATION>替换成步骤1中获取的Endpoint和Authentication。
    const metadata = new grpc.Metadata();
    metadata.set("Authentication", "<AUTHENTICATION>");
    
    const exporter = new OTLPTraceExporter({ url: "<ENDPOINT>", metadata });
    provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
    provider.register();
  5. 可选:添加自定义事件和属性。
    说明 OpenTelemetry API的使用方法,请参见OpenTelemetry官方文档
    const api = require("@opentelemetry/api");
    const currentSpan = api.trace.getSpan(api.context.active());
    currentSpan.addEvent("timestamp", { value: Date.now() });
    currentSpan.setAttribute("tagKey-01", "tagValue-01");

在ARMS控制台查看Trace

ARMS控制台应用监控 > Trace Explorer页面可以查看Node.js应用上报的监控数据。

基于Express框架的Node.js应用完整示例

"use strict";

const { Resource } = require("@opentelemetry/resources");
const {
  OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-grpc");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { HttpInstrumentation } = require("instrumentation-http");
const { SimpleSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const {
  ExpressInstrumentation,
} = require("@opentelemetry/instrumentation-express");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const {
  SemanticResourceAttributes,
} = require("@opentelemetry/semantic-conventions");
const grpc = require("@grpc/grpc-js");

const provider = new NodeTracerProvider({
  resource: new Resource({
    [SemanticResourceAttributes.HOST_NAME]: require("os").hostname(),
    [SemanticResourceAttributes.SERVICE_NAME]: "opentelemetry-express",
  }),
});

registerInstrumentations({
  tracerProvider: provider,
  instrumentations: [HttpInstrumentation, ExpressInstrumentation],
});

const metadata = new grpc.Metadata();
metadata.set("Authentication", "<AUTHENTICATION>");

const exporter = new OTLPTraceExporter({ url: "<ENDPOINT>", metadata });
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register();

// 应用代码
const api = require("@opentelemetry/api");
const axios = require("axios").default;
const express = require("express");
const app = express();

app.get("/", async (req, res) => {
  const result = await axios.get("http://localhost:7001/api");
  return res.status(201).send(result.data);
});

app.get("/api", async (req, res) => {
  const currentSpan = api.trace.getSpan(api.context.active());
  currentSpan.addEvent("timestamp", { value: Date.now() });
  currentSpan.setAttribute("tagKey-01", "tagValue-01");
  res.json({ code: 200, msg: "success" });
});

app.use(express.json());

app.listen(7001, () => {
  console.log("Listening on http://localhost:7001");
});