Skip to content

Commit

Permalink
Merge pull request #314 from DataDog/ncreated/RUMM-696-stop-sending-t…
Browse files Browse the repository at this point in the history
…racing-span-for-rum-resources

RUMM-696 Stop sending tracing `Span` for RUM Resources
  • Loading branch information
ncreated authored Nov 23, 2020

Unverified

This user has not yet uploaded their public signing key.
2 parents f119262 + 0524cea commit 51cd4a3
Showing 17 changed files with 442 additions and 249 deletions.
4 changes: 4 additions & 0 deletions Datadog/Datadog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -207,6 +207,7 @@
6193DCCE251B6201009B8011 /* RUMTASScreen1ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6193DCCD251B6201009B8011 /* RUMTASScreen1ViewController.swift */; };
6193DCE1251B692C009B8011 /* RUMTASTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6193DCE0251B692C009B8011 /* RUMTASTableViewController.swift */; };
6193DCE8251B9AB1009B8011 /* RUMTASCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6193DCE7251B9AB1009B8011 /* RUMTASCollectionViewController.swift */; };
61940C7C25668EC600A20043 /* URLSessionInterceptionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61940C7B25668EC600A20043 /* URLSessionInterceptionHandler.swift */; };
6198D27124C6E3B700493501 /* RUMViewScopeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6198D27024C6E3B700493501 /* RUMViewScopeTests.swift */; };
61A763DC252DB2B3005A23F2 /* NSURLSessionBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 61A763DB252DB2B3005A23F2 /* NSURLSessionBridge.m */; };
61AD4E182451C7FF006E34EA /* TracingFeatureMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61AD4E172451C7FF006E34EA /* TracingFeatureMocks.swift */; };
@@ -611,6 +612,7 @@
6193DCCD251B6201009B8011 /* RUMTASScreen1ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMTASScreen1ViewController.swift; sourceTree = "<group>"; };
6193DCE0251B692C009B8011 /* RUMTASTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMTASTableViewController.swift; sourceTree = "<group>"; };
6193DCE7251B9AB1009B8011 /* RUMTASCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMTASCollectionViewController.swift; sourceTree = "<group>"; };
61940C7B25668EC600A20043 /* URLSessionInterceptionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionInterceptionHandler.swift; sourceTree = "<group>"; };
6198D27024C6E3B700493501 /* RUMViewScopeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMViewScopeTests.swift; sourceTree = "<group>"; };
61A763D9252DB2B3005A23F2 /* DatadogTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DatadogTests-Bridging-Header.h"; sourceTree = "<group>"; };
61A763DA252DB2B3005A23F2 /* NSURLSessionBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSURLSessionBridge.h; sourceTree = "<group>"; };
@@ -1456,6 +1458,7 @@
children = (
613F23DC252B05BD006CD2D7 /* URLFiltering */,
618E1337252340810098C6B0 /* URLSessionInterceptor.swift */,
61940C7B25668EC600A20043 /* URLSessionInterceptionHandler.swift */,
61417DC52525CDDE00E2D55C /* TaskInterception.swift */,
);
path = Interception;
@@ -2494,6 +2497,7 @@
618715F924DC13A100FC0F69 /* RUMDataModelsMapping.swift in Sources */,
61C3638524361E9200C4D4E6 /* Globals.swift in Sources */,
E1D202EA24C065CF00D1AF3A /* ActiveSpansPool.swift in Sources */,
61940C7C25668EC600A20043 /* URLSessionInterceptionHandler.swift in Sources */,
61F3CDA3251118FB00C816E5 /* UIKitRUMViewsHandler.swift in Sources */,
61C5A88824509A0C00DA608C /* Warnings.swift in Sources */,
618DCFD924C7269500589570 /* RUMUUIDGenerator.swift in Sources */,
6 changes: 6 additions & 0 deletions Datadog/Example/TestScenarios.swift
Original file line number Diff line number Diff line change
@@ -80,6 +80,9 @@ class TracingURLSessionScenario: URLSessionBaseScenario, TestScenario {
static func envIdentifier() -> String { "TracingURLSessionScenario" }

override func configureSDK(builder: Datadog.Configuration.Builder) {
_ = builder
.enableRUM(false)

super.configureSDK(builder: builder)
}
}
@@ -92,6 +95,9 @@ class TracingNSURLSessionScenario: URLSessionBaseScenario, TestScenario {
static func envIdentifier() -> String { "TracingNSURLSessionScenario" }

override func configureSDK(builder: Datadog.Configuration.Builder) {
_ = builder
.enableRUM(false)

super.configureSDK(builder: builder)
}
}
Original file line number Diff line number Diff line change
@@ -6,16 +6,7 @@

