Since our launch one year ago, Crashlytics has set the bar for the most informative crash reports on mobile. Above and beyond stack traces, RAM usage, and disk utilization, we've sought to provide all the critical data-points that developers need to pinpoint and fix issues - device orientation, battery state, even whether the device was being held up to the ear! And we're never satisfied.
A treasure-trove of data lies in an app's logs and there's no better way to debug a problem than by knowing exactly what happened leading up to the critical moment. Capturing logging data has been our number-one customer request for months and our number-one concern. We care deeply about security and end-user privacy: collecting logging data opens the door to substantial risks. We wanted to begin the path down the road to building a Splunk for Mobile.
I'm excited to announce that after focusing our R&D efforts, we think we've cracked it, and I wanted to share some details on our approach.
The easiest way to deliver logging would be to capture and redirect all output from NSLog(), but this is also the easiest way to infringe user privacy. Many apps don't take the care they should in scrubbing log lines of personally-identifiable information: names, email addresses, even passwords often appear in URLs or internal settings that might commonly get logged. Sending this data, even encrypted over SSL, would be dangerous and in-breach of most privacy policies.
Instead, we've chosen to introduce completely distinct logging functionality called CLSLog(), so it's explicit what data will be collected and transmitted with Crashlytics reports.
We also took the opportunity to make some performance improvements - in our benchmarks, CLSLog() is 10X faster than NSLog() under the same conditions. Using CLSLog() could not be easier - it's a drop-in replacement:
[pyg language="objc" style="monokai" linenos="inline" linenostart="1"] OBJC_EXTERN void CLSLog(NSString *format, ...); // Log messages to be sent with crash reports [/pyg]
[pyg language="objc" style="monokai" linenos="inline" linenostart="1"] NSLog(@"Detected Higgs Boson with mass %f!!", [boson mass]); CLSLog(@"Detected Higgs Boson with mass %f!!", [boson mass]); [/pyg]
Options, Options, Options
Of course, in many case you might want your log messages to also output to the system log, or show up in Xcode's console. For these cases, we've also provided CLSNSLog(), which records the output and then passes it along to NSLog():
[pyg language="objc" style="monokai" linenos="inline" linenostart="1"] OBJC_EXTERN void CLSNSLog(NSString *format, ...); // Log messages to be sent with crash reports as well as to NSLog() [/pyg]
But what if both could happen? In development builds, it would be ideal for everything to pass-thru to Xcode's console so debugging was as easy as possible. In release builds, though, that's nothing but overhead — it would be great to take advantage of the blinding speed of our 100% in-memory implementation of CLSLog().
We've got you covered:
[pyg language="objc" style="monokai" linenos="inline" linenostart="1"] /** * * The CLS_LOG macro provides as easy way to gather more information in your log messages that are * sent with your crash data. CLS_LOG prepends your custom log message with the function name and * line number where the macro was used. If your app was built with the DEBUG preprocessor macro * defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog. * If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only, for a ~10X speed-up. * * Example output: * -[AppDelegate login:] line 134 $ login start * **/ #ifdef DEBUG #define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) #else #define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) #endif [/pyg]
In Debug builds, CLS_LOG() will pass-thru to NSLog, but in Release builds, it will be as fast as possible:
[pyg language="objc" style="monokai" linenos="inline" linenostart="1"] CLS_LOG(@"Higgs-Boson detected! Bailing out... %@", attributesDict); [/pyg]
We've designed our custom logging functionality from the ground-up to respect your end-users network connections and your app's performance. Since it's implementation is entirely in-process, it's blazingly fast with no IPC overhead. It also accepts as much data as you choose to throw at it: CLSLog() maintains an auto-scrolling 64kb buffer of your log data, which is more than enough to record what happened in the moments leading up to a crash without exploding your app's memory requirements or your end-users cellular data plan. Believe it or not, it's even more memory-efficient than it sounds - our advanced architecture doesn't even require holding all 64kb in RAM!
That's Not All...
Viewing logging information is a whole other story. Rather than explain it, I'd encourage you to head over to our SDK Overview and see for yourself! We're hard at work on additional SDK functionality and have much more to talk about in the coming weeks - stay tuned!