All Products
Search
Document Center

Application Real-Time Monitoring Service:Enable end-to-end tracing for a web application or mini program

Last Updated:Mar 11, 2026

The Real User Monitoring (RUM) sub-service of Application Real-Time Monitoring Service (ARMS) replays user sessions to track errors, delays, and exceptions that occur during user interaction. In addition, RUM implements end-to-end tracing in conjunction with Application Monitoring or Managed Service for OpenTelemetry, linking each browser or mini program request to the backend spans it triggers. This gives you a single trace view from user click to server response, so you can pinpoint exactly where latency, errors, or exceptions occur.

Prerequisites

Before you begin, make sure that you have:

How it works

RUM injects trace context headers into outgoing HTTP requests from the browser or mini program. The backend parses these headers to continue the trace, creating a connected chain of spans from the user's browser to the backend services.

Supported tracing protocols

RUM supports the following propagation protocols:

ProtocolApplicable to
W3C (tracecontext)OpenTelemetry client, ARMS agent
B3 single and multiZipkin
JaegerJaeger
sw8SkyWalking

Step 1: Enable tracing on the frontend

Enable tracing during frontend integration with RUM. Choose the integration method that matches your setup: CDN (synchronous or asynchronous) or npm.

Important

Tracing incurs fees, which are included in the bills for Application Monitoring or Managed Service for OpenTelemetry.

Simple mode (same-origin requests only)

If your application only makes same-origin requests and the backend uses the OpenTelemetry protocol, set tracing: true. This is equivalent to { enable: true, sample: 100, tracestate: true, allowedUrls: [], baggage: false }.

Important

Simple mode is recommended for web applications only. For mini programs, use full mode and configure the allowedUrls parameter, because mini program requests do not differentiate domains.

CDN synchronous loading

<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 asynchronous loading

<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
});

Full mode (cross-origin or multi-protocol)

Use full mode to:

  • Send traced requests to cross-origin backends

  • Use multiple propagation protocols for different backend services

  • Customize the sampling rate

Set tracing to a configuration object with the allowedUrls array to specify which URLs to trace and which protocol to use for each.

CDN synchronous loading

