Node.js 插桩示例

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

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

手动和零代码插桩简介

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

本文档不介绍零代码插桩。如需了解该主题,请参阅 JavaScript 零代码插桩

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

准备工作

  1. Install the Google Cloud CLI.

  2. 配置 gcloud CLI 以使用您的联合身份。

    如需了解详情,请参阅使用联合身份登录 gcloud CLI

  3. 如需初始化 gcloud CLI,请运行以下命令:

    gcloud init
  4. Create or select a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.
    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Verify that billing is enabled for your Google Cloud project.

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

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    gcloud services enable logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com
  7. 如果您在 Cloud Shell、 Google Cloud资源或本地开发环境中运行示例,则只需具备本部分中列出的权限即可。对于生产应用,通常由服务账号提供用于写入日志、指标和跟踪记录数据的凭据。

    如需获得让示例应用写入日志、指标和跟踪记录数据所需的权限,请让管理员为您授予项目的以下 IAM 角色:

    如需获得查看日志、指标和跟踪记录数据所需的权限,请让管理员为您授予项目的以下 IAM 角色:

    如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

    您也可以通过自定义角色或其他预定义角色来获取所需的权限。

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

    如需对应用进行插桩处理以收集跟踪记录和指标数据,并将结构化 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.diagLogLevelFromString(
        opentelemetry.core.getStringFromEnv('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 配置

    如需将应用配置为写入结构化日志并使用 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
      
    5. 查看指标

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

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

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

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

        进入 Metrics Explorer

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

      2. 在 Google Cloud 控制台的工具栏中,选择您的 Google Cloud 项目。对于 App Hub 配置,请选择 App Hub 宿主项目或已启用应用的文件夹的管理项目。
      3. 指标元素中,展开选择指标菜单,在过滤条件栏中输入 http_server,然后使用子菜单选择一个特定资源类型和指标:
        1. 活跃资源菜单中,选择 Prometheus 目标
        2. 活跃指标类别菜单中,选择 HTTP
        3. 活跃指标菜单中,选择指标。
        4. 点击应用
      4. 如需添加用于从查询结果中移除时序的过滤条件,请使用过滤条件元素

      5. 配置数据的查看方式。

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

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

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

      查看跟踪记录

      跟踪记录数据可能需要几分钟的时间才可使用。例如,当您的项目收到跟踪记录数据时,Google Cloud Observability 可能需要创建数据库来存储这些数据。创建数据库可能需要几分钟时间,在此期间,您无法查看任何跟踪记录数据。

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

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

        转到 Trace 探索器

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

      2. 在此页面的表部分,选择 span 名称为 /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. 点击包含“处理/多请求”消息的日志条目中的 跟踪记录,然后选择查看跟踪记录详情

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

        您的日志数据可能会比跟踪记录数据早几分钟可用。如果您在通过按 ID 搜索跟踪记录或通过执行此任务中的步骤来查看跟踪记录数据时遇到错误,请等待一两分钟,然后重试相应操作。

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

      后续步骤