Moya: Analyzing and logging Moya Errors. Response & request correlation.

Created on 27 Apr 2016  路  16Comments  路  Source: Moya/Moya

Originally Alamofire provides both response and request as a part of Response structure. It helps with debugging and logging of incidents. Current implementation of Moya.Response has only response NSURLResponse (instead of NSHTTPURLResponse in Alamofire) and no request information at all. With such approach when error happened there is no way to find for which request it happened. There is also no way to find header fields from NSURLResponse. With all being said what would be a recommended way of analyzing and logging Moya Errors?

enhancement

Most helpful comment

Would it be a natural thing to return back correlation of request & response into Moya result? Why in Miya.swift on line 207 of Alamofire response, NSURLRequest just discarded, while the rest is propagated as result? It is common feature of networking libraries. It was in Alamofire, MKNetworkingKit etc as well as for libraries on other platforms. As I am not really familiar with details of some architecture decisions It would be interesting to understand what was a decision process for removing this feature from Moya.

All 16 comments

To debug using Moya, I generally take advantage of the NetworkLoggerPlugin. See the Providers documentation.

Yeah, basically you pass the plugins in init of your provider, the same as closures, manager etc. For example something like:

MoyaProvider<GitHub>(plugins: [NetworkLoggerPlugin()])

NetworkLoggerPlugin does not have correlation capabilities. It seems like logging strategy now "all or nothing" while I have a particular need in mind which I believe is common. When error happened I would like to log both response with an error and request that caused it. Does it look like a valid problem?

I've written a whitelist/blacklist logger plugin. It's not elegant (which is why it's not in Moya 馃槢 ) but it works: https://github.com/artsy/eidolon/blob/master/Kiosk/App/Networking/NetworkLogger.swift

Would it be a natural thing to return back correlation of request & response into Moya result? Why in Miya.swift on line 207 of Alamofire response, NSURLRequest just discarded, while the rest is propagated as result? It is common feature of networking libraries. It was in Alamofire, MKNetworkingKit etc as well as for libraries on other platforms. As I am not really familiar with details of some architecture decisions It would be interesting to understand what was a decision process for removing this feature from Moya.

I can think of a few ways to accomplish what you're after.

  1. Hard: Fork Moya and add NSURLRequest to the completion. Then you'd have to maintain a fork, which is no fun.
  2. Reasonable: Fork Moya and add a plugin method to line 210 plugins.forEach { $0.didReceiveRequest(request, result: result, target: target) }. Again a fork, but such a subtle difference that this would probably be easier to maintain. And the debugging would be off on its own, in a plugin, which is nice.
  3. Easy: Just re-create the request in your response handler! There's nothing special about the NSURLRequest instance, and it's easy to create a copy.
provider.request(.Zen) { result in
  let request = Api.Zen.urlRequest
  ...

Better yet would be to wrap this in a networking layer, and then you can send the request back in a custom completion block:

class Network {
  static let provider = MoyaProvider<MyService>()

  static func request(endpoint: MyService, completion: (NSURLRequest, Result) -> Void) {
    provider.request(endpoint) { result in completion(endpoint.urlRequest, result) }
  }
}

Yeah that would be awesome to have request printed such as:
curl -X GET blablabla

You can now get curl commands logged using the included network activity plugin.

@colinta recreating URL requests is not the same as getting a reference to the one that were sent. There could be various random identifiers attached etc, so the question to trace an exact request sent. Somehow "hard way" looks most logical one, which is very unfortunate. Thanks.

@nikita-leonov what do you think, has this issue been addressed to your satisfaction? If not, what are the next steps?

@ashfurrow as for today we do not have a good solution. The only elegant solution is option 1 suggested by @colinta, but maintaining an own fork of Moya at the current stage of our project is killing. Having correlation logging in the plugin does not work as well since such option missing a context of the call and many other benefits of having request together in response in a place of request initialization.
Recreating a call when needed does not work, since as I mentioned it missing parameters uniquely generated for the response.
Do you think re-introducing request & response correlation, that was initially available in AFNetworking would be accepted as PR into Moya? I would be happy to work on it.

I'd be happy to review a PR, I'm not sure what request/response correlation would look like so I can't say for sure if it'd be accepted. Could you explain your implementation idea?

@ashfurrow I provided PR. Understand a point about "no guarantees on accepting it", no worries. I am happy to discuss and provide changes if you think something is not in line with grand-plan of Moya.

PR is merged! Are there any next steps on this issue?

Closing this for now, as it seems this has been resolved by merging the PR. Please reopen if there's anything else that needs discussing! :)

@BasThomas @ashfurrow
Actually merging PR is not enough to get request information when "request" method is failed.

Problem

MoyaProvider<NetworkAPI>().request(.getList) { (result) in
            switch result {
                case .success(let response)
                break
                case .failure(let moyaError)
                // We can not get any request information here
                // Because Response that has request information object not available here
            }
        }

Alamofire has custom struct that has request property, in this way we can grab request information in response closure like below

Alamofire Able To Do This

        Alamofire.request(url,
                          method: method,
                          parameters: parameters,
                          encoding: encoding,
                          headers: headers)
            .responseJSON { (dataResponse) in

                if let request = dataResponse.request {
                    ...
                }
}

Alamofire Data Response Struct

public struct DataResponse<Value> {
    /// The URL request sent to the server.
    public let request: URLRequest?
...
}

Moya Compilation

public typealias Completion = (_ result: Result<Moya.Response, MoyaError>) -> Void

My Offers

1- Add request property to compilation as a new parameter,
2- Create wrapper struct for Result<Moya.Response, MoyaError> like Alamofire did.

public struct MoyaResponse<Value, Error> {
    /// The URL request sent to the server.
    public let request: URLRequest?

    /// The result of response serialization.
    public let result: Result<Value, Error>

    /// Returns the associated value of the result if it is a success, `nil` otherwise.
    public var value: Value? { return result.value }

    /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
    public var error: Error? { return result.error }

    public init(request: URLRequest?,
                result: Result<Value, MoyaError>) {
        self.request = request
        self.result = result
}
Was this page helpful?
0 / 5 - 0 ratings