使用 Node.js 生成跟踪记录和指标

本文档介绍了如何修改 Node.js JavaScript 应用以使用开源 OpenTelemetry 框架收集跟踪记录和指标数据,以及如何将结构化 JSON 日志写入标准输出。本文档还提供了有关您可以安装和运行的示例 Node.js 应用的信息。该应用使用 Fastify Web 框架,并配置为生成指标、跟踪记录和日志。

如需详细了解插桩,请参阅以下文档:

手动和自动插桩简介

对于这种语言,OpenTelemetry 将自动插桩定义为从库和框架收集遥测数据的做法,而无需更改代码。不过,您必须安装模块并设置环境变量。

本文档不介绍自动化插桩。如需了解该主题,请参阅适用于 Node 的自动插桩

如需了解一般信息,请参阅适用于节点的 OpenTelemetry 插桩

准备工作

启用 Cloud Logging, Cloud Monitoring, and Cloud Trace API。

启用 API

对应用进行插桩处理以收集跟踪记录、指标和日志

如需对应用进行插桩处理以收集跟踪记录和指标数据,并将结构化 JSON 写入标准输出,请执行以下步骤,如本文档后续部分所述:

  1. 配置 OpenTelemetry
  2. 将您的应用配置为预加载 OpenTelemetry 配置
  3. 配置结构化日志记录
  4. 写入结构化日志

配置 OpenTelemetry

OpenTelemetry Node.js SDK 的默认配置使用 OTLP 协议导出跟踪记录。它还会将 OpenTelemetry 配置为使用 W3C 跟踪记录上下文格式来传播跟踪记录上下文。此配置可确保 span 在跟踪记录中具有正确的父子关系。

以下代码示例展示了用于设置 OpenTelemetry 的 JavaScript 模块。

如需查看完整示例,请点击 更多,然后选择在 GitHub 上查看


diag.setLogger(
  new DiagConsoleLogger(),
  opentelemetry.core.getEnv().OTEL_LOG_LEVEL
);

const sdk = new opentelemetry.NodeSDK({
  instrumentations: getNodeAutoInstrumentations({
    // Disable noisy instrumentations
    '@opentelemetry/instrumentation-fs': {enabled: false},
  }),
  resourceDetectors: getResourceDetectorsFromEnv(),
  metricReader: getMetricReader(),
});

try {
  sdk.start();
  diag.info('OpenTelemetry automatic instrumentation started successfully');
} catch (error) {
  diag.error(
    'Error initializing OpenTelemetry SDK. Your application is not instrumented and will not produce telemetry',
    error
  );
}

// Gracefully shut down the SDK to flush telemetry when the program exits
process.on('SIGTERM', () => {
  sdk
    .shutdown()
    .then(() => diag.debug('OpenTelemetry SDK terminated'))
    .catch(error => diag.error('Error terminating OpenTelemetry SDK', error));
});

上述代码示例将 OpenTelemetry 配置为使用 OTLP 协议导出指标,并使用 @opentelemetry/auto-instrumentations-node 软件包配置所有可用的 Node.js 插桩。

为了确保所有待处理的遥测数据都被清空,并在应用关闭之前正常关闭连接,SIGTERM 处理程序会调用 shutdown

如需了解详情和配置选项,请参阅 OpenTelemetry Node.js 自动插桩

将您的应用配置为预加载 OpenTelemetry 配置

如需将应用配置为写入结构化日志并使用 OpenTelemetry 收集指标和跟踪记录数据,请更新应用的调用以使用 --require 标志预加载 Node.js 插桩模块。使用 --require 标志可确保 OpenTelemetry 在应用启动之前初始化。如需了解详情,请参阅 OpenTelemetry Node.js 使用入门

以下代码示例展示了传递 --require 标志的 Dockerfile:

CMD node --require ./build/src/instrumentation.js build/src/index.js 2>&1 | tee /var/log/app.log

配置结构化日志记录

如需在写入标准输出的 JSON 格式日志中包含跟踪记录信息,请将应用配置为输出 JSON 格式的结构化日志。Fastify 使用 Pino 日志框架并在每个请求处理程序中提供日志记录器。以下代码示例演示了一个将应用配置为输出 JSON 结构化日志的 Pino LoggerOptions 对象:


