Welcome to the Log4Cocoa home page

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.

About the project

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.

Downloading

Log4Cocoa can be downloaded from the SourceForge project download page

 

Usage

Initializing the system

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.

Initializing the system from a unit test

- (void) setUp
{
  [[L4Logger rootLogger] setLevel:[L4Level all]];
  [[L4Logger rootLogger] addAppender: [[L4ConsoleAppender alloc] initTarget:YES withLayout: [L4Layout simpleLayout]]];
}

 

Logging

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:

 

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.

Changing log levels for a single class

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.

Using a configuration file

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.

 

TODO