Connecting RUM resources with APM traces for GRPC requests
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!
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
spanContextto the RUM command. But there is no way to link these withstartResource().
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 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)
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.
Thanks @ncreated @gonzalezreal. I am able to verify that trace linking is working correctly. 👍🏻