How can I log each request/response using Alamofire?
Asked Answered
S

10

78

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.

Spontaneous answered 4/11, 2014 at 13:32 Comment(0)
S
66

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
Salpinx answered 11/5, 2017 at 13:4 Comment(5)
This is the best option since it does not requiring any changes to existing code.Chert
Great solution. Thanks!Railroader
Small update needed to support Swift 5 / Xcode 11, but works perfectly!Highball
@Highball what' that?Symphysis
@VarunRaj nothing special, just a simple adjustment to latest Swift syntax.Highball
F
49

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)
    })
  }
}
Febricity answered 20/5, 2015 at 16:40 Comment(7)
Awesome, thanks! Could you post the code you have in mind for the responseObject extension?Hillier
Haven't got time to revisit this right now, @MatthieuRiegler. Care to provide a v3-compatible writeup? :)Febricity
@Febricity can we make it call debugLog automatically when request is called?Insipid
@Febricity can we stop the request if some condition met in debugLog(), I tried request.cancel(), but still it goes the the request with result failureInsipid
@vinbhai4u Sorry, not sure…I'm not currently using any of this in production code.Febricity
@Febricity I am trying to use your code, but whats Serializer? is it some kind of JSON parser or something?Insipid
@vinbhai4u Sorry again. Been out of town, and I still don't have any good info for you…. It looks like the raywenderlich tutorial has been updated since I posted this answer, so I have no reference for the code above. Maybe try ResponseSerializer instead of Serializer???Febricity
O
24

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

Orva answered 9/9, 2019 at 22:42 Comment(4)
In Alamofire 5.0.0 I had to replace "DataResponse" by "AFDataResponse"Heavyladen
what if I am using 'AF.request' ?Interspace
This is exactly what I was looking for. @Interspace AF is referencing the Alamofire session. you would then use session.request instead.Cad
I am not sure why but the only callback I am getting is requestDidResume and the rest not firing up. In addition I've also tried using ClosureEventMonitor and getting the same result.Drunkometer
J
11

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
}()
}
Jourdain answered 15/1, 2016 at 12:39 Comment(2)
How do you limit it to an NSURLSession?Peptidase
It works but for some reason, app became way too much slow when Timberjack is active and not all requests are tracked automatically. Maybe that's by-design.Highball
N
7

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
  }
 }
Narcolepsy answered 24/6, 2020 at 18:14 Comment(0)
I
6

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

Insipid answered 20/1, 2017 at 17:1 Comment(0)
H
2

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.

Heavyladen answered 1/3, 2020 at 12:20 Comment(1)
It's not logging all the data, my body is getting trimmed. Do not use NSLog.Rhonarhonchus
K
1

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

Kahl answered 6/6, 2017 at 13:46 Comment(0)
I
0

In Alamofire 5 and above you can get curl request details by the below code:

request.cURLDescription(calling: { (curl) in
    print(curl)
})

And response/error data:

request.responseDecodable { (response:AFDataResponse<T>) in
            
            switch response.result {
            case .success(let value):
                
                var responseMessage : String?
                
                if let data = response.data {
                    let json = String(data: data, encoding: String.Encoding.utf8)
                    responseMessage = String(describing: json)
                }
                
                print(responseMessage)
                
                break;
                
            case .failure(let error):
                
                var message : String?
                
                if let data = response.data {
                    let json = String(data: data, encoding: String.Encoding.utf8)
                    message = String(describing: json)
                }
                
                print(message)
                
                break
            }
            
        }
Illstarred answered 21/1, 2022 at 16:32 Comment(0)
R
0

Alamofire provides a powerful way to gain insight into all the internal events via the EventMonitor protocol. EventMonitor includes several Alamofire events such as URLSessionDelegate request events. This makes EventMonitor ideal for logging events.

import Alamofire

class NetworkLogger: EventMonitor {
  
  let queue = DispatchQueue(label: "com.company.project.networklogger")
  
  func requestDidFinish(_ request: Request) {
    print(request.description)
  }
  
  func request<Value>(
    _ request: DataRequest,
    didParseResponse response: DataResponse<Value, AFError>
  ) {
    guard let data = response.data else {
      return
    }
    if let json = try? JSONSerialization
      .jsonObject(with: data, options: .mutableContainers) {
        print(json)
    }
  }
}

Reference: https://www.raywenderlich.com/11668143-alamofire-tutorial-for-ios-advanced-usage#toc-anchor-004

Ratliff answered 27/4, 2022 at 11:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.