// Expected attributes that OpenTelemetry adds to correlate logs with spans
interface LogRecord {
  trace_id?: string;
  span_id?: string;
  trace_flags?: string;
  [key: string]: unknown;
}

// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity
const PinoLevelToSeverityLookup: Record<string, string | undefined> = {
  trace: 'DEBUG',
  debug: 'DEBUG',
  info: 'INFO',
  warn: 'WARNING',
  error: 'ERROR',
  fatal: 'CRITICAL',
};

export const loggerConfig = {
  messageKey: 'message',
  // Same as pino.stdTimeFunctions.isoTime but uses "timestamp" key instead of "time"
  timestamp(): string {
    return `,"timestamp":"${new Date(Date.now()).toISOString()}"`;
  },
  formatters: {
    log(object: LogRecord): Record<string, unknown> {
      // Add trace context attributes following Cloud Logging structured log format described
      // in https://cloud.google.com/logging/docs/structured-logging#special-payload-fields
      const {trace_id, span_id, trace_flags, ...rest} = object;

      return {
        'logging.googleapis.com/trace': trace_id,
        'logging.googleapis.com/spanId': span_id,
        'logging.googleapis.com/trace_sampled': trace_flags
          ? trace_flags === '01'
          : undefined,
        ...rest,
      };
    },
    // See
    // https://getpino.io/#/docs/help?id=mapping-pino-log-levels-to-google-cloud-logging-stackdriver-severity-levels
    level(label: string) {
      return {
        severity:
          PinoLevelToSeverityLookup[label] ?? PinoLevelToSeverityLookup['info'],
      };
    },
  },
} satisfies LoggerOptions;

前面的配置会从日志消息中提取活跃 span 的相关信息,然后将该信息作为属性添加到 JSON 结构化日志中。然后,您可以使用这些属性将日志与跟踪记录相关联:

  • logging.googleapis.com/trace:与日志条目关联的跟踪记录的资源名称。
  • logging.googleapis.com/spanId:与日志条目关联的跟踪记录的 span ID。
  • logging.googleapis.com/trace_sampled:此字段的值必须是 truefalse

如需详细了解这些字段,请参阅 LogEntry 结构。

若要将 Pino 配置与 Fastify 搭配使用,请在创建 Fastify 应用时传递日志记录器配置对象:

// Create the Fastify app providing the Pino logger config
const fastify = Fastify({
  logger: loggerConfig,
});

写入结构化日志

如需写入链接到跟踪记录的结构化日志,请使用 Fastify 提供的 Pino 日志记录器。例如,以下语句显示了如何调用 Logger.info() 方法:

request.log.info({subRequests}, 'handle /multi request');

OpenTelemetry 使用 OpenTelemetry 上下文中当前活跃 span 的 span 上下文自动填充 Pino 日志条目。然后,此 span 上下文会包含在 JSON 日志中,如配置结构化日志记录中所述。

运行配置为收集遥测数据的示例应用

示例应用使用不受制于供应商的格式,包括 JSON(用于日志)和 OTLP(用于指标和跟踪记录)以及 Fastify 框架。为了将遥测路由到 Google Cloud,此示例使用配置了 Google 导出器的 OpenTelemetry Collector。该应用有两个端点:

  • /multi 端点由 handleMulti 函数处理。该应用中的负载生成器会向 /multi 端点发出请求。此端点收到请求时,它会向本地服务器上的 /single 端点发送 3 到 7 个请求。

    /**
     * handleMulti handles an http request by making 3-7 http requests to the /single endpoint.
     *
     * OpenTelemetry instrumentation requires no changes here. It will automatically generate a
     * span for the handler body.
     */
    fastify.get('/multi', async request => {
      const subRequests = randInt(3, 8);
      request.log.info({subRequests}, 'handle /multi request');
    
      for (let i = 0; i < subRequests; i++) {
        await axios.get(`http://localhost:${port}/single`);
      }
      return 'ok';
    });
  • /single 端点由 handleSingle 函数处理。当此端点收到请求时,它会休眠一小段延迟时间,然后以字符串进行响应。

    /**
     * handleSingle handles an http request by sleeping for 100-200 ms. It writes the number of
     * milliseconds slept as its response.
     */
    fastify.get('/single', async request => {
      // Sleep between 100-200 milliseconds
      const sleepMillis = randInt(100, 200);
      request.log.info({sleepMillis}, 'Going to sleep');
      await sleep(sleepMillis);
      return `slept ${sleepMillis}\n`;
    });

