68
votes

Is there a way to log each request / response using Alamofire (something similar to AFNetworkActivityLogger) ?

I am aware of Printable, DebugPrintable and Output (cURL) but they are not quite what I am looking for.

8

8 Answers

57
votes

There's a sweet little pod for this: https://github.com/konkab/AlamofireNetworkActivityLogger

Add this to your podfile:

pod 'AlamofireNetworkActivityLogger', '~> 2.0'

In your AppDelegate:

import AlamofireNetworkActivityLogger

Then in your didFinishLaunchingWithOptions, add this:

NetworkActivityLogger.shared.level = .debug
NetworkActivityLogger.shared.startLogging()

EDIT: I've actually encountered crashes with this in production. To be on the safe side, use "build flags" to only use this in debug, something like this:

#if DEBUG
    NetworkActivityLogger.shared.level = .debug
    NetworkActivityLogger.shared.startLogging()
#endif
48
votes

Something like this might be what you were looking for:

extension Request {
   public func debugLog() -> Self {
      #if DEBUG
         debugPrint(self)
      #endif
      return self
   }
}

Usage:

Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
         .debugLog()
         .response {…}

If you want to print all responses, you could write your own response method, similar to the responseObject() method at the top of this tutorial:

http://www.raywenderlich.com/87595/intermediate-alamofire-tutorial

[Update: added below per the request from @trauzti.]

Here's how one might do the responseObject() approach in order to print output on every request.

Caveat lector: I haven't personally tested this code, and would probably make different choices in production. This simply shows how the Wenderlich tutorial code can include debug logging. Also note: since the tutorial is pre-Swift 2.0, I've used the old println() instead of print().

@objc public protocol ResponseObjectSerializable {
  init(response: NSHTTPURLResponse, representation: AnyObject)
}

extension Alamofire.Request {
  public func responseObject<T: ResponseObjectSerializable>(completionHandler: (NSURLRequest, NSHTTPURLResponse?, T?, NSError?) -> Void) -> Self {
    let serializer: Serializer = { (request, response, data) in

      #if DEBUG
         println("Request: \(request.URL)")
      #endif

      let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
      let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
      if response != nil && JSON != nil {
        #if DEBUG
           println("Response:")
           debugPrint(JSON)
        #endif

        return (T(response: response!, representation: JSON!), nil)
      } else {
        #if DEBUG
           println("Failed Serialization:")
           debugPrint(serializationError)
        #endif

        return (nil, serializationError)
      }
    }

    return response(serializer: serializer, completionHandler: { (request, response, object, error) in
      completionHandler(request, response, object as? T, error)
    })
  }
}
15
votes

Since Alamofire 5, the easiest way is to define an EventMonitor subclass:

final class AlamofireLogger: EventMonitor {
    func requestDidResume(_ request: Request) {
        let body = request.request.flatMap { $0.httpBody.map { String(decoding: $0, as: UTF8.self) } } ?? "None"
        let message = """
        ⚡️ Request Started: \(request)
        ⚡️ Body Data: \(body)
        """
        NSLog(message)
    }

    func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value>) {
        NSLog("⚡️ Response Received: \(response.debugDescription)")
    }
}

Then use it on your session:

let session = Session(eventMonitors: [ AlamofireLogger() ])

This sample code was adapted from https://github.com/Alamofire/Alamofire/issues/2867#issuecomment-509662892

11
votes

Timberjack is what you are looking. Timberjack is a simple, unintrusive network activity logger. Log every request your app makes, or limit to only those using a certain NSURLSession if you’d prefer. It also works with Alamofire, if that’s your thing.

https://cocoapods.org/pods/Timberjack

usage:

import Alamofire
import Timberjack

class HTTPManager: Alamofire.Manager {
static let sharedManager: HTTPManager = {
    let configuration = Timberjack.defaultSessionConfiguration()
    let manager = HTTPManager(configuration: configuration)
    return manager
}()
}
6
votes

