Application Real-Time Monitoring Service (ARMS) のサブサービスである Real User Monitoring (RUM) は、ユーザー会話の再生機能により、ユーザー操作中に発生するエラー、遅延、例外を追跡します。さらに、RUM はアプリケーションモニタリングまたは OpenTelemetry 向けマネージドサービスと連携してエンドツーエンドトレースを実装し、各ブラウザまたはミニアプリのリクエストを、そのリクエストがトリガーしたバックエンドのスパンと関連付けます。これにより、ユーザーのクリックからサーバー応答までの単一のトレースビューが得られるため、遅延、エラー、例外が発生する正確な箇所を特定できます。
前提条件
開始する前に、以下の条件を満たしていることを確認してください。
RUM に統合済みのフロントエンドアプリケーション。詳細については、「Web アプリケーションまたは HTML5 アプリケーションの統合」または「ミニアプリの統合」をご参照ください。
アプリケーションモニタリングまたは OpenTelemetry 向けマネージドサービスに統合済みのバックエンドアプリケーション。詳細については、「サービスまたはコンポーネントの統合」をご参照ください。
ヘッダーの解析およびトレースコンテキスト伝搬をサポートする HTTP ベースのバックエンドサービス。
仕組み
RUM は、ブラウザまたはミニアプリから送信される送出 HTTP リクエストにトレースコンテキストヘッダーを挿入します。バックエンドはこれらのヘッダーを解析してトレースを継続し、ユーザーのブラウザからバックエンドサービスに至るまで、接続されたスパンの連鎖を作成します。
対応するトレースプロトコル
RUM は以下の伝搬プロトコルをサポートしています。
| プロトコル | 適用対象 |
|---|---|
W3C (tracecontext) | OpenTelemetry クライアント、ARMS エージェント |
| B3 single および multi | Zipkin |
| Jaeger | Jaeger |
| sw8 | SkyWalking |
ステップ 1:フロントエンドでのトレースの有効化
RUM へのフロントエンド統合時にトレースを有効化します。ご使用の環境に合わせて、CDN(同期または非同期)または npm のいずれかの統合方法を選択してください。
トレースには課金が発生し、その費用はアプリケーションモニタリングまたは OpenTelemetry 向けマネージドサービスの請求書に含まれます。
簡易モード(同一オリジンリクエストのみ)
アプリケーションが同一オリジンリクエストのみを実行し、バックエンドが OpenTelemetry プロトコルを使用する場合、tracing: true を設定します。これは { enable: true, sample: 100, tracestate: true, allowedUrls: [], baggage: false } と等価です。
簡易モードは Web アプリケーション専用で推奨されます。ミニアプリの場合は、フルモードを使用し、allowedUrls パラメーターを構成してください。ミニアプリのリクエストではドメインの区別がないためです。
CDN 同期読み込み
<script>
window.__rum = {
pid: "<your-pid>",
endpoint: "<your-endpoint>",
tracing: true
};
</script>
<script type="text/javascript" src="https://sdk.rum.aliyuncs.com/v2/browser-sdk.js" crossorigin></script>CDN 非同期読み込み
<script>
!(function(c,b,d,a){c[a]||(c[a]={});c[a].config=
{
pid: "<your-pid>",
endpoint: "<your-endpoint>",
tracing: true
}
with(b)with(body)with(insertBefore(createElement("script"),firstChild))setAttribute("crossorigin","",src=d)
})(window,document,"https://sdk.rum.aliyuncs.com/v1/bl.js","__bl");
</script>npm
import ArmsRum from '@arms/rum-browser';
ArmsRum.init({
pid: "<your-pid>",
endpoint: "<your-endpoint>",
tracing: true
});フルモード(クロスオリジンまたはマルチプロトコル)
以下の用途でフルモードを使用します。
クロスオリジンバックエンドへのトレース付きリクエストを送信する場合
異なるバックエンドサービスに対して複数の伝搬プロトコルを使用する場合
サンプリング率をカスタマイズする場合
tracing を、allowedUrls 配列を含む構成オブジェクトに設定し、トレース対象の URL と各 URL に使用するプロトコルを指定します。
CDN 同期読み込み
<script>
window.__rum = {
pid: "<your-pid>",
endpoint: "<your-endpoint>",
tracing: {
enable: true,
sample: 60, // サンプリング率:0~100。デフォルト値:100。
tracestate: true, // tracestate ヘッダー(W3C のみ)を含める。デフォルト値:true。
baggage: false, // baggage ヘッダーを含める。デフォルト値:false。
allowedUrls: [
{ match: 'https://api.example.com', propagatorTypes: ['tracecontext', 'b3'] },
{ match: /api\.backend\.com/i, propagatorTypes: ['b3multi'] },
{ match: (url) => url.includes('.api'), propagatorTypes: ['jaeger'] }
]
}
};
</script>
<script type="text/javascript" src="https://sdk.rum.aliyuncs.com/v2/browser-sdk.js" crossorigin></script>CDN 非同期読み込み
<script>
!(function(c,b,d,a){c[a]||(c[a]={});c[a].config=
{
pid: "<your-pid>",
endpoint: "<your-endpoint>",
tracing: {
enable: true,
sample: 100,
tracestate: true,
baggage: true,
allowedUrls: [
{ match: 'https://api.example.com', propagatorTypes: ['tracecontext', 'b3'] },
{ match: /api\.backend\.com/i, propagatorTypes: ['b3multi'] },
{ match: (url) => url.includes('.api'), propagatorTypes: ['jaeger'] }
]
}
}
with(b)with(body)with(insertBefore(createElement("script"),firstChild))setAttribute("crossorigin","",src=d)
})(window,document,"https://sdk.rum.aliyuncs.com/v1/bl.js","__bl");
</script>npm
import ArmsRum from '@arms/rum-browser';
ArmsRum.init({
pid: "<your-pid>",
endpoint: "<your-endpoint>",
tracing: {
enable: true,
sample: 100,
tracestate: true,
baggage: true,
allowedUrls: [
{ match: 'https://api.example.com', propagatorTypes: ['tracecontext', 'b3'] },
{ match: /api\.backend\.com/i, propagatorTypes: ['b3multi'] },
{ match: (url) => url.includes('.api'), propagatorTypes: ['jaeger'] }
]
}
});トレースパラメーター
| パラメーター | 型 | デフォルト値 | 説明 |
|---|---|---|---|
tracing.enable | ブール値 | true | トレースの有効化または無効化。無効な値が指定された場合は、true に自動的に戻ります。 |
tracing.sample | 数値 | 100 | サンプリング率(0~100)。トレースを生成するリクエストの割合を制御します。無効な値が指定された場合は、100 に自動的に戻ります。 |
tracing.tracestate | ブール値 | true | tracestate ヘッダーを含めるかどうか。W3C プロトコルにのみ適用されます。false に設定すると、W3C リクエストでは tracestate ヘッダーが省略されます。 |
tracing.baggage | ブール値 | false | baggage ヘッダーを含めるかどうか。true に設定すると、RUM はプロトコルに関係なくすべてのリクエストに baggage ヘッダーを追加します。 |
tracing.propagatorTypes | PropagatorType | PropagatorType[] | null | 伝搬プロトコル。指定されている場合、tracing.allowedUrls[].propagatorTypes によって上書きされます。複数のプロトコルが設定されている場合、sw8 が優先されます。 |
tracing.allowedUrls | Array<MatchOption | TraceOption> | undefined | undefined | トレース対象の URL。Web アプリケーションの場合、同一オリジンリクエストはデフォルトでトレースされますが、クロスオリジンリクエストは明示的な登録が必要です。ミニアプリの場合、このパラメーターは必須です。 |
URL マッチング (MatchOption)
type MatchOption = string | RegExp | ((value: string) => boolean);allowedUrls パラメーターは、完全な URL に対して以下の 3 つの方法のいずれかを使用してマッチングを行います。
文字列:指定された値で始まる任意の URL にマッチします。たとえば、
'https://api.example.com'はhttps://api.example.com/v1/resourceにマッチします。正規表現:正規表現を用いて URL とマッチします。
関数:URL が条件を満たす場合に
trueを返します。
Web アプリケーションの場合、以下のデフォルトルールが自動的に tracing.allowedUrls に追加されます。
{
match: (url) => (/^https?:\/\/*/.test(url) || startsWith(url, location.origin)),
propagatorTypes: ['tracecontext']
}伝搬プロトコルの種類 (PropagatorType)
type PropagatorType = 'tracecontext' | 'b3' | 'b3multi' | 'jaeger' | 'sw8';各プロトコルは特定のヘッダー形式を使用します。
| プロトコル | ヘッダー形式 |
|---|---|
| W3C | traceparent: {version}-{trace-id}-{parent-id}-{trace-flags} |
tracestate: rum={version}&{appType}&{pid}&{sessionId} | |
| B3 single | b3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId} |
| B3 multi | X-B3-TraceId: {TraceId} |
X-B3-SpanId: {SpanId} | |
X-B3-ParentSpanId: {ParentSpanId} | |
X-B3-Sampled: {SamplingState} | |
| Jaeger | uber-trace-id: {trace-id}:{span-id}:{parent-span-id}:{flags} |
| sw8 | sw8: {sample}-{trace-id}-{segment-id}-{0}-{service}-{instance}-{endpoint}-{peer} |
クロスオリジンリクエスト(CORS)の処理
上記に示したトレースヘッダーは、標準の HTTP ヘッダーまたは CORS 安全リストに登録されたヘッダーではありません。アプリケーションがクロスオリジンリクエストを実行する場合、バックエンドサーバーを構成してこれらのヘッダーを受け入れるようにする必要があります。具体的には、サーバー側で Access-Control-Allow-Headers パラメーターを指定します。ミニアプリでは、ドメインの区別がないため、常にこの構成が必要です。
使用する伝搬プロトコルに応じて、サーバーの Access-Control-Allow-Headers に適切なヘッダーを追加してください。
| プロトコル | 必要な Access-Control-Allow-Headers 値 |
|---|---|
| W3C | traceparent, tracestate |
| B3 single | b3 |
| B3 multi | X-B3-TraceId, X-B3-SpanId, X-B3-ParentSpanId, X-B3-Sampled |
| Jaeger | uber-trace-id |
| sw8 | sw8 |
ステップ 2:フロントエンド構成の検証
Web アプリケーションおよび HTML5 アプリケーション
ブラウザで Web アプリケーションを開きます。
開発者ツールを開き、[ネットワーク] タブに移動します。
アプリケーションからの XHR または Fetch リクエストを確認し、選択したプロトコルに対応するトレースコンテキストヘッダーがリクエストヘッダーに含まれていることを検証します。
W3C の場合、以下のようなヘッダーが表示されます。
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
tracestate: rum=2&web&your-pid&session-idミニアプリ(Alipay、WeChat、DingTalk)
シミュレーターでミニアプリを実行します。
シミュレーターのデバッガーを開き、[ネットワーク] タブに移動します。
送出リクエストに選択したプロトコルに対応するトレースコンテキストヘッダーが含まれていることを検証します。
ステップ 3:バックエンドでのトレースの有効化
エンドツーエンドトレースを完了するには、バックエンドが着信リクエストからトレースコンテキストヘッダーを解析し、トレースを継続する必要があります。その方法は、使用するイベントトラッキング手法によって異なります。
自動インストルメンテーション (コード変更なし)
バックエンドで以下のいずれかを使用している場合、追加の構成なしでトレースが自動的に有効になります。
| インストルメンテーション方法 | 要件 |
|---|---|
| Java 向け ARMS エージェント | エージェントバージョン V2.x、V3.x、または V4.x(V4.x を推奨)。対応コンテナ:Apache Tomcat、Jetty、WebLogic、Undertow。対応フレームワーク:Spring Boot、Spring Web MVC。詳細については、「ARMS がサポートする Java コンポーネントおよびフレームワーク」をご参照ください。インストール手順については、「Java アプリケーションの監視」をご参照ください。 |
| OpenTelemetry 自動インストルメンテーション (Java) | ほとんどの主要なフレームワークをサポートしています。詳細については、「OpenTelemetry を使用した Java アプリケーションのトレースデータの報告」をご参照ください。 |
| Jaeger Spring Cloud コンポーネント(Java) | 詳細については、「Jaeger を使用した Java アプリケーションデータの報告」をご参照ください。 |
| Java 向け SkyWalking エージェント | エージェント V8.x が必要(sw8 プロトコルを使用)。詳細については、「SkyWalking を使用した Java アプリケーションデータの報告」をご参照ください。 |
| Go 向け SkyWalking エージェント | Gin、go-restful、http、Kratos v2、Go Micro、Go Resty をサポート。詳細については、「SkyWalking を使用した Go アプリケーションデータの報告」をご参照ください。 |
手動イベントトラッキング
ご使用のフレームワークで自動イベントトラッキングが利用できない場合、着信リクエストヘッダーからトレースコンテキストを解析し、子スパンを手動で作成します。
OpenTelemetry を使用した Java
プロジェクトに OpenTelemetry の依存関係を追加します。
<dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-trace</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-extension-annotations</artifactId> <version>1.18.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-semconv</artifactId> <version>1.30.1-alpha</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId> <version>1.34.1</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-extension-incubator</artifactId> <version>1.35.0-alpha</version> </dependency>OpenTelemetry の初期化時に W3C 伝搬を構成します。
Resource resource = Resource.getDefault() .merge(Resource.create(Attributes.of( ResourceAttributes.SERVICE_NAME, "otel-demo", ResourceAttributes.HOST_NAME, "xxxx" ))); SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(OtlpHttpSpanExporter.builder() .setEndpoint("Your Endpoint") .addHeader("Authentication", "Your Token") .build()).build()) .setResource(resource) .build(); openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(sdkTracerProvider) // W3C 伝搬設定を追加 .setPropagators(ContextPropagators.create( TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance())) ).buildAndRegisterGlobal(); // トレーサーを構成 tracer = ExtendedTracer.create(openTelemetry.getTracer("com.example.tracer", "1.0.0"));リクエストヘッダーからトレースコンテキストを解析し、子スパンを作成します。
@RequestMapping("/test") public String test(@RequestHeader Map<String, String> headers) { Span span = OpenTelemetrySupport.getTracer() .spanBuilder("/test") // ヘッダーから親スパンを解析 .setParentFrom(OpenTelemetrySupport.getContextPropagators(), headers) .setSpanKind(SpanKind.SERVER) .startSpan(); try (Scope scope = span.makeCurrent()) { // リクエストを処理 } catch (Throwable t) { span.setStatus(StatusCode.ERROR, "handle parent span error"); } finally { span.end(); } return "success"; }
Jaeger を使用した Java
Jaeger クライアントの依存関係を追加します。
<dependency> <groupId>io.jaegertracing</groupId> <artifactId>jaeger-client</artifactId> <version>最新バージョン</version> </dependency>トレーサーを初期化します。
<endpoint>は、OpenTelemetry 向けマネージドサービスコンソールの [アクセスポイント情報] タブ([クラスター構成] ページ)から取得したエンドポイントに置き換えてください。// manualDemo をご自身のアプリケーション名に置き換え io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("manualDemo"); io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration(); sender.withEndpoint("<endpoint>"); config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1)); config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000)); GlobalTracer.register(config.getTracer());トレースコンテキストを解析し、子スパンを作成します。
@RequestMapping("/test") public String test(@RequestHeader Map<String, String> headers) { Tracer tracer = GlobalTracer.get(); SpanContext parentCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers)); Span span; if (parentCtx != null) { span = tracer.buildSpan("/test").asChildOf(parentCtx).start(); } else { span = tracer.buildSpan("/test").start(); } try (Scope ignored = tracer.activateSpan(span)) { tracer.activeSpan().setTag("methodName", "test"); // リクエストを処理 } catch (Throwable t) { TracingHelper.onError(t, span); throw t; } finally { span.finish(); } return "success"; }
Zipkin を使用した Java
Zipkin 経由でアプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「Zipkin を使用した Java アプリケーションデータの報告」をご参照ください。
リクエストヘッダーからトレースコンテキストを解析します。
// リクエストヘッダーからトレースコンテキストを解析
extractor = tracing.propagation().extractor(Request::getHeader);
// コンテキストをスパンに変換
oneWayReceive = nextSpan(tracer, extractor.extract(request))
.name("process-request")
.kind(SERVER)
... add tags etc.
// サーバー側スパンを開始し、フラッシュ
oneWayReceive.start().flush();
// 後続処理用の子スパンを作成
next = tracer.newSpan(oneWayReceive.context()).name("step2").start();OpenTelemetry を使用した Go
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「OpenTelemetry を使用した Go アプリケーションのトレースデータの送信」をご参照ください。
リクエストコンテキストからスパンを生成します。
// トレーサーを初期化
tracer := otel.Tracer(common.TraceInstrumentationName)
// リクエストコンテキストからスパンを生成
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
span := trace.SpanFromContext(ctx)
// リクエストを処理
w.Write([]byte("Hello World"))
})Jaeger を使用した Go
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「Jaeger を使用した Go アプリケーションデータの報告」をご参照ください。
HTTP リクエストヘッダーからスパンコンテキストを解析します。
// HTTP リクエストヘッダーからスパンコンテキストを解析
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span := tracer.StartSpan("myspan", opentracing.ChildOf(spanCtx))
...
defer span.Finish()Zipkin を使用した Go
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「Zipkin を使用した Go アプリケーションのトレースデータの送信」をご参照ください。
リクエストコンテキストからスパンを取得します。
// トレーサーを初期化
tracer, err := exampletracer.NewTracer("go-frontend", frontendPort)
// リクエストコンテキストからスパンを生成
router.Methods("GET").Path("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
span := zipkin.SpanFromContext(r.Context())
span.Tag("some_key", "some_value")
// リクエストを処理
span.Annotate(time.Now(), "some_event")
})SkyWalking を使用した Go
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「SkyWalking を使用した Go アプリケーションデータの報告」をご参照ください。
sw8 ヘッダーからトレースコンテキストを解析します。
// sw8 HTTP リクエストヘッダーからコンテキストを抽出
span, ctx, err := tracer.CreateEntrySpan(r.Context(), "/api/test", func(key string) (string, error) {
return r.Header.Get(key), nil
})OpenTelemetry を使用した Python
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「OpenTelemetry を使用した Python アプリケーションのトレースデータの報告」をご参照ください。
traceparent および tracestate ヘッダーからトレースコンテキストを解析します。
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
tracer = trace.get_tracer(__name__)
@app.route('/test')
def test():
headers = dict(request.headers)
# リクエストヘッダーからトレースコンテキストを解析
carrier = {'traceparent': headers['Traceparent'], 'tracestate': headers['Tracestate']}
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
with tracer.start_span("test", context=ctx):
# リクエストを処理
return "success"Jaeger を使用した Python
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「Jaeger を使用した Python アプリケーションデータの報告」をご参照ください。
リクエストヘッダーからトレースコンテキストを解析します。
import logging
from flask import Flask
from jaeger_client import Config
from opentracing.ext import tags
from opentracing.propagation import Format
# トレーサーを初期化
def init_tracer(service, scope_manager=None):
logging.getLogger('').handlers = []
logging.basicConfig(format='%(message)s', level=logging.DEBUG)
config = Config(
config={
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
'reporter_batch_size': 1,
},
service_name=service,
scope_manager=scope_manager
)
return config.initialize_tracer()
# トレースデコレータ
def trace(tracer, span_name):
def decorator(f):
@functools.wraps(f)
def wrapped(*args, **kwargs):
# リクエストヘッダーからトレースコンテキストを解析
span_ctx = tracer.extract(Format.HTTP_HEADERS, request.headers)
span_tags = {tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER}
with tracer.start_active_span(span_name, child_of=span_ctx, tags=span_tags) as scope:
rv = f(*args, **kwargs)
return rv
return wrapped
return decorator
# 例:エンドポイント
@app.route('/test')
@trace(tracer, 'test')
def test():
return "success"SkyWalking を使用した Python
アプリケーションを OpenTelemetry 向けマネージドサービスと統合します。詳細については、「SkyWalking を使用した Python アプリケーションデータの報告」をご参照ください。
sw8 ヘッダーからトレースコンテキストを解析します。
from skywalking import config, agent
from skywalking.trace.context import SpanContext, get_context
from skywalking.trace.carrier import CarrierItem
# SkyWalking を構成
config.init(agent_collector_backend_services='<endpoint>',
agent_authentication='<auth-token>')
agent.start()
def handle_request(headers):
# リクエストヘッダーからトレース情報を抽出
carrier_items = []
for item in SpanContext.make_carrier():
carrier_header = headers.get(item.key.lower())
if carrier_header:
carrier_items.append(CarrierItem(item.key, carrier_header))
carrier = SpanContext.make_carrier(carrier_items)
# キャリアからトレースコンテキストを抽出
context = get_context().extract(carrier)
# スパンを作成
with get_context().new_entry_span(op='operation_name') as span:
# リクエストを処理。完了時にスパンが自動的に送信されます。
...
# 例:模擬された着信リクエストヘッダー
incoming_headers = {
'sw8': '1-My40LjU=-MTY1MTcwNDI5OTk5OA==-xxxx-xx-x-x==',
}
handle_request(incoming_headers)ステップ 4:トレースデータの表示
エンドツーエンドトレースを有効化した後、ARMS コンソールの RUM モジュールで、任意のフロントエンドリクエストの完全なトレースデータを表示できます。
[呼び出しチェーンの表示] をクリックすると、リクエストの完全なトレースとアプリケーショントポロジーが表示されます。このデータを活用して、フロントエンドのタイミングとバックエンドのスパン詳細を相関させることで、遅延や失敗したリクエストを分析できます。
トレース内の最上位スパンはルートスパンです。スパン名およびアプリケーション名はアプリケーションの種類によって異なります。
| アプリケーションの種類 | アプリケーション名 | スパン名プレフィックス |
|---|---|---|
| Web または HTML5 | rum-browser | browser.request: |
| ミニアプリ | rum-miniapp | miniapp.request: |
| Android | rum-android | android.request: |
| iOS | rum-ios | ios.request: |
トポロジー表示では、各リクエストに関与する上流および下流のサービスが示されます。