Centralized Logging of iPhone apps with Papertrail

Papertrail for iPhone

I use Papertrail for all my server logging needs, and love it. I’m almost always bullish on the use of third party services to offload work from a product dev team. Not too many people want to spend time working on log drains, me included.

When doing iPhone development, working with logs outside of XCode is clumsy at best and usually nonexistent. And when your beta is in the wild, that’s not even an option because you’re unable to round up all your testers’ devices.

Don’t worry, I’m here to tell you your iOS app can be configured to automatically upload logs to Papertrail as described here within 15 minutes.

###Status Quo The standard solution for all this are Crash Reports via Apple Connect, Crashlytics, Parse, etc. But these only take you so far because sometimes you need more than a call stack. These logs will complement the crash report.

###Reference Material

  • CocoaLumberjack is a pod that enables one to easily and asynchronously carry logs from the device to a destination, whether it be a terminal, a file, or a service. It’s the keystone to this arch, if you get what I’m saying.
  • PapertrailLumberjack allows one to transfer the logs from your iPhone to the Papertrail service.
  • Unfortunately, the instructions for PapertrailLumberjack are only talking about Objective C, and they refer to an outdated CocoaLumberjack.

###Issues

  1. The PapertrailLumberjack pod doesn’t support the current CocoaLumberjack.

    No worries. There is a patched PaperTrailLumberjack that supports CocoaLumberjack 2.x.

  2. PapertrailLumberjack incompatible with Swift CocoaLumberjack.

    I never did get the Swift version of CocoaLumberjack working alongside the Obj C version of PapertrailLumberjack (there’s a nightmare waiting to happen when converting ObjC Macros to Swift) so I’m using the ObjC version with my own Swift wrapper as you can see below.

As Swift and CocoaPods mature, PaperTrailLumberjack will eventually just work out of the box. Until then:

###Steps:

  1. Add CocoaLumberjack 2.x and the patched PaperTrailLumberjack to your Podfile, then pod install.

     pod 'CocoaLumberjack', git: 'git@github.com:dimroc/CocoaLumberjack.git'
     pod 'PaperTrailLumberjack', git: 'https://bitbucket.org/luisrecuenco/papertraillumberjack.git'
    
  2. Add the #imports to your bridging header file.

     #import "CocoaLumberjack.h"
     #import "RMPaperTrailLogger.h"
    
  3. For Swift, add my DDLogHelper to your project (copy pasta).

public let defaultDebugLevel = DDLogLevel.Debug public let defaultTag = NSString(format: "ManhattanForum")

class DDLogHelper { class func SwiftLogMacro(async: Bool, level: DDLogLevel, flag flg: DDLogFlag, context: Int = 0, file: String = FILE, function: String = FUNCTION, line: UWord = LINE, tag: AnyObject? = defaultTag, format: String, args: CVaListPointer) { let string = NSString(format: format, arguments: args) as String SwiftLogMacro(async, level: level, flag: flg, context: context, file: file, function: function, line: line, tag: tag, string: string) }

class func SwiftLogMacro(isAsynchronous: Bool, level: DDLogLevel, flag: DDLogFlag, context: Int = 0, file: String = __FILE__, function: String = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = defaultTag, string: String) {
    // Tell the DDLogMessage constructor to copy the C strings that get passed to it.
    // Had to hardcode context to 0.
    let logMessage = DDLogMessage(message: string, level: level, flag: flag, context: context, file: file, function: function, line: line, tag: tag, options: DDLogMessageOptions.CopyFunction | DDLogMessageOptions.CopyFile, timestamp: NSDate())

    DDLog.log(isAsynchronous, message: logMessage)
}

class func debug(logText: String, level: DDLogLevel = defaultDebugLevel, file: String = __FILE__, function: String = __FUNCTION__, line: UWord = __LINE__, asynchronous async: Bool = true, args: CVarArgType...) {
    SwiftLogMacro(async, level: level, flag: DDLogFlag.Debug, file: file, function: function, line: line, format: logText, args: getVaList(args))
} }</pre></noscript><script src="https://gist.github.com/dimroc/2aef1b6b1e391f0085d2.js"> </script>
  1. Configure DDLog and PapertrailLumberjack with your settings.

     class func launch() { // Or any initializer method
       let paperTrailLogger = RMPaperTrailLogger.sharedInstance()
       paperTrailLogger.host = "MyHost.papertrailapp.com"
       paperTrailLogger.port = myportnumber
    
       DDLog.addLogger(paperTrailLogger)
       DDLog.addLogger(DDASLLogger.sharedInstance())
       DDLog.addLogger(DDTTYLogger.sharedInstance())
     }
    
  2. Log away.

     DDLogHelper.debug("Launching Papertrail logging for My App")
    

###Conclusion Compared to what I was doing before, I’m as happy as can be. And this was all with a free hosting plan. I’m still waiting to see what the impact on battery life and performance is. If you have any experience with this, drop me a line below.

comments powered by Disqus