{"id":7,"date":"2013-02-10T21:44:34","date_gmt":"2013-02-11T03:44:34","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=7"},"modified":"2014-06-15T09:44:29","modified_gmt":"2014-06-15T15:44:29","slug":"7","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/7\/","title":{"rendered":"Improved iOS Logging with Lumberjack"},"content":{"rendered":"<p><b>Edit:<\/b>  Lumberjack in it&#8217;s current form isn&#8217;t compatible with Apple&#8217;s new Swift.  If you&#8217;re playing with Swift and looking for some logging alternatives in the meantime, see <a href=\"http:\/\/dev.iachieved.it\/iachievedit\/?p=256\">our latest post<\/a>.<\/p>\n<p>For our very first blog entry, we&#8217;re going to introduce one of our favorite tools for debugging iOS applications, and that&#8217;s a logging framework known as Lumberjack. \u00a0If you&#8217;ve used NSLog thus far to debug or provide trace statements for your applications, you are in for a treat.<\/p>\n<p>We&#8217;ll walk you through adding Lumberjack to your XCode project one step at a time, but feel free to take a detour and check Lumberjack out at\u00a0https:\/\/github.com\/robbiehanson\/CocoaLumberjack.<\/p>\n<p>We&#8217;ll assume that you have little experience with XCode projects, but you should at least have XCode installed and should have familiarity with using the File menu to create a new project. \u00a0So go ahead and do that now, and create a Single View Application:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/singleviewapp_png.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-17\" alt=\"singleviewapp_png\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/singleviewapp_png.png\" width=\"736\" height=\"494\" \/><\/a><\/p>\n<p>For your project options, if you don&#8217;t have an Organization Name or Company Identifier, you can easily make something up here. \u00a0Once you are ready to start your <em>real\u00a0<\/em>application, you&#8217;ll want to put some additional thought here. \u00a0Of course we&#8217;ve used our LLC name, but you can use Example Company and com.example. if you like. \u00a0For the Product Name something like lumberjackExample will suffice, and of course you will want to Use Storyboards and Use Automatic Reference Counting (unless you don&#8217;t want to use them).<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/project_options_png.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-18\" alt=\"project_options_png\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/project_options_png.png\" width=\"733\" height=\"496\" \/><\/a><\/p>\n<p>And finally, find a\u00a0<em>parent folder\u00a0<\/em>where you want to create your\u00a0<em>project folder.<\/em> \u00a0We&#8217;re going to put everything under\u00a0<strong>blogging<\/strong>, and XCode will create a folder underneath called <strong>lumberjackExample<\/strong>. \u00a0Although you see\u00a0<em>Create local git repository for this project\u00a0<\/em>checked in the dialog below, we actually unchecked it because at the end of this tutorial you will be able to download the entire example project (actually you don&#8217;t have to wait until the end) from GitHub. \u00a0Until you start using GitHub to store your projects, we recommend you leave it checked.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/project_folder_png.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-19\" alt=\"project_folder_png\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/project_folder_png.png\" width=\"731\" height=\"630\" \/><\/a><\/p>\n<p>Whew, all that work and we haven&#8217;t even gotten to the good part yet. \u00a0Before we go further with XCode, let&#8217;s go ahead and download Lumberjack and add it to our project. \u00a0First, go to the GitHub for Lumberjack in your browser: \u00a0https:\/\/github.com\/robbiehanson\/CocoaLumberjack, and then hit the Zip button.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/github_lumberjack_png.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-20\" alt=\"github_lumberjack_png\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/github_lumberjack_png.png\" width=\"924\" height=\"128\" \/><\/a><\/p>\n<p>This is going to download a zipped-up version of the framework into your Downloads folder. \u00a0Click on the zipfile, let your Mac uncompress it, and you will find a folder called CocoaLumberjack-master in your Downloads directory. \u00a0There are three directories inside (well, at least there were three at the time of this writing), and only one of them is worth our interest at this point: \u00a0Lumberjack.<\/p>\n<p>Now, with the Finder window open showing you the Lumberjack folder, and with your XCode project in sight next to it, drag and drop that Lumberjack folder into the XCode project, in the left-hand pane where your project files are displayed. \u00a0We prefer to create a group called 3rdParty Libraries and place everything under there, but for this example, just drag and drop under the lumberjackExample folder. \u00a0XCode is going to bring up a dialog, and its important to get these right:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/add_lumberjack_png.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-21\" alt=\"add_lumberjack_png\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/add_lumberjack_png.png\" width=\"734\" height=\"498\" \/><\/a><\/p>\n<p>You definitely want\u00a0<em>Copy items into destination group&#8217;s folder (if needed)\u00a0<\/em>selected, and you probably want\u00a0<em>Create groups for any added folders<\/em>, and you definitely want to add the files to the lumberjackExample target.<\/p>\n<p>Now, you&#8217;ll see that the files have been added to your project:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/lumberjack_example_files_png.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-22\" alt=\"lumberjack_example_files_png\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/lumberjack_example_files_png.png\" width=\"261\" height=\"580\" \/><\/a><\/p>\n<p>You can even go ahead and build the application (under the <strong>Product<\/strong> menu, choose <strong>Build<\/strong>). \u00a0Of course, it won&#8217;t do anything just yet, and we need to add a few helper files to make our life easier.<\/p>\n<p>We&#8217;re going to create some nice macros to help us out while logging, so go ahead and create a new file calling Logging.h. \u00a0To create a simple header file, when presented with the bewildering options of types of files, choose iOS C and C++ files, and then Header file. \u00a0Note that when you are prompted to indicate the location to save the file, XCode puts you in the project directory, which contains your .xcodeproj file\u00a0<em>as well as\u00a0<\/em>another folder named after your project, in this case lumberjackExample. \u00a0If you are lazy you can save the header file right in the top-level project folder, but purists might frown, so go ahead and and double-click on the lumberjackExample folder and save it there. \u00a0Remember to name it Logging.h, and since this is a header file you do not have to add it to a target.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/select_h_file_png.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/select_h_file_png.png\" alt=\"select_h_file_png\" width=\"732\" height=\"494\" class=\"alignnone size-full wp-image-29\" \/><\/a><\/p>\n<p>Now, find the file in your project explorer (the left-hand pane), and let&#8217;s add a bit of code to it:<\/p>\n<p>[objc]<br \/>\n#ifndef lumberjackExample_Logging_h<br \/>\n#define lumberjackExample_Logging_h<\/p>\n<p>#import &quot;DDLog.h&quot;<\/p>\n<p>#define ENTRY_LOG      DDLogVerbose(@&quot;%s ENTRY &quot;, __PRETTY_FUNCTION__);<br \/>\n#define EXIT_LOG       DDLogVerbose(@&quot;%s EXIT &quot;, __PRETTY_FUNCTION__);<br \/>\n#define ERROR_EXIT_LOG DDLogError(@&quot;%s ERROR EXIT&quot;, __PRETTY_FUNCTION__);<\/p>\n<p>#endif<br \/>\n[\/objc]<\/p>\n<p>Okay, that looks good, and we&#8217;ll use it in a moment, but let&#8217;s take a look at our AppDelegate.m class and configure Lumberjack.  If you&#8217;ve never written an iOS application before, there is a lot to learn for sure, but for the sake of this tutorial, let&#8217;s look at only a couple of methods in the application delegate.  Again, the point of this tutorial isn&#8217;t to teach you everything you need to know about iOS development, but to provide you with a tool to debug as you begin to explore.<\/p>\n<p>Before we dive into the AppDelegate methods, we&#8217;re going to add a property to the application delegate which is a reference to a file-based logger we&#8217;re going to create.  <strong>Before<\/strong> the <code>@implementation AppDelegate<\/code> line, add the following:<\/p>\n<p>[objc]<br \/>\n@interface AppDelegate()<\/p>\n<p>@property (strong, nonatomic) DDFileLogger* fileLogger;<\/p>\n<p>@end<br \/>\n[\/objc]<\/p>\n<p>If you&#8217;re not familiar with this syntax, don&#8217;t worry, it will become more clear later on.<\/p>\n<p>The first method we are going to add code to is <\/p>\n<p>[objc]<br \/>\n&#8211; (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions<br \/>\n{<br \/>\n    \/\/ Override point for customization after application launch.<br \/>\n    return YES;<br \/>\n}<br \/>\n[\/objc]<\/p>\n<p>This is the method that is called <strong>by<\/strong> iOS when your application has launched.  It&#8217;s a great place to bootstrap third-party frameworks (which we consider Lumberjack to be), so let&#8217;s do just that:<\/p>\n<p>[objc]<br \/>\n&#8211; (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions<br \/>\n{<br \/>\n  \/\/ Lumberjack<br \/>\n  [DDLog addLogger:[DDTTYLogger sharedInstance]];<br \/>\n  self.fileLogger = [[DDFileLogger alloc] init];<br \/>\n  self.fileLogger.rollingFrequency =  60 * 60 * 24; \/\/ 1 hour rolling<br \/>\n  self.fileLogger.logFileManager.maximumNumberOfLogFiles = 7;<br \/>\n  self.fileLogger.maximumFileSize = 384 * 1024; \/\/ 384K<br \/>\n  [self.fileLogger setLogFormatter:[[DDLogFileFormatterDefault alloc]init]];<br \/>\n  [DDLog addLogger:self.fileLogger];<br \/>\n  return YES;<br \/>\n}<br \/>\n[\/objc]<\/p>\n<p>Now, you&#8217;re first question might be, gee, did I really need all that code for logging?  And the answer is no, you didn&#8217;t, but with this code not only did you get a logger that logs to the console (that first line, [DDLog addLogger:[DDTTYLogger sharedInstance]];), but you also got one that logs to a file (which in future posts we&#8217;ll show you how to mail that file to yourself, how handy is that?), and more the file logger does all sorts of nice things like rolls once an hour, manages the maximum number of log files, the maximum file size of the logs, etc.  There are oodles of options available, and we suggest you read the Lumberjack site in Github for details.<\/p>\n<p>If you tried to compile the application right now (you tried that didn&#8217;t you), it will probably fail, unless you were clever enough to go ahead and add the proper import statements in your AppDelegate.m.  You can either add them there, or if you plan on using logging in a lot of files, go ahead and add them to your .pch (precompiled header) file and the compiler will automatically include it in all other source code modules.  Our .pch contains:<\/p>\n<p>[objc]<br \/>\n#import &quot;DDTTYLogger.h&quot;<br \/>\n#import &quot;DDFileLogger.h&quot;<br \/>\n#import &quot;Logging.h&quot;<br \/>\n[\/objc]<\/p>\n<p>Our application is still boring, and doesn&#8217;t even log anything yet!  Now, let&#8217;s add some logging statements.  Try replacing the method <code>-(void)applicationDidEnterBackground:(UIApplication *)application<\/code> with the following:<\/p>\n<p>[objc]<br \/>\n&#8211; (void)applicationDidEnterBackground:(UIApplication *)application<br \/>\n{<br \/>\n  ENTRY_LOG;<br \/>\n  EXIT_LOG;<br \/>\n}<br \/>\n[\/objc]<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/didEnterBackground_png.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/didEnterBackground_png.png\" alt=\"didEnterBackground_png\" width=\"809\" height=\"74\" class=\"alignnone size-full wp-image-30\" \/><\/a><\/p>\n<p>Oh boy, the compiler complains straight away with nonsense about use of an undeclared identifier called <code>ddLogLevel<\/code>.  You would think this is all becoming more trouble than its worth, but <em>au contraire<\/em>.  <code>ddLogLevel<\/code> is what allows use to have different log levels set for different modules (.m files).  This is incredibly useful, particularly when you have debugged and isolated a given module and you no longer want it generating logs, even during debug mode.  On a per-file basis you can set these levels.  But first we need to set it altogether, so back to the top of our <code>AppDelegate.h<\/code> file, and add the following code between the <code>@interface<\/code> and <code>@implementation<\/code> blocks in <code>AppDelegate.m<\/code>:<\/p>\n<p>[objc]<br \/>\n#ifdef DEBUG<br \/>\nstatic const int ddLogLevel = LOG_LEVEL_VERBOSE;<br \/>\n#else<br \/>\nstatic const int ddLogLevel = LOG_LEVEL_ERROR;<br \/>\n#endif<br \/>\n[\/objc]<\/p>\n<p>This is pretty straightforward &#8211; if you are compiling a debug load, turn the log level to verbose, otherwise, turn it to error.  The default log levels in Lumberjack are Verbose, Info, Warn, and Error.  When Verbose is enabled you&#8217;ll get all four levels; when Info is enabled you&#8217;ll get Info, Warn, Error, and so on and so forth.  See https:\/\/github.com\/robbiehanson\/CocoaLumberjack\/wiki\/CustomLogLevels for more details on the levels, how to customize (we at iAchieved.it have never used the customization facility, the four levels has suited us just fine!).<\/p>\n<p><strong>Now<\/strong>, try running the application either on your phone or in the simulator.  If you&#8217;ve done everything correctly thus far, you&#8217;ll still see a bunch of nothing.  What kind of tutorial is this any way?  Well, we only added logs to the <code>applicationDidEnterBackground<\/code> method, so to see anything, we&#8217;ll have to send the app to the background!  Do so by hitting the Home button on either your device or simulator.  You should have seen the XCode log output window show you some goods!<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/first_logs_png.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2013\/02\/first_logs_png.png\" alt=\"first_logs_png\" width=\"300\" height=\"35\" class=\"alignnone size-medium wp-image-31\" \/><\/a><\/p>\n<p>Now, you&#8217;ve got a good grasp of what&#8217;s possible with Lumberjack, and remember that <code>ENTRY_LOG<\/code> and <code>EXIT_LOG<\/code> are just macros to add at the beginning and end of your methods. To really get cooking you&#8217;d add a variety of statements to your code, like:<\/p>\n<p>[objc]<br \/>\n  DDLogVerbose(@&quot;Starting up application&#8230;&quot;);<\/p>\n<p>  \/\/ Perform some actions<br \/>\n  for (int i = 1; i &lt; 5; i++) {<br \/>\n    DDLogInfo(@&quot;Performing startup action %d&quot;, i);<br \/>\n  }<\/p>\n<p>  DDLogVerbose(@&quot;Startup actions complete&quot;);<br \/>\n[\/objc]<\/p>\n<p>Note that the <code>DDLog<\/code> statements can use positional argument placeholders like <code>%@<\/code> and <code>%d<\/code>.<\/p>\n<p>Our output (we added this to the <code>didFinishLaunchingWithOptions:<\/code> method):<\/p>\n<pre>\r\n2013-02-10 23:15:53:618 lumberjackExample[28210:c07] Starting up application...\r\n2013-02-10 23:15:53:618 lumberjackExample[28210:c07] Performing startup action 1\r\n2013-02-10 23:15:53:618 lumberjackExample[28210:c07] Performing startup action 2\r\n2013-02-10 23:15:53:618 lumberjackExample[28210:c07] Performing startup action 3\r\n2013-02-10 23:15:53:618 lumberjackExample[28210:c07] Performing startup action 4\r\n2013-02-10 23:15:53:618 lumberjackExample[28210:c07] Startup actions complete\r\n<\/pre>\n<p>Well, that&#8217;s all the time we have for this tutorial, and we&#8217;ve just scratched the surface.  Our next tutorial will be how to add and manage multiple schemes and build configurations to aid in managing the various types of builds (debug vs. release), etc.<\/p>\n<p>The final project code can be downloaded from GitHub at <a href=\"https:\/\/github.com\/iachievedit\/lumberjackExample\" title=\"https:\/\/github.com\/iachievedit\/lumberjackExample\">https:\/\/github.com\/iachievedit\/lumberjackExample<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Edit: Lumberjack in it&#8217;s current form isn&#8217;t compatible with Apple&#8217;s new Swift. If you&#8217;re playing with Swift and looking for some logging alternatives in the meantime, see our latest post. For our very first blog entry, we&#8217;re going to introduce one of our favorite tools for debugging iOS applications, and that&#8217;s a logging framework known [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-7","post","type-post","status-publish","format-standard","hentry","category-xcodetips"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/7"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=7"}],"version-history":[{"count":19,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/7\/revisions"}],"predecessor-version":[{"id":275,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/7\/revisions\/275"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=7"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=7"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=7"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}