ブラウザベースのウェブアプリケーションを手動でインストルメンテーションする

Splunk Observability Cloud リアルユーザーモニタリング / RUM のフロントエンドアプリケーションに手動でインストルメンテーションを追加し、テレメトリの追加収集、個人識別情報 (PII) のサニタイズ、ユーザーの識別などを行います。

ブラウザ RUM エージェントを使用して、Splunk RUM 用にフロントエンド アプリケーションを手動でインストルメンテーションし、追加のテレメトリを収集したり、個人識別情報(PII)をサニタイズしたり、ユーザーを識別したりすることができます。以下の API 例では、Splunk RUM 用の手動インストルメンテーションをいくつか紹介しています。

別のベンダー用に作成された手動インストルメンテーションを移行するには、「既存の手動インストルメンテーションの移行」を参照してください。ブラウザ RUM の API リファレンスについては、「ブラウザ RUM インストルメンテーションの API リファレンス」を参照してください。

OpenTelemetry API を使ってアプリケーションをインストルメンテーションする

フロントエンド アプリケーションを手動でインストルメンテーションするには、OpenTelemetry API を使用します。ブラウザ RUM エージェントは、@opentelemetry/api を使って TraceProvider を自動的に登録し、独自のインストルメンテーションがアクセスできるようにします。

OpenTelemetry APIのバージョンを確認する

アプリケーションを手動でインストルメンテーションするには、使用する @opentelemetry/api のバージョンが、ブラウザ RUM エージェントが使用する @opentelemetry/api のメジャーバージョンと同じでなければなりません。

これを確認するには、インストルメンテーションした任意のページからブラウザのコンソールで window[Symbol.for('opentelemetry.js.api.1')].version を実行します。このコマンドは、OpenTelemetry API の完全なバージョンを返します。

スパンを作成する

次の例は、属性を持つスパンを作成する方法を示しています:

import {trace} from '@opentelemetry/api'

const span = trace.getTracer('searchbox').startSpan('search');
span.setAttribute('searchLength', searchString.length);
// Time passes
span.end();

すべてのスパンにユーザーIDを設定する

次の例は、ユーザーIDをグローバルに設定する方法を示しています:

import SplunkRum from '@splunk/otel-web';

SplunkRum.setGlobalAttributes({
   'enduser.id': 'Test User'
});

カスタムイベントの作成

次の例は、カスタムイベントの作成方法を示しています:

import {trace} from '@opentelemetry/api'

const tracer = trace.getTracer('appModuleLoader');
const span = tracer.startSpan('test.module.load', {
attributes: {
   'workflow.name': 'test.module.load'
}
});
// time passes
span.end();
注: ブラウザ RUMエージェントのCDNバージョンを使用する際に、コンテンツブロッカーによるロードの問題を回避するために、 SplunkRum APIコールの周りに if (window.SplunkRum) チェックを追加します。

個人を特定できる情報(PII)のサニタイズ

ブラウザ RUM エージェントによって収集されたメタデータには、フロントエンド アプリケーションがそのコードに個人を特定できる情報(PII)を注入した場合、そのようなデータが含まれている可能性があります。たとえば、UI コンポーネントの ID に PII が含まれている可能性があります。

Splunk RUM 用に収集したデータ内の PII を再編集するには、以下の例のように ブラウザ RUM インストルメンテーションを初期化する際に exporter.onAttributesSerializing 設定を使用します:

SplunkRum.init({
// ...
exporter: {
// You can use the entire span as an optional second argument of the sanitizer if needed
   onAttributesSerializing: (attributes) => ({
      ...attributes,
      'http.url': /secret\=/.test(attributes['http.url']) ? '[redacted]' : attributes['http.url'],
   }),
},
});
注: ブラウザ RUMの自動インストルメンテーションは、リクエストのペイロードや POSTボディのサイズ以外のデータを収集したり報告したりしません。

グローバル属性を使用してユーザーメタデータを追加する

デフォルトでは、ブラウザ RUM エージェントはサイトのユーザーにトレースを自動的にリンクしません。ただし、トレースをフィルタリングまたはデバッグするために、ユーザーメタデータの収集が必要になる場合があります。

OpenTelemetry仕様のグローバル属性( enduser.idenduser.role など)をスパンに追加することで、ユーザーを特定することができます。

以下の例は、初期化時にユーザーデータにアクセスできるかどうかに応じて、エージェントの初期化時または初期化後に識別メタデータをグローバル属性として追加する方法を示しています。

初期化時に識別メタデータを追加する

