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:
A frontend application integrated into RUM. See Integrate a web application or an HTML5 application or Integrate a mini program
A backend application integrated into Application Monitoring or Managed Service for OpenTelemetry. See Integrate services or components
An HTTP-based backend service that supports header parsing and trace context propagation
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:
| Protocol | Applicable to |
|---|---|
W3C (tracecontext) | OpenTelemetry client, ARMS agent |
| B3 single and multi | Zipkin |
| Jaeger | Jaeger |
| sw8 | SkyWalking |
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.
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 }.
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
| Parameter | Type | Default | Description |
|---|---|---|---|
tracing.enable | Boolean | true | Enable or disable tracing. Reverts to true if the value is invalid. |
tracing.sample | Number | 100 | Sampling rate (0--100). Controls the percentage of requests that generate traces. Reverts to 100 if the value is invalid. |
tracing.tracestate | Boolean | true | Include the tracestate header. Applies to W3C only. When set to false, W3C requests omit the tracestate header. |
tracing.baggage | Boolean | false | Include the baggage header. When set to true, RUM adds the baggage header to requests regardless of the protocol. |
tracing.propagatorTypes | PropagatorType | PropagatorType[] | null | Propagation protocols. Overridden by tracing.allowedUrls[].propagatorTypes if specified. When multiple protocols are set, sw8 takes precedence. |
tracing.allowedUrls | Array<MatchOption | TraceOption> | undefined | undefined | URLs 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'matcheshttps://api.example.com/v1/resource.RegExp: matches URLs against a regular expression.
Function: returns
trueif 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:
| Protocol | Header format |
|---|---|
| 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} |
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:
| Protocol | Required Access-Control-Allow-Headers value |
|---|---|
| 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 |
Step 2: Verify the frontend configuration
Web and HTML5 applications
Open your web application in a browser.
Open the developer tools and go to the Network tab.
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-idMini programs (Alipay, WeChat, or DingTalk)
Run the mini program in the simulator.
Open the simulator debugger and go to the Network tab.
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 method | Requirements |
|---|---|
| ARMS agent for Java | Agent 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 Java | Agent V8.x required (uses sw8 protocol). See Use SkyWalking to report Java application data. |
| SkyWalking agent for Go | Supports 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
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>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"));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
Add the Jaeger client dependency.
<dependency> <groupId>io.jaegertracing</groupId> <artifactId>jaeger-client</artifactId> <version>Latest version</version> </dependency>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());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 type | Application name | Span name prefix |
|---|---|---|
| Web or HTML5 | rum-browser | browser.request: |
| Mini program | rum-miniapp | miniapp.request: |
| Android | rum-android | android.request: |
| iOS | rum-ios | ios.request: |
The topology view shows the upstream and downstream services involved in each request.