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

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

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

手动和自动插桩简介

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

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

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

准备工作

Enable the Cloud Logging, Cloud Monitoring, and Cloud Trace APIs.

Enable the APIs

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

如需对应用进行插桩处理以收集跟踪记录和指标数据,并将结构化 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. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

  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 查看日志

后续步骤