import Foundation

/// An interface for sending Tracing `Spans` for given `URLSession` task interception.
internal protocol URLSessionRUMResourcesHandlerType {
func subscribe(commandsSubscriber: RUMCommandSubscriber)
/// Notifies the `URLSessionTask` interception start.
func notify_taskInterceptionStarted(interception: TaskInterception)
/// Notifies the `URLSessionTask` interception completion.
func notify_taskInterceptionCompleted(interception: TaskInterception)
}

internal class URLSessionRUMResourcesHandler: URLSessionRUMResourcesHandlerType {
internal class URLSessionRUMResourcesHandler: URLSessionInterceptionHandler {
private let dateProvider: DateProvider

// MARK: - Initialization
@@ -24,14 +15,16 @@ internal class URLSessionRUMResourcesHandler: URLSessionRUMResourcesHandlerType
self.dateProvider = dateProvider
}

// MARK: - URLSessionRUMResourcesHandlerType
// MARK: - Internal

weak var subscriber: RUMCommandSubscriber?

func subscribe(commandsSubscriber: RUMCommandSubscriber) {
self.subscriber = commandsSubscriber
}

// MARK: - URLSessionInterceptionHandler

func notify_taskInterceptionStarted(interception: TaskInterception) {
let url = interception.request.url?.absoluteString ?? "unknown_url"

Original file line number Diff line number Diff line change
@@ -6,16 +6,26 @@

import Foundation

/// An interface for sending Tracing `Spans` for given `URLSession` task interception.
internal protocol URLSessionTracingHandlerType {
/// Sends `Span` for given `TaskInterception`.
func sendSpan(for interception: TaskInterception, using tracer: Tracer)
}
internal class URLSessionTracingHandler: URLSessionInterceptionHandler {
// MARK: - URLSessionInterceptionHandler

internal class URLSessionTracingHandler: URLSessionTracingHandlerType {
// MARK: - URLSessionTracingHandlerType
func notify_taskInterceptionStarted(interception: TaskInterception) {
/* no-op */
}

func sendSpan(for interception: TaskInterception, using tracer: Tracer) {
func notify_taskInterceptionCompleted(interception: TaskInterception) {
if !interception.isFirstPartyRequest {
return // `Span` should be only send for 1st party requests
}
guard let tracer = Global.sharedTracer as? Tracer else {
userLogger.warn(
"""
`URLSession` request was completed, but no `Tracer` is registered on `Global.sharedTracer`. Tracing auto instrumentation will not work.
Make sure `Global.sharedTracer = Tracer.initialize()` is called before any network request is send.
"""
)
return
}
guard let resourceMetrics = interception.metrics,
let resourceCompletion = interception.completion else {
return
4 changes: 4 additions & 0 deletions Sources/Datadog/Tracing/Propagation/TracingHTTPHeaders.swift
Original file line number Diff line number Diff line change
@@ -9,6 +9,10 @@ import Foundation
internal struct TracingHTTPHeaders {
static let traceIDField = "x-datadog-trace-id"
static let parentSpanIDField = "x-datadog-parent-id"
static let originField = "x-datadog-origin"
/// Value for `originField` header field, indicating that the request is tracked as RUM Resource by the client.
static let rumOriginValue = "rum"

// TODO: RUMM-338 support `x-datadog-sampling-priority`. `dd-trace-ot` reference:
// https://github.com/DataDog/dd-trace-java/blob/4ba0ca0f9da748d4018310d026b1a72b607947f1/dd-trace-ot/src/main/java/datadog/opentracing/propagation/DatadogHttpCodec.java#L23
}
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ internal class TaskInterception {
/// The initial request send during this interception. It is, the request send from `URLSession`, not the one
/// given by the user (as the request could have been modified in `URLSessionSwizzler`).
internal let request: URLRequest
/// Tells if the `request` is send to a 1st party host.
internal let isFirstPartyRequest: Bool
/// Task metrics collected during this interception.
private(set) var metrics: ResourceMetrics?
/// Task completion collected during this interception.
@@ -20,9 +22,10 @@ internal class TaskInterception {
/// or when the task was created through `URLSession.dataTask(with:url)` on some iOS13+.
private(set) var spanContext: DDSpanContext?

init(request: URLRequest) {
init(request: URLRequest, isFirstParty: Bool) {
self.identifier = UUID()
self.request = request
self.isFirstPartyRequest = isFirstParty
}

func register(metrics: ResourceMetrics) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-2020 Datadog, Inc.
*/

import Foundation

/// An interface for handling `URLSession` interceptions.
internal protocol URLSessionInterceptionHandler {
/// Notifies the `URLSessionTask` interception start.
func notify_taskInterceptionStarted(interception: TaskInterception)
/// Notifies the `URLSessionTask` interception completion.
func notify_taskInterceptionCompleted(interception: TaskInterception)
}
Original file line number Diff line number Diff line change
@@ -34,52 +34,73 @@ internal class URLSessionInterceptor: URLSessionInterceptorType {
private let firstPartyURLsFilter: FirstPartyURLsFilter
/// Filters internal `URLs` used by the SDK.
private let internalURLsFilter: InternalURLsFilter

/// Handles tracing `Span` creation for intercepted resources. `nil` if Tracing is disabled.
internal let tracingHandler: URLSessionTracingHandlerType?
/// Handles RUM Resource creation for intercepted resources. `nil` if RUM is disabled.
internal let rumResourceHandler: URLSessionRUMResourcesHandlerType?
/// Handles resources interception.
/// Depending on which instrumentation is enabled, this can be either RUM or Tracing handler sending respectively: RUM Resource or tracing Span.
internal let handler: URLSessionInterceptionHandler
/// Whether or not to inject tracing headers to intercepted 1st party requests.
/// Set to `true` if Tracing instrumentation is enabled (no matter o RUM state).
internal let injectTracingHeadersToFirstPartyRequests: Bool
/// Additional header injected to intercepted 1st party requests.
/// Set to `x-datadog-origin: rum` if both RUM and Tracing instrumentations are enabled and `nil` in all other cases.
internal let additionalHeadersForFirstPartyRequests: [String: String]?

// MARK: - Initialization

convenience init(
configuration: FeaturesConfiguration.URLSessionAutoInstrumentation,
dateProvider: DateProvider
) {
self.init(
configuration: configuration,
tracingHandler: configuration.instrumentTracing ? URLSessionTracingHandler() : nil,
rumResourceHandler: configuration.instrumentRUM ? URLSessionRUMResourcesHandler(dateProvider: dateProvider) : nil
)
let handler: URLSessionInterceptionHandler

if configuration.instrumentRUM {
handler = URLSessionRUMResourcesHandler(dateProvider: dateProvider)
} else {
handler = URLSessionTracingHandler()
}

self.init(configuration: configuration, handler: handler)
}

init(
configuration: FeaturesConfiguration.URLSessionAutoInstrumentation,
tracingHandler: URLSessionTracingHandlerType?,
rumResourceHandler: URLSessionRUMResourcesHandlerType?
handler: URLSessionInterceptionHandler
) {
self.firstPartyURLsFilter = FirstPartyURLsFilter(configuration: configuration)
self.internalURLsFilter = InternalURLsFilter(configuration: configuration)
self.tracingHandler = tracingHandler
self.rumResourceHandler = rumResourceHandler
self.queue = DispatchQueue(label: "com.datadoghq.URLSessionInterceptor", target: .global(qos: .utility))
self.handler = handler

if configuration.instrumentTracing {
self.injectTracingHeadersToFirstPartyRequests = true

if configuration.instrumentRUM {
// If RUM instrumentation is enabled, additional `x-datadog-origin: rum` header is injected to the user request,
// so that user's backend instrumentation can further process it and count on RUM quota.
self.additionalHeadersForFirstPartyRequests = [
TracingHTTPHeaders.originField: TracingHTTPHeaders.rumOriginValue
]
} else {
self.additionalHeadersForFirstPartyRequests = nil
}
} else {
self.injectTracingHeadersToFirstPartyRequests = false
self.additionalHeadersForFirstPartyRequests = nil
}
}

// MARK: - URLSessionInterceptorType

/// An internal queue for synchronising the access to `interceptionByTask`.
private let queue: DispatchQueue
private let queue = DispatchQueue(label: "com.datadoghq.URLSessionInterceptor", target: .global(qos: .utility))
/// Maps `URLSessionTask` to its `TaskInterception` object.
private var interceptionByTask: [URLSessionTask: TaskInterception] = [:]

func modify(request: URLRequest) -> URLRequest {
guard !internalURLsFilter.isInternal(url: request.url) else {
return request
}
if let tracer = Global.sharedTracer as? Tracer {
if firstPartyURLsFilter.isFirstParty(url: request.url) {
return injectSpanContext(into: request, using: tracer)
}
if injectTracingHeadersToFirstPartyRequests,
firstPartyURLsFilter.isFirstParty(url: request.url) {
return injectSpanContext(into: request)
}
return request
}
@@ -91,15 +112,17 @@ internal class URLSessionInterceptor: URLSessionInterceptorType {
}

queue.async {
let interception = TaskInterception(request: request)
let interception = TaskInterception(
request: request,
isFirstParty: self.firstPartyURLsFilter.isFirstParty(url: request.url)
)
self.interceptionByTask[task] = interception

if let tracer = Global.sharedTracer as? Tracer,
let spanContext = self.extractSpanContext(from: request, using: tracer) {
if let spanContext = self.extractSpanContext(from: request) {
interception.register(spanContext: spanContext)
}

self.rumResourceHandler?.notify_taskInterceptionStarted(interception: interception)
self.handler.notify_taskInterceptionStarted(interception: interception)
}
}

@@ -147,34 +170,39 @@ internal class URLSessionInterceptor: URLSessionInterceptorType {

private func finishInterception(task: URLSessionTask, interception: TaskInterception) {
interceptionByTask[task] = nil

if let tracer = Global.sharedTracer as? Tracer,
firstPartyURLsFilter.isFirstParty(url: interception.request.url) {
tracingHandler?.sendSpan(for: interception, using: tracer)
}

self.rumResourceHandler?.notify_taskInterceptionCompleted(interception: interception)
handler.notify_taskInterceptionCompleted(interception: interception)
}

// MARK: - SpanContext Injection & Extraction

private func injectSpanContext(into request: URLRequest, using tracer: Tracer) -> URLRequest {
private func injectSpanContext(into firstPartyRequest: URLRequest) -> URLRequest {
guard let tracer = Global.sharedTracer as? Tracer else {
return firstPartyRequest
}

let writer = HTTPHeadersWriter()
let spanContext = tracer.createSpanContext()

tracer.inject(spanContext: spanContext, writer: writer)

var newRequest = request
var newRequest = firstPartyRequest
writer.tracePropagationHTTPHeaders.forEach { field, value in
newRequest.setValue(value, forHTTPHeaderField: field)
}

additionalHeadersForFirstPartyRequests?.forEach { field, value in
newRequest.setValue(value, forHTTPHeaderField: field)
}

return newRequest
}

private func extractSpanContext(from request: URLRequest, using tracer: Tracer) -> DDSpanContext? {
guard let headers = request.allHTTPHeaderFields else {
private func extractSpanContext(from request: URLRequest) -> DDSpanContext? {
guard let tracer = Global.sharedTracer as? Tracer,
let headers = request.allHTTPHeaderFields else {
return nil
}

let reader = HTTPHeadersReader(httpHeaderFields: headers)
return tracer.extract(reader: reader) as? DDSpanContext
}
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ internal class URLSessionAutoInstrumentation {
}

func subscribe(commandSubscriber: RUMCommandSubscriber) {
interceptor.rumResourceHandler?.subscribe(commandsSubscriber: commandSubscriber)
let rumResourceHandler = interceptor.handler as? URLSessionRUMResourcesHandler
rumResourceHandler?.subscribe(commandsSubscriber: commandSubscriber)
}
}
Loading

0 comments on commit 51cd4a3

Please sign in to comment.