Node.js インストルメンテーションのサンプル

このドキュメントでは、OpenTelemetry SDK と OpenTelemetry コレクタを使用してトレースと指標データを収集するように Node.js JavaScript アプリを計測する方法について説明します。また、構造化 JSON ログを標準出力に書き込む方法についても説明します。計測をテストするには、サンプルアプリをダウンロードして実行します。このアプリは、Fastify ウェブ フレームワークを使用してログ、指標、トレースデータを生成します。

OpenTelemetry コレクタを使用する場合は、SDK と SDK の OTLP プロセス内エクスポータを使用してアプリケーションを計測します。この計測はベンダーに依存しません。また、プロセス内エクスポータからテレメトリーを受信し、そのテレメトリーを Google Cloud プロジェクトにエクスポートする OpenTelemetry コレクタもデプロイします。コレクタの詳細については、Google が構築した OpenTelemetry コレクタをご覧ください。

お使いの環境でコレクタの使用がサポートされている場合は、OpenTelemetry コレクタを使用してテレメトリー データをエクスポートすることをおすすめします。環境によっては、Google Cloud プロジェクトにデータを直接送信するインプロセス エクスポータを使用する必要があります。インプロセス計測についての詳細は、Trace エクスポータから OTLP エンドポイントに移行するをご覧ください。

計測について詳しくは、次のドキュメントをご覧ください。

手動インストルメンテーションとゼロコード インストルメンテーションについて

この言語について、OpenTelemetry はゼロコード インストルメンテーションを、コードを変更せずにライブラリとフレームワークからテレメトリーを収集する手法と定義しています。ただし、モジュールをインストールし、環境変数を設定する必要があります。

このドキュメントでは、ゼロコード インストルメンテーションについては説明しません。このトピックについては、JavaScript zero-code instrumentation をご覧ください。

一般的な情報については、Node の OpenTelemetry インストルメンテーションをご覧ください。

始める前に

  1. Google Cloud アカウントにログインします。 Google Cloudを初めて使用する場合は、 アカウントを作成して、実際のシナリオでの Google プロダクトのパフォーマンスを評価してください。新規のお客様には、ワークロードの実行、テスト、デプロイができる無料クレジット $300 分を差し上げます。
  2. Google Cloud CLI をインストールします。

  3. 外部 ID プロバイダ(IdP)を使用している場合は、まず連携 ID を使用して gcloud CLI にログインする必要があります。

  4. gcloud CLI を初期化するには、次のコマンドを実行します。

    gcloud init
  5. Google Cloud プロジェクトを作成または選択します

    プロジェクトの選択または作成に必要なロール

    • プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
    • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。ロールを付与する方法を確認する
    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、 Google Cloud プロジェクトの名前に置き換えます。

  6. Google Cloud プロジェクトに対して課金が有効になっていることを確認します

  7. Cloud Logging、Cloud Monitoring、Cloud Trace、Telemetry API を有効にします。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。ロールを付与する方法を確認する

    gcloud services enable logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com telemetry.googleapis.com
  8. Google Cloud CLI をインストールします。

  9. 外部 ID プロバイダ(IdP)を使用している場合は、まず連携 ID を使用して gcloud CLI にログインする必要があります。

  10. gcloud CLI を初期化するには、次のコマンドを実行します。

    gcloud init
  11. Google Cloud プロジェクトを作成または選択します

    プロジェクトの選択または作成に必要なロール

    • プロジェクトを選択する: プロジェクトの選択に特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
    • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。ロールを付与する方法を確認する
    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、 Google Cloud プロジェクトの名前に置き換えます。

  12. Google Cloud プロジェクトに対して課金が有効になっていることを確認します

  13. Cloud Logging、Cloud Monitoring、Cloud Trace、Telemetry API を有効にします。

    API を有効にするために必要なロール

    API を有効にするには、serviceusage.services.enable 権限を含む Service Usage 管理者 IAM ロール(roles/serviceusage.serviceUsageAdmin)が必要です。ロールを付与する方法を確認する

    gcloud services enable logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com telemetry.googleapis.com
  14. Cloud Shell、 Google Cloudリソース、ローカル開発環境でサンプルを実行する場合は、このセクションに記載されている権限で十分です。本番環境アプリケーションの場合、通常、サービス アカウントはログ、指標、トレースデータを書き込む認証情報を提供します。

    サンプル アプリケーションがログ、指標、トレースデータを書き込むために必要な権限を取得するには、プロジェクトに対する次の IAM ロールを付与するよう管理者に依頼してください。

    ログ、指標、トレースデータを表示するために必要な権限を取得するには、プロジェクトに対する次の IAM ロールを付与するよう管理者に依頼してください。

    ロールの付与については、プロジェクト、フォルダ、組織に対するアクセス権の管理をご覧ください。

    必要な権限は、カスタムロールや他の事前定義ロールから取得することもできます。