<script src="https://cdn.signalfx.com/o11y-gdi-rum/latest/splunk-otel-web.js" crossorigin="anonymous"></script>
<script>
SplunkRum.init({
   realm: '<realm>',
   rumAccessToken: '<RUM access token>',
   applicationName: '<application-name>',
   globalAttributes: {
      // The following data is already available
      'enduser.id': 42,
      'enduser.role': 'admin',
   },
});
</script>

初期化後に識別メタデータを追加

import SplunkRum from '@splunk/otel-web';

const user = await (await fetch('/api/user')).json();
// Spans generated prior to this call don't have user metadata
SplunkRum.setGlobalAttributes({
   'enduser.id': user ? user.id : undefined,
   'enduser.role': user ? user.role : undefined,
});

Splunk APM からサーバートレースコンテキストを追加する

ブラウザ RUM エージェントは、APM インストルメンテーションによって提供されるバックエンドデータを使用して、Server-Timing ヘッダーを通してサーバートレースコンテキストを収集します。場合によっては、ヘッダーを手動で生成する必要があります。

Server-Timing ヘッダーを手動で作成するには、traceparent という名前の Server-Timing ヘッダーを用意します。desc フィールドには、バージョン、トレース ID、親 ID、トレースフラグが格納されます。

次のHTTPヘッダーを考えてみます:

Server-Timing: traceparent;desc="00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"

この例は、以下のデータを含むコンテキストに解決します:

version=00 trace-id=4bf92f3577b34da6a3ce929d0e0e4736
parent-id=00f067aa0ba902b7 trace-flags=01

traceparent ヘッダーの値を生成するときは、それが以下の正規表現にマッチすることを確認します:

00-([0-9a-f]{32})-([0-9a-f]{16})-01

パターンにマッチしない値を持つサーバータイミングヘッダーは自動的に破棄されます。詳しくは、W3C Web サイトの Server-Timingtraceparent のドキュメントを参照してください。

注: Access-Control-* のような、クロスオリジンリソース共有(CORS)ヘッダーを使用している場合、Server-Timing ヘッダーを読む許可を与える必要がある可能性があります。次に例を示します。Access-Control-Expose-Headers: Server-Timing

ワークフロー・スパンを作成する

ワークフロー・スパンでは、スパンにメタデータを追加して、フォームへの入力やショッピングカートのチェックなど、アプリケーションのワークフローで発生するステップを追跡することができます。

ワークフロー・スパンには以下の属性があります:

Name

タイプ

説明

id

文字列

ワークフローインスタンスの固有ID。

name

文字列

現在のワークフローの意味的な名前。

以下のスニペットは、ワークフロー・スパンの作成方法を示しています:

import {trace} from '@opentelemetry/api'

const tracer = trace.getTracer('appModuleLoader');
const span = tracer.startSpan('test.module.load', {
attributes: {

   'workflow.name': 'test.module.load'
}
});

// Time passes
span.end();

ワークフロー・スパンのエラー収集を有効にするには、error および error.message 属性を追加します:

import {trace} from '@opentelemetry/api'

const tracer = trace.getTracer('appModuleLoader');
const span = tracer.startSpan('test.module.load', {
attributes: {
   'workflow.name': 'test.module.load',
   'error': true,
   'error.message': 'Custom workflow error message'
}
});

span.end();

シングルページアプリケーションにカスタム・スパンを作成する

OpenTelemetry API を使用して、アプリケーションの構造に固有のカスタムスパンを作成できます。たとえば、ユーザーが特定のボタンをクリックしたときにスパンを生成したり、カスタム通信プロトコルをインストルメンテーションすることができます。

OpenTelemetry API をセットアップする

npmを使って、現在のバージョンのOpenTelemetry APIパッケージを追加します:

npm install @opentelemetry/api
注: OpenTelemetry API のバージョンが、@splunk/otel-web パッケージで使用されている API のメジャーバージョンと一致していることを確認してください。バージョン情報は、リリースノートで確認できます。

カスタム・スパンの作成

トレーサーを含めることで、カスタムスパンを作成することができます。例:

import {trace} from '@opentelemetry/api';

// Create a tracer
const tracer = trace.getTracer('my-application', '1.0.0');

// Example of an async/await function
async function processForm(form) {
   const span = tracer.startSpan('process form');

   // Wait for processing to be done
   span.end();
}

// Example of a callback function
function markCompleted(item) {
   const span = tracer.startSpan('item complete');

   processCompletion(item, function() {
      // ... Update item display
      span.end();
   });
}

