Being used to logging on java and .Net, NSLog does seem very primitive, and one solution does appear to be cocoalumberjack. I will be investigating this and doing a post soon.
In the mean time I am experimenting with using blocks to give me the flexibility I need as a minimum. This was partly motivated as a learning exercise for closures in Objective-C
The basic Logger header looks like this:
typedef enum {
kError = 0,
kWarn,
kInfo,
kDebug,
} TRLLogLevel;
@interface TRLLog : NSObject
+(void)logWithBlock:(NSString* (^)(void))block withLevel:(TRLLogLevel)level;
+(void)setLogLevel:(TRLLogLevel)level;
+(void)error:(NSString* (^)(void))block;
+(void)warn:(NSString* (^)(void))block;
+(void)info:(NSString* (^)(void))block;
+(void)debug:(NSString* (^)(void))block;
@end
.. so a simple logger API then. The interesting part as far as I am concerned is this bit...
(NSString* (^)(void))
There is a full discussion on block syntax here, so I am not going to go into lots of detail. The example above is defining a block which takes no parameters and returns a string.
Inside the implementation, the block is used like a normal C function
+(void)logWithBlock:(NSString* (^)(void))block withLevel:(TRLLogLevel)level {
if (level > currentLogLevel)
return;
NSString *msg = block();
NSLog(@"%@",msg);
[msg release];
}
This means that any expensive operations inside the block are not called if the log level is not met. To use the logger, the code looks like this
CGPoint point = [recogniser translationInView:self.view];
[TRLLog debug:^{
return [[NSString alloc]initWithFormat:@"handlePan [%f, %f]",point.x,point.y];
}];
Note how because the block is a closure, it can use other variables that are in scope.
No comments:
Post a Comment