アプリを計測してトレース、指標、ログを収集する

アプリを計測して、トレースと指標データを収集し、構造化 JSON を標準出力に出力するには、このドキュメントの以降のセクションで説明する手順を実施します。

  1. OpenTelemetry を構成する
  2. OpenTelemetry 構成をプリロードするようにアプリを構成する
  3. 構造化ロギングを構成する
  4. 構造化ログを書き込む

OpenTelemetry を構成する

OpenTelemetry Node.js SDK のデフォルト構成では、OTLP プロトコルを使用してトレースをエクスポートします。また、トレース コンテキストの伝播W3C トレース コンテキスト形式を使用するように OpenTelemetry を構成します。この構成により、トレース内でスパンが正しい親子関係を持つことが保証されます。

次のコードサンプルでは、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));
});

上記のコードサンプルでは、OTLP プロトコルを使用して指標をエクスポートするように OpenTelemetry を構成し、@opentelemetry/auto-instrumentations-node パッケージを使用して、使用可能なすべての Node.js のインストルメンテーションを構成しています。

アプリがシャットダウンする前に、保留中のすべてのテレメトリーがフラッシュされ、接続が正常に終了するように、SIGTERM ハンドラは shutdown を呼び出します。

詳細と構成オプションについては、Zero-Code Instrumentation Configuration をご覧ください。

OpenTelemetry 構成をプリロードするようにアプリを構成する

構造化ログを書き込み、OpenTelemetry を使用して指標とトレースデータを収集するようにアプリを構成するには、Node.js --require フラグを使用して計測モジュールをプリロードするようにアプリの呼び出しを更新します。--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 形式で出力するようにアプリを構成します。

次のコードサンプルは、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;

前の構成では、アクティブなスパンに関する情報がログメッセージから抽出され、抽出された情報が JSON 構造化ログに属性として追加されます。これらの属性を使用して、ログをトレースに関連付けることができます。

  • logging.googleapis.com/trace: ログエントリに関連付けられているトレースのリソース名。
  • logging.googleapis.com/spanId: ログエントリに関連付けられているトレースのスパン ID。
  • logging.googleapis.com/trace_sampled: このフィールドの値は true または false にする必要があります。

これらのフィールドの詳細については、LogEntry 構造体をご覧ください。

Fastify で Pino 構成を使用するには、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 Context で現在アクティブなスパンのスパン コンテキストを Pino ログエントリに自動的に入力します。このスパン コンテキストは、構造化ロギングを構成するで説明されているように、JSON ログに含まれます。

テレメトリーを収集するように構成されたサンプルアプリを実行する

サンプルアプリの計測では、ログデータに JSON、指標データとトレースデータに OTLP など、ベンダーに依存しない形式を使用しています。このアプリは、Fastify フレームワークも使用します。OpenTelemetry Collector は、Google エクスポータを使用してログデータと指標データをプロジェクトに送信します。トレースデータは、OTLP を使用する Telemetry API を使用してプロジェクトに送信します。

このアプリには 2 つのエンドポイントがあります。

  • /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 をアクティブにする

    Google Cloud コンソールの下部にある Cloud Shell セッションが開始し、コマンドライン プロンプトが表示されます。Cloud Shell はシェル環境です。Google Cloud CLI がすでにインストールされており、現在のプロジェクトの値もすでに設定されています。セッションが初期化されるまで数秒かかることがあります。

  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 計測は、Metrics Explorer で表示可能な Prometheus 指標を生成します。

  • Prometheus/http_server_duration_milliseconds/histogram は、サーバー リクエストの所要時間を記録し、結果をヒストグラムに保存します。

  • Prometheus/http_client_duration_milliseconds/histogram は、クライアント リクエストの所要時間を記録し、結果をヒストグラムに保存します。

