HTTPClient: verbose logs for request IDs (#3320)

See https://github.com/RevenueCat/khepri/pull/6798.
This can help debug issues as it helps us cross-reference requests on
our end.

### Changes:
- Logging `X-Request-ID` and `X-Amzn-Trace-ID` headers if present (only
in verbose mode)
- Logging these even for failed requests
- Also logging `request_handled_by_load_shedder` for failed requests
- To accomplish that. `HTTPURLResponse` now conforms to our
`HTTPResponseType`, this allows us to extract headers in an uniform way

### Example:
```
[network] DEBUG: ℹ️ API request completed: GET '/v1/subscribers/$RCAnonymousID%3Ad9dc4f3b33054cf28c0f2ec9da0f48d5' (304)
Request-ID: 'be534c50-5231-403b-be2f-7c077861751d'; Amzn-Trace-ID: 'Root=1-6532c6ca-7ef80f961ec9c4145484df13'
```
This commit is contained in:
NachoSoto authored and GitHub committed 2023-10-25 20:24:23 -07:00
1 parent 38b4e3b411
commit ed80980d08
19 files changed
+161 -83

No files matched your search

+44 -9
View File
@@ -130,6 +130,8 @@ extension HTTPClient {
case requestDate = "X-RevenueCat-Request-Time"
case contentType = "Content-Type"
case isLoadShedder = "X-RevenueCat-Fortress"
case requestID = "X-Request-ID"
case amazonTraceID = "X-Amzn-Trace-ID"
}
@@ -323,7 +325,7 @@ private extension HTTPClient {
if let response = response, response.verificationResult == .failed {
if case .enforced = request.verificationMode {
return .failure(.signatureVerificationFailed(path: request.httpRequest.path,
code: response.statusCode))
code: response.httpStatusCode))
} else {
// Any other mode gets forwarded as a success, but we log the error
Logger.error(Strings.signing.request_failed_verification(request.httpRequest))
@@ -353,13 +355,16 @@ private extension HTTPClient {
)
if let response = response {
let httpURLResponse = urlResponse as? HTTPURLResponse
switch response {
case let .success(response):
Logger.debug(Strings.network.api_request_completed(
request.httpRequest,
// Getting status code from the original response to detect 304s
// If that can't be extracted, get status code from the parsed response.
httpCode: urlResponse?.httpStatusCode ?? response.statusCode
httpCode: httpURLResponse?.httpStatusCode ?? response.httpStatusCode,
metadata: Logger.verboseLogsEnabled ? response.metadata : nil
))
if response.isLoadShedder {
@@ -367,9 +372,18 @@ private extension HTTPClient {
}
case let .failure(error):
Logger.debug(Strings.network.api_request_failed(request.httpRequest,
httpCode: urlResponse?.httpStatusCode,
error: error))
let httpURLResponse = urlResponse as? HTTPURLResponse
Logger.debug(Strings.network.api_request_failed(
request.httpRequest,
httpCode: httpURLResponse?.httpStatusCode,
error: error,
metadata: httpURLResponse?.metadata)
)
if httpURLResponse?.isLoadShedder == true {
Logger.debug(Strings.network.request_handled_by_load_shedder(request.httpRequest.path))
}
}
request.completionHandler?(response)
@@ -470,6 +484,16 @@ private extension HTTPClient {
// MARK: - Extensions
extension HTTPClient {
/// Information from a response to help identify a request.
struct ResponseMetadata {
var requestID: String?
var amazonTraceID: String?
}
}
extension HTTPRequest {
func requestAddingNonceIfRequired(
@@ -550,7 +574,7 @@ extension Result where Success == Data?, Failure == NetworkError {
return self.flatMap { body in
return .success(
.init(
statusCode: .init(rawValue: response.statusCode),
httpStatusCode: response.httpStatusCode,
responseHeaders: response.allHeaderFields,
body: body
)
@@ -583,7 +607,7 @@ extension Result where Success == VerifiedHTTPResponse<Data>, Failure == Network
// Converts an unsuccessful response into a `Result.failure`
fileprivate func convertUnsuccessfulResponseToError() -> Self {
return self.flatMap {
$0.response.statusCode.isSuccessfulResponse
$0.response.httpStatusCode.isSuccessfulResponse
? .success($0)
: .failure($0.response.parseUnsuccessfulResponse())
}
@@ -602,8 +626,19 @@ private extension VerifiedHTTPResponse {
}
}
}
private extension HTTPResponseType {
var isLoadShedder: Bool {
return self.response.value(forHeaderField: HTTPClient.ResponseHeader.isLoadShedder) == "true"
return self.value(forHeaderField: HTTPClient.ResponseHeader.isLoadShedder) == "true"
}
var metadata: HTTPClient.ResponseMetadata {
return .init(
requestID: self.value(forHeaderField: HTTPClient.ResponseHeader.requestID),
amazonTraceID: self.value(forHeaderField: HTTPClient.ResponseHeader.amazonTraceID)
)
}
}
@@ -618,7 +653,7 @@ private extension HTTPResponse where Body == Data {
isJSON
? .from(self.body)
: .defaultResponse,
self.statusCode
self.httpStatusCode
)
}