<script>
  window.__rum = {
    pid: "<your-pid>",
    endpoint: "<your-endpoint>",
    tracing: {
      enable: true,
      sample: 60,                // Sampling rate: 0-100. Default: 100.
      tracestate: true,          // Include tracestate header (W3C only). Default: true.
      baggage: false,            // Include baggage header. Default: 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 asynchronous loading

<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 parameters

ParameterTypeDefaultDescription
tracing.enableBooleantrueEnable or disable tracing. Reverts to true if the value is invalid.
tracing.sampleNumber100Sampling rate (0--100). Controls the percentage of requests that generate traces. Reverts to 100 if the value is invalid.
tracing.tracestateBooleantrueInclude the tracestate header. Applies to W3C only. When set to false, W3C requests omit the tracestate header.
tracing.baggageBooleanfalseInclude the baggage header. When set to true, RUM adds the baggage header to requests regardless of the protocol.
tracing.propagatorTypesPropagatorType | PropagatorType[]nullPropagation protocols. Overridden by tracing.allowedUrls[].propagatorTypes if specified. When multiple protocols are set, sw8 takes precedence.
tracing.allowedUrlsArray<MatchOption | TraceOption> | undefinedundefinedURLs to trace. For web applications, same-origin requests are traced by default; cross-origin requests require explicit entries. For mini programs, this parameter is required.

URL matching (MatchOption)

type MatchOption = string | RegExp | ((value: string) => boolean);

The allowedUrls parameter matches against full URLs using one of three methods:

  • String: matches any URL that starts with the specified value. For example, 'https://api.example.com' matches https://api.example.com/v1/resource.

  • RegExp: matches URLs against a regular expression.

  • Function: returns true if the URL matches.

For web applications, the following default rule is automatically added to tracing.allowedUrls:

{
  match: (url) => (/^https?:\/\/*/.test(url) || startsWith(url, location.origin)),
  propagatorTypes: ['tracecontext']
}

Propagation protocol types (PropagatorType)

type PropagatorType = 'tracecontext' | 'b3' | 'b3multi' | 'jaeger' | 'sw8';

Each protocol uses a specific header format:

ProtocolHeader format
W3Ctraceparent: {version}-{trace-id}-{parent-id}-{trace-flags}
tracestate: rum={version}&{appType}&{pid}&{sessionId}
B3 singleb3: {TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
B3 multiX-B3-TraceId: {TraceId}
X-B3-SpanId: {SpanId}
X-B3-ParentSpanId: {ParentSpanId}
X-B3-Sampled: {SamplingState}
Jaegeruber-trace-id: {trace-id}:{span-id}:{parent-span-id}:{flags}
sw8sw8: {sample}-{trace-id}-{segment-id}-{0}-{service}-{instance}-{endpoint}-{peer}

Handle cross-origin requests (CORS)

The trace headers listed above are not standard HTTP headers or CORS-safelisted headers. If your application makes cross-origin requests, configure the backend server to accept these headers by specifying the Access-Control-Allow-Headers parameter on the server. Mini programs always require this configuration because their requests do not differentiate domains.

Add the appropriate headers to Access-Control-Allow-Headers on the server based on the propagation protocol you use:

ProtocolRequired Access-Control-Allow-Headers value
W3Ctraceparent, tracestate
B3 singleb3
B3 multiX-B3-TraceId, X-B3-SpanId, X-B3-ParentSpanId, X-B3-Sampled
Jaegeruber-trace-id
sw8sw8

Step 2: Verify the frontend configuration

Web and HTML5 applications

  1. Open your web application in a browser.

  2. Open the developer tools and go to the Network tab.

  3. Look for XHR or Fetch requests from your application. Verify that the request headers include the trace context headers for your chosen protocol.

For W3C, you should see headers similar to:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
tracestate: rum=2&web&your-pid&session-id

Mini programs (Alipay, WeChat, or DingTalk)

  1. Run the mini program in the simulator.

  2. Open the simulator debugger and go to the Network tab.

  3. Verify that outgoing requests include the trace context headers for your chosen protocol.

Step 3: Enable tracing on the backend

To complete the end-to-end trace, the backend must parse the trace context headers from incoming requests and continue the trace. The approach depends on your instrumentation method.

Auto-instrumentation (no code changes)

If your backend uses any of the following, tracing works automatically with no additional configuration:

Instrumentation methodRequirements
ARMS agent for JavaAgent version V2.x, V3.x, or V4.x (V4.x recommended). Supported containers: Apache Tomcat, Jetty, WebLogic, Undertow. Supported frameworks: Spring Boot, Spring Web MVC. See Java components and frameworks supported by ARMS. To install, see Monitor Java applications.
OpenTelemetry auto-instrumentation (Java)Supports most mainstream frameworks. See Use OpenTelemetry to report the trace data of Java applications.
Jaeger Spring Cloud component (Java)See Use Jaeger to report Java application data.
SkyWalking agent for JavaAgent V8.x required (uses sw8 protocol). See Use SkyWalking to report Java application data.
SkyWalking agent for GoSupports Gin, go-restful, http, Kratos v2, Go Micro, Go Resty. See Use SkyWalking to report Go application data.

Manual instrumentation

If auto-instrumentation is not available for your framework, parse the trace context from incoming request headers and create child spans manually.

Java with OpenTelemetry

  1. Add the OpenTelemetry dependencies to your project.

        <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>
  2. Configure W3C propagation during OpenTelemetry initialization.

        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)
                // Add W3C propagation settings
                .setPropagators(ContextPropagators.create(
                        TextMapPropagator.composite(W3CTraceContextPropagator.getInstance(), W3CBaggagePropagator.getInstance()))
                 ).buildAndRegisterGlobal();
    
        // Configure a tracer
        tracer = ExtendedTracer.create(openTelemetry.getTracer("com.example.tracer", "1.0.0"));
  3. Parse the trace context from the request headers and create a child span.

        @RequestMapping("/test")
        public String test(@RequestHeader Map<String, String> headers) {
            Span span = OpenTelemetrySupport.getTracer()
                    .spanBuilder("/test")
                    // Parse the parent span from the headers
                    .setParentFrom(OpenTelemetrySupport.getContextPropagators(), headers)
                    .setSpanKind(SpanKind.SERVER)
                    .startSpan();
            try (Scope scope = span.makeCurrent()) {
                // Process the request
            } catch (Throwable t) {
                span.setStatus(StatusCode.ERROR, "handle parent span error");
            } finally {
                span.end();
            }
            return "success";
        }

Java with Jaeger

  1. Add the Jaeger client dependency.

        <dependency>
          <groupId>io.jaegertracing</groupId>
          <artifactId>jaeger-client</artifactId>
          <version>Latest version</version>
        </dependency>
  2. Initialize the tracer. Replace <endpoint> with the endpoint from the Access point information tab on the Cluster Configurations page in the Managed Service for OpenTelemetry console.

        // Replace manualDemo with your application name
        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());
  3. Parse the trace context and create a child span.

        @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");
                // Process the request
            } catch (Throwable t) {
                TracingHelper.onError(t, span);
                throw t;
            } finally {
                span.finish();
            }
            return "success";
        }

Java with Zipkin

Integrate your application with Managed Service for OpenTelemetry through Zipkin. See Use Zipkin to report Java application data.

Parse the trace context from the request headers:

// Parse the trace context from the request headers
extractor = tracing.propagation().extractor(Request::getHeader);

// Convert the context to a span
oneWayReceive = nextSpan(tracer, extractor.extract(request))
.name("process-request")
.kind(SERVER)
... add tags etc.

// Start the server-side span and flush
oneWayReceive.start().flush();

// Create child spans for follow-up work
next = tracer.newSpan(oneWayReceive.context()).name("step2").start();

Go with OpenTelemetry

Integrate your application with Managed Service for OpenTelemetry. See Use OpenTelemetry to submit the trace data of a Go application.

Generate a span from the request context:

// Initialize the tracer
tracer := otel.Tracer(common.TraceInstrumentationName)
// Generate a span from the request context
handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    span := trace.SpanFromContext(ctx)
    // Process the request
    w.Write([]byte("Hello World"))
})

Go with Jaeger

Integrate your application with Managed Service for OpenTelemetry. See Use Jaeger to report Go application data.

Parse the span context from the HTTP request headers:

// Parse the span context from the HTTP request headers
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span := tracer.StartSpan("myspan", opentracing.ChildOf(spanCtx))
...
defer span.Finish()

Go with Zipkin

Integrate your application with Managed Service for OpenTelemetry. See Use Zipkin to submit the trace data of a Go application.

Retrieve the span from the request context:

// Initialize the tracer
tracer, err := exampletracer.NewTracer("go-frontend", frontendPort)
// Generate a span from the request context
router.Methods("GET").Path("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    span := zipkin.SpanFromContext(r.Context())
    span.Tag("some_key", "some_value")
    // Process the request
    span.Annotate(time.Now(), "some_event")
})

Go with SkyWalking

Integrate your application with Managed Service for OpenTelemetry. See Use SkyWalking to report Go application data.

Parse the trace context from the sw8 header:

// Extract context from the sw8 HTTP request header
span, ctx, err := tracer.CreateEntrySpan(r.Context(), "/api/test", func(key string) (string, error) {
    return r.Header.Get(key), nil
})

Python with OpenTelemetry

Integrate your application with Managed Service for OpenTelemetry. See Use OpenTelemetry to report the trace data of Python applications.

Parse the trace context from the traceparent and tracestate headers:

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)

    # Parse the trace context from the request headers
    carrier = {'traceparent': headers['Traceparent'], 'tracestate': headers['Tracestate']}
    ctx = TraceContextTextMapPropagator().extract(carrier=carrier)

    with tracer.start_span("test", context=ctx):
        # Process the request
        return "success"

Python with Jaeger

Integrate your application with Managed Service for OpenTelemetry. See Use Jaeger to report Python application data.

Parse the trace context from the request headers:

import logging
from flask import Flask
from jaeger_client import Config
from opentracing.ext import tags
from opentracing.propagation import Format

# Initialize the tracer
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()

# Trace decorator
def trace(tracer, span_name):
    def decorator(f):
        @functools.wraps(f)
        def wrapped(*args, **kwargs):
            # Parse the trace context from the request headers
            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

# Example endpoint
@app.route('/test')
@trace(tracer, 'test')
def test():
    return "success"

Python with SkyWalking

Integrate your application with Managed Service for OpenTelemetry. See Use SkyWalking to report Python application data.

Parse the trace context from the sw8 header:

from skywalking import config, agent
from skywalking.trace.context import SpanContext, get_context
from skywalking.trace.carrier import CarrierItem

# Configure SkyWalking
config.init(agent_collector_backend_services='<endpoint>',
            agent_authentication='<auth-token>')

agent.start()

def handle_request(headers):
    # Extract trace information from the 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)

    # Extract the trace context from the carrier
    context = get_context().extract(carrier)

    # Create a span
    with get_context().new_entry_span(op='operation_name') as span:
        # Process the request. The span is submitted automatically when complete.
        ...

# Example: simulated incoming request headers
incoming_headers = {
    'sw8': '1-My40LjU=-MTY1MTcwNDI5OTk5OA==-xxxx-xx-x-x==',
}

handle_request(incoming_headers)

Step 4: View trace data

After end-to-end tracing is enabled, view the full trace data for any frontend request in the RUM module of the ARMS console.

Click View Call Chain to see the complete trace for a request and the application topology. Use this data to analyze slow or failed requests by correlating frontend timing with backend span details.

The top span in the trace is the root span. The span name and application name vary by application type:

Application typeApplication nameSpan name prefix
Web or HTML5rum-browserbrowser.request:
Mini programrum-miniappminiapp.request:
Androidrum-androidandroid.request:
iOSrum-iosios.request:

The topology view shows the upstream and downstream services involved in each request.