サンプルアプリによって生成された指標を表示する手順は次のとおりです。
  1. Google Cloud コンソールで [Metrics Explorer] のページに移動します。

    [Metrics Explorer] に移動

    検索バーを使用してこのページを検索する場合は、小見出しが [Monitoring] である結果を選択します。

  2. Google Cloud コンソールのツールバーで、 Google Cloud プロジェクトを選択します。App Hub の構成には、App Hub ホスト プロジェクトまたはアプリ対応フォルダの管理プロジェクトを選択します。
  3. [指標] 要素の [指標を選択] メニューを開いてフィルタバーに「http_server」と入力し、サブメニューを使用して特定のリソースタイプと指標を選択します。
    1. [有効なリソース] メニューで、[Prometheus Target] を選択します。
    2. [有効な指標カテゴリ] メニューで、[Http] を選択します。
    3. [ACTIVE METRICS] メニューで指標を選択します。
    4. [適用] をクリックします。
  4. クエリ結果から時系列を削除するフィルタを追加するには、[フィルタ] 要素を使用します。

  5. データの表示方法を構成します。

    指標の測定値が累積値である場合、Metrics Explorer によりアライメント期間ごとに測定データが自動的に正規化され、その結果、グラフに率が表示されます。詳細については、種類、タイプ、変換をご覧ください。

    2 つの counter 指標など、integer 値または double 値が測定されると、Metrics Explorer はすべての時系列を自動的に合計します。HTTP ルート /multi/single のデータを表示するには、[集計] エントリの最初のメニューを [なし] に設定します。

    グラフの構成の詳細については、Metrics Explorer 使用時の指標の選択をご覧ください。

トレースを表示する

トレースデータが利用可能になるまでに数分かかることがあります。たとえば、プロジェクトでトレースデータが受信されたときに、Google Cloud Observability がそのデータを保存するデータベースを作成することが必要になる場合があります。データベースの作成には数分かかることがあり、その間トレースデータを表示することはできません。

トレースデータを表示するには、次の操作を行います。

  1. Google Cloud コンソールで、 [Trace エクスプローラ] ページに移動します。

    [Trace エクスプローラ] に移動

    このページは、検索バーを使用して見つけることもできます。

  2. ページの表セクションで、スパンの名称が /multi の行を選択します。
  3. [トレースの詳細] パネルのガントチャートで、/multi というラベルのスパンを選択します。

    パネルが開き、HTTP リクエストに関する情報が表示されます。詳細には、メソッド、ステータス コード、バイト数、呼び出し元のユーザー エージェントが含まれます。

  4. このトレースに関連付けられているログを表示するには、[ログとイベント] タブを選択します。

    このタブには、個々のログが表示されます。ログエントリの詳細を表示するには、ログエントリを開きます。[ログを表示] をクリックし、ログ エクスプローラを使用してログを表示することもできます。

Cloud Trace エクスプローラの使用方法について詳しくは、トレースを検索して調査するをご覧ください。

ログを表示する

ログ エクスプローラではログを調査できます。また、関連するトレース(存在する場合)を確認することもできます。

  1. Google Cloud コンソールで、 [ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

    検索バーを使用してこのページを検索する場合は、小見出しが「Logging」の結果を選択します。

  2. handle /multi request の説明を含むログを見つけます。

    ログの詳細を表示するには、ログエントリを開きます。

  3. 「handle /multi request」メッセージを含むログエントリの [ トレース] をクリックし、[トレースの詳細表示] を選択します。

    [トレースの詳細] パネルが開き、選択したトレースが表示されます。

    ログデータは、トレースデータが利用可能になる数分前に利用可能になる場合があります。ID でトレースを検索するか、このタスクの手順に沿ってトレースデータを表示するときにエラーが発生した場合は、1~2 分待ってから操作を再試行してください。

ログ エクスプローラの使用方法については、ログ エクスプローラを使用してログを表示するをご覧ください。

次のステップ