dd-sdk-ios icon indicating copy to clipboard operation
dd-sdk-ios copied to clipboard

Connecting RUM resources with APM traces for GRPC requests

Open manoj036 opened this issue 2 months ago • 3 comments

Question

Hello 👋🏻 ,

I'm trying to correlate GRPC requests with backend APM traces in Datadog. The automatic URLSession instrumentation works perfectly, but I need to manually track GRPC requests.

Following the documentation on RUM and APM correlation, I'm injecting trace headers and tracking the resource, but the trace correlation isn't working - I see "Associated trace is missing" in the RUM Explorer.

What I'm doing

1. Injecting trace headers into GRPC metadata:

let traceID = DefaultTraceIDGenerator().generate()
let spanID = DefaultSpanIDGenerator().generate()

// Inject headers into GRPC request
let headers = [
    "x-datadog-trace-id": String(traceID.idLo),
    "x-datadog-parent-id": String(spanID.rawValue),
    "x-datadog-origin": "rum",
    "x-datadog-sampling-priority": "1"
]
// These headers are added to GRPC metadata

2. Tracking the resource in RUM:

RUMMonitor.shared().startResource(
    resourceKey: requestID,
    httpMethod: .post,
    urlString: "grpc://api.example.com/service.method",
    attributes: [:]
)

// Later...
RUMMonitor.shared().stopResource(
    resourceKey: requestID,
    statusCode: 200,
    kind: .native,
    size: nil,
    attributes: [:]
)

The backend receives the trace headers and creates APM spans correctly, but in the Datadog UI the RUM resource and APM trace are not linked.

The problem

With automatic URLSession instrumentation, the correlation works because the SDK internally passes spanContext to the RUM command. But there is no way to link these with startResource().

Is there a way to manually correlate RUM resources with APM traces for non-URLSession protocols like GRPC? Or is this only supported for automatic URLSession instrumentation?

Thanks for your help!

manoj036 avatar Oct 27 '25 12:10 manoj036

Hi @manoj036 👋

Thanks for the detailed question—your understanding of the correlation flow is spot on 👍.

The problem

With automatic URLSession instrumentation, the correlation works because the SDK internally passes spanContext to the RUM command. But there is no way to link these with startResource().

You're right — currently there is no public API to manually link a RUM resource started via startResource() with a trace that was created outside of URLSessionInstrumentation. This limitation is something we may consider addressing in future SDK versions.

✅ Temporary Workaround

Until such a public API is introduced, there’s an internal mechanism used by our cross-platform SDKs (e.g. React Native and Flutter) that you can leverage today.

You can manually inject the trace and span IDs into the resource using the following internal attributes:

RUMMonitor.shared().startResource(
    // ...
    attributes: [
        CrossPlatformAttributes.traceID: traceIDString,
        CrossPlatformAttributes.spanID: spanIDString
    ]
)

Where both traceIDString and spanIDString must be String representations of the trace and span IDs you're using in your headers - you can look at / use our implementation of String extensions for TraceID and SpanID to ensure proper encoding of these values. To fully understand how it works, see how we decode these attributes: here.

⚠️ Important: While this approach is functional, it relies on internal APIs (DatadogInternal) and may change in future SDK versions. To minimize the risk of breakage, we recommend using the CrossPlatformAttributes.traceID and .spanID constants directly instead of hardcoded string keys.

ncreated avatar Oct 28 '25 11:10 ncreated

@ncreated Thanks for the prompt reply.

Would it be proper to use startResource or do you recommend using startSpan for tracking gRPC requests?

(I need some more time to test the above suggestion of using CrossPlatformAttributes as I am not able to get it to work)

manoj036 avatar Nov 06 '25 09:11 manoj036

Hi @manoj036 👋

I'd recommend using startResource.

Based on the SDK implementation, when you track a resource with trace context (via CrossPlatformAttributes), the RUM backend creates the corresponding APM span. Using Tracer.shared().startSpan() would create a separate, independent span.

This is how automatic URLSession instrumentation works - see https://github.com/DataDog/dd-sdk-ios/blob/develop/DatadogTrace/Sources/Integrations/TracingURLSessionHandler.swift#L102.

gonzalezreal avatar Nov 13 '25 16:11 gonzalezreal

Thanks @ncreated @gonzalezreal. I am able to verify that trace linking is working correctly. 👍🏻

manoj036 avatar Nov 21 '25 07:11 manoj036