// Example of hook system provided by another library
router.beforeEach((transition) => {
   transition.span = tracer.startSpan('navigate', {
      attributes: {
         'router.path': transition.path
      }
   });
});

router.afterEach((transition) => {
   if (transition.span) {
      transition.span.end();
   }
});

// For a list of available methods, see the OpenTelemetry API documentation.

生成されたスパンに子スパンを追加するには、Context API を使用します。例:

import {trace, context} from '@opentelemetry/api';

// Create a tracer
const tracer = trace.getTracer('my-application', '1.0.0');

async function processForm(form) {
   const span = tracer.startSpan('process form');
   await context.with(trace.setSpan(context.active(), span), async () => {

      await client.send(form); // client.send would create a XHR span using instrumentation

   });
   span.end();
}
注: たとえば、Promise.then, setTimeout, ... ブロックの中など、直接呼び出されていない子スパンにはコンテキストが伝搬されない可能性があります。この問題を軽減するには、非同期トレースをアクティブにします。「非同期トレース設定」を参照してください。

シングルページアプリケーションのフレームワークでエラーを収集する

独自のエラーインターセプターまたはハンドラを使用するシングルページアプリケーション(SPA)フレームワークからのJavaScriptエラーの収集を有効にするには、ブラウザ RUMエージェントをフレームワークと統合する必要があります。

以下のフレームワーク固有の例では、ブラウザ RUM エージェントをサポートするフレームワークと統合する方法を示します。すべての例で、npm を使用してブラウザ RUM エージェントがインストールされていることを前提としています。「npm パッケージ」を参照してください。

React

エラー境界コンポーネントで Splunk RUM エージェント API を使用します:

import React from 'react';
import SplunkRum from '@splunk/otel-web';

class ErrorBoundary extends React.Component {
   componentDidCatch(error, errorInfo) {
// To avoid loading issues due to content blockers
// when using the CDN version of the Browser RUM
// agent, add if (window.SplunkRum) checks around
// SplunkRum API calls
      SplunkRum.error(error, errorInfo)
   }

   // Rest of your error boundary component
   render() {
      return this.props.children
   }
}

Vue.js

Vue errorHandler にcollect関数を追加します。

Vue.jsバージョン3.xの場合は、以下のコードを使用します:

import Vue from 'vue';
import SplunkRum from '@splunk/otel-web';

const app = createApp(App);

app.config.errorHandler = function (error, vm, info) {
// To avoid loading issues due to content blockers
// when using the CDN version of the Browser RUM
// agent, add if (window.SplunkRum) checks around
// SplunkRum API calls
   SplunkRum.error(error, info)
}
app.mount('#app')

Vue.jsバージョン2.xの場合は、以下のコードを使用します:

import Vue from 'vue';
import SplunkRum from '@splunk/otel-web';

Vue.config.errorHandler = function (error, vm, info) {
// To avoid loading issues due to content blockers
// when using the CDN version of the Browser RUM
// agent, add if (window.SplunkRum) checks around
// SplunkRum API calls
   SplunkRum.error(error, info)
}

Angular

Angularバージョン2.xの場合は、エラーハンドラモジュールを作成します:

import {NgModule, ErrorHandler} from '@angular/core';
import SplunkRum from '@splunk/otel-web';

class SplunkErrorHandler implements ErrorHandler {
   handleError(error) {
// To avoid loading issues due to content blockers
// when using the CDN version of the Browser RUM
// agent, add if (window.SplunkRum) checks around
// SplunkRum API calls
      SplunkRum.error(error, info)
   }
}

@NgModule({
   providers: [
      {
         provide: ErrorHandler,
         useClass: SplunkErrorHandler
      }
   ]
})
class AppModule {}

Angular バージョン 1.x の場合は、exceptionHandler を作成します:

import SplunkRum from '@splunk/otel-web';

angular.module('...')
   .factory('$exceptionHandler', function () {
      return function (exception, cause) {
// To avoid loading issues due to content blockers
// when using the CDN version of the Browser RUM
// agent, add if (window.SplunkRum) checks around
// SplunkRum API calls
         SplunkRum.error(exception, cause)
      }
})

Ember.js

以下の例のように、Ember.onerror フックを設定します:

import Ember from 'ember';
import SplunkRum from '@splunk/otel-web';

Ember.onerror = function(error) {
// To avoid loading issues due to content blockers
// when using the CDN version of the Browser RUM
// agent, add if (window.SplunkRum) checks around
// SplunkRum API calls
   SplunkRum.error(error)
}