Adding to above answer for Alamofire 4.0+ Swift 3

extension DataRequest {        
        public func LogRequest() -> Self {
        //Your logic for logging
        return self
    }
}

When Requesting

Alamofire.request(requestUrl, method: .post, parameters: parameter, encoding: JSONEncoding.default)
            .LogRequest()
            .responseJSON { response in
            //Do your thing
            }

If you want to cancel the request in any case(which was something I wanted) you can self.cancel() anywhere before you return self

3
votes

in Alamofire 5 URLRequest is created asynchronously which means

extension Request {
 public func debugLog() -> Self {
  #if DEBUG
     debugPrint(self)
  #endif
  return self
  }
}

is not the best solution anymore. instead, calling cURLDescription is recommend as below:

let request = AF.request(<Your request>))
request.cURLDescription { (curl) in
   print("CURL \(curl)")
}
request.responseJSON { response in
   //Do something with your response...
}

or

extension Request {
public func debugLog() -> Self {
    #if DEBUG
    cURLDescription(calling: { (curl) in
        debugPrint("=======================================")
        print(curl)
        debugPrint("=======================================")
    })
    #endif
    return self
  }
 }
1
votes

SOLUTION FOR SWIFT 3.0+

For Printing Request parameter and headers:

Alamofire.request(url, method: .get, parameters: parameters, headers: headers)
            .validate()
            .responseObject { (response: DataResponse<T>) in
                self.pendingRequests.removeValue(forKey: endPoint)
                completion!(response)

                if(NetworkConfig.loggingEnable) {
                    debugPrint("************* printing REQUEST parameter and Headers *************")
                    debugPrint("RESPONSE : \(response.debugDescription)")
                }
        }.responseDebugPrint()

For Printing Response . use below extension .

import Foundation
import Alamofire

extension Alamofire.DataRequest {
    func responseDebugPrint() -> Self {
        if NetworkConfig.loggingEnable {

            return responseJSON() {
                response in
                if let  JSON = response.result.value,
                    let JSONData = try? JSONSerialization.data(withJSONObject: JSON, options: .prettyPrinted),
                    let prettyString = NSString(data: JSONData, encoding: String.Encoding.utf8.rawValue) {
                    print(prettyString)
                } else if let error = response.result.error {
                    print("Error Debug Print: \(error.localizedDescription)")
                }
            }
        }
        return self
    }
}

Small gist for you : https://gist.github.com/manishpathak99/348f2eb0167c0ff6e12ecd667612bc9b/edit

1
votes

In Alamofire 5.0.0 I used the answer based on: https://github.com/Alamofire/Alamofire/issues/2867#issuecomment-509662892 but I had to replace DataResponse by AFDataResponse. For example:

import Alamofire

final class AlamofireLogger: EventMonitor {

    func requestDidResume(_ request: Request) {

        let allHeaders = request.request.flatMap { $0.allHTTPHeaderFields.map { $0.description } } ?? "None"
        let headers = """
        ⚡️⚡️⚡️⚡️ Request Started: \(request)
        ⚡️⚡️⚡️⚡️ Headers: \(allHeaders)
        """
        NSLog(headers)


        let body = request.request.flatMap { $0.httpBody.map { String(decoding: $0, as: UTF8.self) } } ?? "None"
        let message = """
        ⚡️⚡️⚡️⚡️ Request Started: \(request)
        ⚡️⚡️⚡️⚡️ Body Data: \(body)
        """
        NSLog(message)
    }

    func request<Value>(_ request: DataRequest, didParseResponse response: AFDataResponse<Value>) {

        NSLog("⚡️⚡️⚡️⚡️ Response Received: \(response.debugDescription)")
        NSLog("⚡️⚡️⚡️⚡️ Response All Headers: \(String(describing: response.response?.allHeaderFields))")
    }
}

And then you can use it in the following way:

let session = Session(eventMonitors: [ AlamofireLogger() ])

As it has explained by 0xced in an aforementioned post.