CFNetwork Diagnostic Logging
Q: My app makes extensive use of Foundation’s networking classes. When I encounter a problem with that networking, are there any tools to help debug it?
A: When debugging a networking problem, a good place to start is a packet trace. Getting a Packet Trace describes how to get a packet trace on both macOS and iOS.
Packet traces are a very important tool but they have two drawbacks:
If the data is protected by TLS (Transport Layer Security), or its predecessor SSL (Secure Sockets Layer), it will not be directly visible in the packet trace.
A packet trace offers no insight into the internal state of CFNetwork, the framework that implements the core of Foundation’s networking classes.
Fortunately there is a technique that avoids both of these drawbacks: CFNetwork diagnostic logging. When you enable this feature, CFNetwork will log a lot of useful information about its progress; you can examine those logs to help debug any networking problems you encounter. This feature is supported on both iOS and macOS.
You enable CFNetwork diagnostic logging via the
CFNETWORK_DIAGNOSTICS environment variable. This should be set to an integer value from 0 to 3, where 0 is off and higher numbers give you progressively more logging. During normal development you can set this environment variable via Xcode’s scheme editor. When you run your app from Xcode, the CFNetwork log entries will appear in Xcode’s debug console area (if that’s not visible, choose View > Debug Area > Show Debug Area to show it).
You must restrict access to any logs you capture.
If you build an app with logging enabled via the technique shown in Logging Outside of Xcode, make sure that anyone who receives that app understands the security implications of using it.
If you send a log to Apple, make sure to redact any security-sensitive information.
Logging Outside of Xcode
If you need to investigate problems outside of Xcode, you can enable CFNetwork diagnostic logging programmatically using the code shown in Listing 1.
Listing 1 Programmatically enabling CFNetwork diagnostic logging
setenv("CFNETWORK_DIAGNOSTICS", "3", 1);
You should do this right at the beginning of the app’s launch sequence. Normally putting this at the start of
main is sufficient, but if you have C++ static initialisers that use CFNetwork you’ll have to run it before them.
Oh, and in Swift you can remove the semicolon!
How you get at the resulting log entries depends on your specific situation:
On macOS, if you can reproduce the problem locally, you can run the Console utility on your Mac and view log entries there.
On iOS, if you can reproduce the problem locally and it’s OK to connect the device to your Mac via USB, you can run the Console utility on your Mac and view log entries there. Make sure you select your iOS device from the source list on the left (choose View > Show Sources if it’s not visible).
If neither of the above work for you — for example, if you’re trying to debug a problem that can only be reproduced by one of your users in the field — you can use the sysdiagnose mechanism to recover a log from the machine exhibiting the problem. See the Bug Reporting > Profiles and Logs page for details on how to do this.
Document Revision History
Updated for iOS 10 and macOS 10.12.
New document that describes CFNetwork diagnostic logging, an important debugging technique for apps that use Foundation or Core Foundation networking.