Network Capture
The Embrace SDK's NetworkCaptureService
automatically captures HTTP/HTTPS network requests and responses in your app, providing visibility into API performance, error rates, and data transfer volumes.
How Network Capture Works
The network capture service instruments URLSession-based networking in your app by swizzling key methods in the URLSession and URLSessionTask classes. This allows Embrace to create OpenTelemetry spans for each network request that capture:
- Request URL, method, and headers
- Response status code and headers
- Request and response body size
- Network timing metrics (DNS lookup, TLS handshake, time to first byte, etc.)
- Error information (if applicable)
This data helps identify slow APIs, track error rates, and understand the network performance impact on your app.
By default, the SDK captures raw network data and does not automatically scrub personally identifiable information (PII) or sensitive data. You should configure header filters, URL pattern blacklists, and custom data sources as described below to prevent capturing sensitive information.
Configuration
You can customize network capture behavior when initializing the Embrace SDK:
let services = CaptureServiceBuilder()
.add(.network(options: NetworkCaptureService.Options(
captureRequestHeaders: true,
captureResponseHeaders: true,
captureBodies: .none,
urlPatternBlacklist: ["api.example.com/user/.*"],
isEnabledForWebsockets: true,
isEnabledForBackgroundSessions: true
)))
.addDefaults()
.build()
try Embrace
.setup(
options: Embrace.Options(
appId: "APPID",
captureServices: services
//...other options
)
)
.start()
Customization Options
Header Capture
You can control whether request and response headers are captured:
NetworkCaptureService.Options(
captureRequestHeaders: true,
captureResponseHeaders: true
)
By default, certain sensitive headers like "Authorization" and "Cookie" are not captured. You can further customize this with header filters:
NetworkCaptureService.Options(
captureRequestHeaders: true,
requestHeadersFilter: { headerName, headerValue in
// Only capture specific headers
return ["Content-Type", "User-Agent", "Accept"].contains(headerName)
},
captureResponseHeaders: true,
responseHeadersFilter: { headerName, headerValue in
// Filter out sensitive data
return !headerName.contains("secret")
}
)
Body Capture
Control how request and response bodies are captured:
NetworkCaptureService.Options(
captureBodies: .all // Capture all bodies
// OR
captureBodies: .none // Don't capture any bodies
// OR
captureBodies: .errorOnly // Only capture bodies for failed requests
// OR
captureBodies: .custom { request, response, error in
// Custom logic to determine if bodies should be captured
return response?.statusCode == 500 || error != nil
}
)
For security and performance reasons, body capture is disabled by default.
URL Filtering
You can blacklist specific URL patterns to prevent sensitive endpoints from being captured:
NetworkCaptureService.Options(
urlPatternBlacklist: [
"api.example.com/user/profile",
"api.example.com/payment/.*"
]
)
The patterns support regular expressions for flexible matching.
WebSocket and Background Session Support
Enable or disable network capture for WebSocket connections and background URLSessions:
NetworkCaptureService.Options(
isEnabledForWebsockets: true,
isEnabledForBackgroundSessions: true
)
Understanding Network Data
Network requests are captured as OpenTelemetry spans with the following attributes:
http.url
: The request URLhttp.method
: The HTTP method (GET, POST, etc.)http.status_code
: The response status codehttp.request_content_length
: Size of the request body in byteshttp.response_content_length
: Size of the response body in byteshttp.request.headers.*
: Request headers (if enabled)http.response.headers.*
: Response headers (if enabled)emb.network.error
: Error information (if an error occurred)emb.network.dns_start
andemb.network.dns_end
: DNS resolution timingemb.network.connect_start
andemb.network.connect_end
: Connection timingemb.network.tls_start
andemb.network.tls_end
: TLS handshake timing (for HTTPS)emb.network.request_start
andemb.network.request_end
: Request transmission timingemb.network.response_start
andemb.network.response_end
: Response reception timing
Example Use Cases
API Performance Monitoring
Track the performance of your backend APIs and identify slow endpoints that may be impacting user experience.
Error Rate Tracking
Monitor API failure rates to quickly identify when backend services are experiencing issues.
Network Dependency Analysis
Understand how network performance affects key user flows and identify opportunities for optimization.
Bandwidth Usage Monitoring
Track the amount of data your app is transferring to help optimize for users on limited data plans.
Best Practices
- Be selective about capturing headers and bodies to avoid privacy concerns and excessive data transfer
- Use URL blacklisting to exclude sensitive endpoints from being captured
- Consider implementing request batching or compression for frequently called APIs
- Add custom attributes to network spans for better context (see the Custom Attributes section)
- Monitor both success rates and performance metrics for critical APIs
Adding Custom Attributes
You can add custom attributes to network spans by implementing a custom delegate:
class MyNetworkDelegate: NetworkCaptureServiceDelegate {
func willBeginRequest(_ request: URLRequest) -> [String: String]? {
// Return attributes to add to the span when a request begins
return [
"user_journey": "checkout",
"app_state": UIApplication.shared.applicationState.rawValue.description
]
}
func didCompleteRequest(_ request: URLRequest,
response: URLResponse?,
error: Error?) -> [String: String]? {
// Return attributes to add to the span when a request completes
var attributes: [String: String] = [:]
if let httpResponse = response as? HTTPURLResponse {
attributes["content_type"] = httpResponse.value(forHTTPHeaderField: "Content-Type") ?? "unknown"
}
return attributes
}
}
// Then use this delegate when configuring the service
let myDelegate = MyNetworkDelegate()
NetworkCaptureService.Options(
delegate: myDelegate
)