In this final wrap of my MyLogger class, I'll show you my experience and usages. As most Objective-C developers know, NSLog
is a C function to print out messages. It is very useful, however, as it is a function, where parameters limit its usage. It possible to pass complicated parameters, however, that' may be too difficult or just impossible. In case if you want to define different storage or format for logging message, class is the way to go. That's my initial intension to define a wrapper class for NSLog
: MyLogger
.
MyLogger
's logging engine still uses NSLog
. You can easily extend it to other storage. The API provides methods to set intention and logging message with various levels.
Usage
Normally, I initialize MyLogger
settings initially in main()
, where you rarely make changes. This will keep all the logging messages in the same format and consistency style. The settings are default logging level, and optional indention char and format. The great advantage of strategy is that you can easily set logging level to none if you want to disable the logging feature and you would not need to remove codes from your projects all over the places.
Here is an example of MyLogger
settings:
#import <UIKit/UIKit.h> #import "MyLogger.h" int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; // Setup the default settings for logger MyLogger.defaultLoggingLevel = LogLevelDebug; MyLogger.indentChar = '-'; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; }
Then you are ready to use
MyLogger
in other places in your project. For me as a newbie of Objective-C developer, Cocoa framework and Objective-C are overwhelming to digest. It was very easy to get lost and frustrated. The way I used MyLogger
is to place it in each method in my classes in a pair: logging at the first line of the method and logging at the exist point of the method, and set indent at the entry and outdent at the exit. I do get a lots debug messages; however, since all the messages are in a nice indention structured layout, it makes much easy to read and understand the flow of my class. That has been great help for me.Another practice I have is that for each class where I want to do logging, I define a private var
MyLogger*
member. In the class constructor, I create the var and initialize it with a context name of that class. In this way, all the logging messages will have a clear context string to identify the messages. To set context in one place makes my logging much easy to maintain in case I need to rename my class as an example.Taking MyClass as example, here is the way I add logging feature to the class:
// .h file #import <UIKit/UIKit.h> @class MyLogger; ... @interface MyViewController : UITableViewController <NSFetchedResultsControllerDelegate, UITableViewDelegate> { ... @private MyLogger* mLogger; ... } ... @end // .m file #import "MyLogger.h" ... @implementation MyViewController ... - (id)initWithStyle:(UITableViewStyle)style { if (self = [super initWithStyle:style]) { mLogger = [[MyLogger alloc] initWithContext:@"MyViewController"]; [[mLogger debug:@"initWithStyle: %@", style == UITableViewStylePlain ? @"Plain" : @"Group"] indent:YES]; ... [[mLogger indent:NO] debug:@"initWithStyle: Done"]; } return self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { [[mLogger debug:@"numberOfSectionsInTableView:"] indent:YES]; NSInteger count = [[self.fetchedResultsController sections] count]; [[mLogger indent:NO] debug:@"numberOfSectionsInTableView: DONE"]; return count; } - (NSFetchedResultsController*) fetchedResultsController { [[mLogger debug:@"fetchedResultsController"] indent:YES]; if (mFetchedResultsController == nil) { ... [mLogger debug:@"created new NSFetchResultsConroller obj: %@" mFetchedResultsController]; ... } [[mLogger indent:NO] debug:@"fetchedResultsController DONE"]; return mFetchedResultsController; } ... @end
Here is a list of my loggging messages:
... 2010-07-09 16:05:28.203 ExpenseLog[2648:207] [DEBUG] MyViewController - initWithStyle: Plain 2010-07-09 16:05:28.209 ExpenseLog[2648:207] [DEBUG] MyViewController - initWithStyle: Done 2010-07-09 16:05:28.210 ExpenseLog[2648:207] [DEBUG] MyViewController - viewDidLoad 2010-07-09 16:05:28.211 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController 2010-07-09 16:05:28.211 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext 2010-07-09 16:05:28.212 ExpenseLog[2648:207] ------[DEBUG] MyViewController - creating new managedObjectContext 2010-07-09 16:05:28.212 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext DONE 2010-07-09 16:05:28.212 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext 2010-07-09 16:05:28.213 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext DONE 2010-07-09 16:05:28.213 ExpenseLog[2648:207] ----[DEBUG] MyViewController - created NSFetchedResultsController obj: <NSFetchedResultsController: 0x8611340> 2010-07-09 16:05:28.214 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE 2010-07-09 16:05:28.215 ExpenseLog[2648:207] [DEBUG] MyViewController - viewDidLoad DONE 2010-07-09 16:05:28.215 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView: 2010-07-09 16:05:28.216 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController 2010-07-09 16:05:28.216 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE 2010-07-09 16:05:28.216 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView: DONE 2010-07-09 16:05:28.218 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView: 2010-07-09 16:05:28.218 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController 2010-07-09 16:05:28.219 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE 2010-07-09 16:05:28.219 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView: DONE 2010-07-09 16:05:28.219 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:numberOfRowsInSection: 2010-07-09 16:05:28.220 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController 2010-07-09 16:05:28.220 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE 2010-07-09 16:05:28.220 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:numberOfRowsInSection: DONE 2010-07-09 16:05:28.221 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:cellForRowAtIndexPath: 2010-07-09 16:05:28.221 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController 2010-07-09 16:05:28.222 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE 2010-07-09 16:05:28.223 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:cellForRowAtIndexPath: DONE ...
I think it is really worthwhile to spend some time design this helper class. The benefits I have been received are tremendous. It greatly helps me to understand Cocoa framework, saves me enormous amount of time, and keeps my mind sharp on the business logic I want to implement.
With new iOS available, I think this class can be further enhanced with block feature to improve the performance. For example, I could put all the messages into a block so that when a logging level is disabled, the block would not be evaluated or executed at all.
Reference
- My Logger Class in Objective-C (3)
- Source codes: MyLogger
- Code Licences: Apache 2.0
2 comments:
This is a nice and simple logging class. I'd like to use it in my commercial product, but my employer is pretty particular about using other people's source code. There isn't any license file with your source. Are there any terms of use that you have for it?
I have indicated the license in my project site (http://code.google.com/p/chudq-objetivec/).
Post a Comment