A colleague was running one of the apps I was working on. He was using the simulator trying to debug a network issue. Since he's not an iOS developer, he has no adequate lldb skills to add symbolic breakpoints and what not. He was looking at me and pointing at the Xcode console window, asking me why I don't log the HTTP request and response pair.

I was able to quickly dismiss his question by pointing that he could get the information he seeks from other sources, such as Charles or Wireshark. However, his expectation of finding a log entry in the console got me thinking; While his enquiry was quick to dismiss, my way of resolving it left me unsatisfied. Surely there must be a standard for what's considered good logging.

A good logging standard does not clutter the console screen, since the console should be also used for lldb commands. Good logging should also not have side effects, such as affecting the application performance, or the possible embarrassment of leaving some private logging in production app.

This article will try to make a case for using DTrace for logging HTTP requests and responses.

DTrace

DTrace definition from wikipedia:

DTrace is a comprehensive dynamic tracing framework created by Sun Microsystems for troubleshooting kernel and application problems on production systems in real time.

DTrace was designed to be an extremely versatile tracing framework; It works by writing tracing scripts in D programming language. These D scripts define a set of probes, that will be called when specific events occur on the system being traces. Probes have actions associated to them, these actions will be executed once the probes get called.

Probes can vary from I/O access, function calling, to network access.

Since DTrace is used to troubleshooting the kernel, it has as little effect as possible on the running system. As such, no performance impact is introduced to the system when no probes are installed, and only a minimal impact is inflicted when some probes are active.

These characteristic makes DTrace a perfect solution for tracing network requests and response; since network access tends to increase in time, it will soon end up cluttering the console, leaving no space for other kind of logs. Also the fact that we can leave DTrace probes in production code, makes it convenient to be used as the tracing and logging practice for network requests and responses.

DTrace static probes

The real benefit of using DTrace is when using static probes. Using static probes allow us to send custom information with the probe events, for example, when tracing an HTTP request, we can send the URL, the HTTP header, the body, etc... as probe custom data.

In order to use static probes, we have to follow these steps:

  • Define the probe interface using a DTrace probe definition script
  • Generate the DTrace header files to include in our project
  • Check if the probes are enable
  • Package and send the DTrace event
  • Create a DTrace script to register the probe
  • Parse the passed event custom data
  • Analyse of the event data and print them on out in an readable format

For that reason, static probes are rarely used in our day-to-day tracing needs.

Easier DTrace

The previous steps describes the process for creating and using static probes for tracing any generic event. If however, the events you are interested to trace are in the domain of network request and response. Then by using dtracer command line tool, and it's objective c library OADTracer you could gain the advantage of DTrace with less hassle.

The process of using dtracer and OADTracer looks like the following:

  • Install dtracer gem

    gem install dtracer
    
  • Add OADTracer to your objc project

    # In your Podfile
    pod "OADTracer"
    
  • Use one of the method of OADTracer to send a DTrace event from the objc application.
  • Register a dtrace probe using dtracer

Using OADTracer and dtracer reduced the steps from 7 to 2 (well, 4 if you count the one time only tools installation). Not only does using OADTracer and dtracer decrease the steps, it also introduce some customisation on how the event data is formatted and printed.

The next sections will describes the way dtracer formats and prints the data.

HTTP Request probe

A request, in the form of NSURLReqesut can be sent to dtracer using - [OADTracer traceRequest:]

In your objective c application you execute the following:

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.google.com"]];

[[OADTracer instance] traceRequest:request];

dtracer provides two ways of formatting the NSURLRequest: as a curl command, and as a detailed string.

dtracer curl will register a probe that will receive the NSURLRequest and converts it to a curl command that.

gif 2

dtracer details will register a probe that will receive the NSURLRequest and prints a digits for that request.

gif 2

The string formatted with dtracer details can be customised by passing flags to the command line app. For example dtracer details -u -m will only output the HTTP method, and the URL.

For a comprehensive list of all the flags, hit dtracer help details.

HTTP Response probe

HTTP response can be send to dtracer, by calling - [OADTracer traceResponse:data:error:] and passing a tuple of NSURLResponse, NSData and NSError. These three parameters are passed together in the callback block passed by NSURLSession dataTaskWithRequest.

[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

   //Send the dtrace events here
   [[OADTracer instance] traceResponse:response data:data error:error];

 }] resume];

Next, in the terminal executing dtracer response, which will receive the DTrace event, and outputs it in readable digest.

gif 3

Custom string probe

Alternatively, you can even send a string and have it printed out. That is achieved by calling the following in your objc application:

[[OADTracer instance] traceString:@"Some string"];

And then on your terminal execute dtracer custom

gif 1

Last thoughts

Since DTrace is an extremely cheap tracing framework, the code for tracing HTTP requests and HTTP responses, can be left in production code. There should be no need to remove the code when building for production versus for development.

Also, DTrace works on the simulator only, there should be no privacy consideration in tracing all the requests and response the app is doing, since in the case of the simulator, you already have the source code of the application on your machine.


More reading on DTrace