下载并部署应用

如需运行示例,请执行以下操作:

  1. 在 Google Cloud 控制台中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。

  2. 克隆代码库:

    git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-js
    
  3. 转到示例目录:

    cd opentelemetry-operations-js/samples/instrumentation-quickstart
    
  4. 构建并运行示例:

    docker compose up --abort-on-container-exit
    

    如果您未在 Cloud Shell 上运行,请使用指向凭据文件的 GOOGLE_APPLICATION_CREDENTIALS 环境变量运行应用。应用默认凭据提供了一个凭据文件 ($HOME/.config/gcloud/application_default_credentials.json)。

    # Set environment variables
    export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
    export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/application_default_credentials.json"
    export USERID="$(id -u)"
    
    # Run
    docker compose -f docker-compose.yaml -f docker-compose.creds.yaml up --abort-on-container-exit
    

查看指标

示例应用中的 OpenTelemetry 插桩生成 Prometheus 指标,您可以使用 Metrics Explorer 查看这些指标:

  • Prometheus/http_server_duration_milliseconds/histogram 会记录服务器请求的持续时间,并将结果存储在直方图中。

  • Prometheus/http_client_duration_milliseconds/histogram 会记录客户端请求的持续时间,并将结果存储在直方图中。

如需查看示例应用生成的指标,请执行以下操作:
  1. 在 Google Cloud 控制台中,转到 Metrics Explorer 页面:

    进入 Metrics Explorer

    如果您使用搜索栏查找此页面,请选择子标题为监控的结果。

  2. 指标元素中,展开选择指标菜单,在过滤栏中输入 http_server,然后使用子菜单选择一个特定资源类型和指标:
    1. 活跃资源菜单中,选择 Prometheus 目标
    2. 活跃指标类别菜单中,选择 HTTP
    3. 活跃指标菜单中,选择指标。
    4. 点击应用
  3. 配置数据的查看方式。

    如果指标的测量结果是累积的,则 Metrics Explorer 会自动按校准时间段对测量数据进行归一化,从而使图表显示速率。如需了解详情,请参阅种类、类型和转换

    测量整数或双精度值时(例如使用两个 counter 指标),Metrics Explorer 会自动对所有时序求和。如需查看 /multi/single HTTP 路由的数据,请将聚合条目的第一个菜单设置为

    如需详细了解如何配置图表,请参阅使用 Metrics Explorer 时选择指标

查看跟踪记录

如需查看跟踪记录数据,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到 Trace 探索器页面:

    转到 Trace 探索器

    您也可以使用搜索栏查找此页面。

  2. 在散点图中,选择 URI 为 /multi 的跟踪记录。
  3. 跟踪记录详情面板的甘特图中,选择标记为 /multi 的 span。

    此时会打开一个面板,其中显示 HTTP 请求的相关信息。这些详细信息包括方法、状态代码、字节数以及调用方的用户代理。

  4. 如需查看与此跟踪记录关联的日志,请选择日志和事件标签页。

    该标签页会显示各个日志。如需查看日志条目的详细信息,请展开日志条目。您还可以点击查看日志,并使用 Logs Explorer 查看日志。

如需详细了解如何使用 Cloud Trace 探索器,请参阅查找和探索跟踪记录

查看日志

在 Logs Explorer 中,您可以检查日志,还可以查看关联的跟踪记录(如果存在)。

  1. 在 Google Cloud 控制台中,转到 Logs Explorer 页面。

    前往 Logs Explorer

    如果您使用搜索栏查找此页面,请选择子标题为 Logging 的结果。

  2. 找到具有 handle /multi request 说明的日志。

    如需查看日志的详细信息,请展开日志条目。

  3. 点击包含“处理/多请求”消息的日志条目中的 跟踪记录,然后选择查看跟踪记录详情

    跟踪记录详情面板随即会打开并显示所选跟踪记录。

如需详细了解如何使用 Logs Explorer,请参阅使用 Logs Explorer 查看日志

后续步骤