Well, the project is under active development again. A release hasn't been made, but will be within a month or so. Until then, check the source our from svn; it's usable.
As I develop for 10.5, that will be my primary area of focus. The framework supports GC, but does not require it; that ties the current source to > 10.5. If people need/want a 10.4 version, one can be added (no GC at all).
I'll update this page in the comming days with progress information, and a roadmap. For now, my plan is to clean up the code, remove dead code, convert to Doxygen, and write a test suite.
Read the readme.txt file
If you want to help out, have a suggestion, or request, please feel free to email me.
Please visit the Log4Cocoa Project home page.
Thank you.
Log4Cocoa started off as a Cocoa port of Log4J. After setting ideal for a while as the original author had other priorities, I decided to pick it up. I was (and still am) amazed at the lack of features offered by NSLog(), and by the development communities acquiescence. After having written large scale enterprise level applications for a few decades, I can't stress enough the importance of a good logging framework.
Many Mac apps use NSLog() — and they use it liberally. This has the duel effect of producing huge amount of text in the system log file, and polluting the system log file to the level where it's practically useless. This project is an attempt to overcome that.
Log4Cocoa support logging to file, rolling log files (based on date or size), and the ability to add custom formatters for log messages. These two features make it possible to keep your applications log messages out of the system log, and this makes the log messages much more valuable, especially as a troubleshooting aid. The ability to use custom formatters is less used, but can be greatly advantageous. The default log format is very verbose; tailoring the output to your needs will greatly increase your ability to understand those messages. You can even have different formats for different log levels or classes.
To use the logging system, it must first be initialized. This can be done in your main method with the following code:
[[L4Logger rootLogger] setLevel:[L4Level error]]; [[L4Logger rootLogger] addAppender:[[L4ConsoleAppender alloc] initTarget:YES withLayout:[L4Layout simpleLayout]]];
The first this this does is to set the global logging level to error. This means nothing less than an error will be logged. This is also typically what you would want in a production application.
These two lines don't specify where to log, so — as the default — log messages go to the console.
- (void) setUp { [[L4Logger rootLogger] setLevel:[L4Level all]]; [[L4Logger rootLogger] addAppender: [[L4ConsoleAppender alloc] initTarget:YES withLayout: [L4Layout simpleLayout]]]; }
There are five logging macros defines for four usage scenarios:
Objective-C | Objective-C with exception | C function | C function with exception | |
---|---|---|---|---|
debug | log4Debug(message, ...) | log4Debug(message, e, ...) | log4CDebug(message, ...) | log4CDebug(message, e, ...) |
informational | log4Info(message, ...) | log4Info(message, e, ...) | log4CInfo(message, ...) | log4CInfo(message, e, ...) |
warning | log4Warn(message, ...) | log4Warn(message, e, ...) | log4CWarn(message, ...) | log4CWarn(message, e, ...) |
error | log4Error(message, ...) | log4Error(message, e, ...) | log4CError(message, ...) | log4CError(message, e, ...) |
fatal | log4Fatal(message, ...) | log4Fatal(message, e, ...) | log4CFatal(message, ...) | log4CFatal(message, e, ...) |
These are also two assertion macros defined:
log4Assert(assertion, message, ...)
log4CAssert(assertion, message, ...)
From your typical Objective-C class, in a method, all you need to do is add #import <Log4Cocoa/Log4Cocoa.h> to your imports section. Then in your source you might have something like
- (void) directoryContentsFor:(NSString *)directoryName { log4Debug(@"Method entry"); // This will log a message on a failed assertion, but will not exit the method. log4Assert(directoryName != nil, @"The dorectory name must not be nil"); NSError *directoryError = nil; NSArray *directoryContents = [defaultManager contentsOfDirectoryAtPath:directoryName error:&directoryError]; if (directoryError != nil) { log4Error(@"Error getting the contents of %@; the error is %@.", directoryName, [directoryError localizedDescription]); } else { log4Info(@"Fetched the directory contents."); } log4Debug(@"Method exit"); }
The exception versions work the same way. You would typically use them in a @catch
block, or just before raising your own exception. The versions for C functions are needed because self
is not available in them, and is used by the macros. The message portion is required an a minimum, but can be an empty if you just want the position.
When there is a particular class you are interested in, you can adjust the logging level for that specific class. This may be to allow more of that classes log messages to appear, but it can also be to hide message from that class.
Let's assume that the system was configure to a level of error, and you want see more messages for a window's controller.
L4Logger *theLogger = [L4Logger loggerForClass:[MyWindowController class]]; [theLogger setLevel:[L4Level debug]];
This would adjust the logging level for instances of MyWindowController to debug; so much more gets logged.
If, instead, you wanted all window controllers to log at a level of debug, you'd use the following instread:
L4Logger *theLogger = [L4Logger loggerForClass:[NSWindowController class]]; [theLogger setLevel:[L4Level debug]];
Now, every instance of every subclass of NSWindowController will show more output from logging.
The logging framework can be configured using a properties file that follows the same basic format as systems like log4j. Assume there is a log4cocoa.properties file with the following contents:
log4cocoa.appender.A1=L4ConsoleAppender log4cocoa.appender.A1.layout=L4SimpleLayout log4cocoa.appender.A1.LogToStandardOut=true log4cocoa.appender.A2=L4RollingFileAppender log4cocoa.appender.A2.MaximumFileSize=10MB log4cocoa.appender.A2.MaxBackupIndex=1 log4cocoa.appender.A2.layout=L4PatternLayout log4cocoa.appender.A2.layout.ConversionPattern=%-5p : %m%n log4cocoa.rootLogger=ALL, A1
and that this file is in your Resources group in Xcode. In your main.m file you can now configure the framework with this code:
NSString *filename = [[NSBundle bundleForClass:[self class]] pathForResource:@"log4cocoa" ofType:@"properties"]; L4PropertyConfigurator *configurator = [[L4PropertyConfigurator alloc] initWithFileName:filename]; [configurator configure]
This will create two appenders, named A1 and A2. The default appender is A1, and it logs all log levels to stdout using the L4SimpleLayout. The second appender, A2, logs to a file that 'roles' once it reaches 10MB, and keeps the last roled log file; older ones are automatically deleted. It uses an L4PatternLayout to format he log messages.