Monday 12 December 2011

Java Logging Framework

Instantiating various kinds of Loggers
There are numerous types of Loggers provided in the framework. Here are just a few examples.
    A logger that writes to the console
    Logger logger = PrintStreamLogger.newConsoleLogger();
    A logger that writes to a file
    Logger logger = new FileLogger("/mylogfiles/mylog.log");
    A logger that writes binary log entries to a socket
    Logger logger = new SerialSocketLogger("SomeHost.com", portNumber);
    A logger that writes to numerous other loggers
    This type of logger, the CompositeLogger, will probably be the main logger for your application. You can then put other, more specific loggers inside this one.

    /* first instantiate some basic loggers */
    Logger consoleLogger = PrintStreamLogger.newConsoleLogger();
    Logger fileLogger = new FileLogger("/mylogfiles/mylog.log");
    Logger socketLogger = new SerialSocketLogger("SomeHost.com", 12345);

    /* now instantiate a CompositeLogger */
    CompositeLogger logger = new CompositeLogger();

    /* add the basic loggers to the CompositeLogger */
    logger.addLogger("console", consoleLogger);
    logger.addLogger("file", fileLogger);
    logger.addLogger("socket", socketLogger);

    /* now all logs to logger will automatically be sent
        to the contained loggers as well */

    Using the InsistentLogger
    The InsistentLogger provides the functionality of retrying to log when a logger fails. For example, if a SerialSocketLogger was unable to connect to a server socket, then an InsistentLogger would automatically provide the retrying capability. Using it is simple:

/* create my basic logger */
Logger logger = new SerialSocketLogger( "SomeHost.com", 12345 );

/* - wrap in an InsistentLogger
   - have a backup of 200 LogEntries
   - retry logging every 60 seconds */
logger = new InsistentLogger( logger, 200, 60 );

/* now I can log to logger just like any other logger */

 Logging in an application
    Various sample logging methods
logger.logDebug("x = " + x);
logger.logInfo("User " + aUser + "has visited page " + page);
logger.logStatus("Active");
logger.logWarning("Database is inaccessible");
logger.logError(anException);
logger.logCritical("Database is down");
logger.logFatal("Application is exitting because " + reason);
    DEBUG, INFO, STATUS, WARNING, ERROR, CRITICAL, and FATAL are all severities. A logger can be set to only log items that are of a specified severity or higher, while ignoring items of lower importance.
    If you prefer, you can specify the severity as a parameter such as
    logger.log( logger.INFO, "This is some information" );
    logger.log( logger.ERROR, anException );
    Using "categories"
    Categories are simply an optional way of organizing log entries. A category can be an instance of any class you wish, but most likely would simply be a String. You might like to categorize log entries based on their respective sub-system, or by their general meaning for the application.

logger.logWarning("Database", "Database is non-responsive");

logger.log( logger.WARNING, "Database", "Database is non-responsive");

Formatting Log Entries

    Simple Date/Time formatting
    Every logger has a formatter. Each formatter has a DateFormat attribute. You can customize the way the timestamp appears by doing something such as

    /* create my own DateFormat to my specs */
    java.text.DateFormat myDateFormat =
       java.text.DateFormat.getDateTimeInstance(
          java.text.DateFormat.FULL,
          java.text.DateFormat.FULL);

    /* now set the logger's date format */
    logger.getFormatter().setDateFormat(myDateFormat);

    Advanced formatting

    The way a LogEntry looks in a log can be completely customized. By default every logger uses an instance of LogEntryStandardFormatter. However, you can create your own subclasses of the abstract class LogEntryFormatter. Simply override the asString(LogEntry) method to provide a completely custom look in the log.

    LogEntryFormatter myFormatter = new MyCustomLogEntryFormatter();
    logger.setFormatter(myFormatter);
    You can also change the default formatter for all your logger instances by using the static setDefaultFormatterClass(LogEntryFormatter) method on Logger.

Logger.setDefaultFormatterClass(MyCustomLogEntryFormatter.class);

Filtering Log Entries

    Filtering log entries is the process of ignoring some entries and actually logging others. Basic filtering is done by setting the severity threshold of a logger.
logger.setSeverityThreshold( logger.WARNING );

    In the above example, all log requests that are of a severity lower than WARNING will be ignored. The default severity threshold for loggers is DEBUG, which is the lowest severity.
    You may wish to have more sophisticated filtering, however. For example, one logger might be specifically for database related log entries, whereas another might be for recording the general state of the application. You can use the provided LogEntryCategoryFilter class to accomplish this filtering. The following example shows how you might use it.

/* create and setup the filter */
LogEntryCategoryFilter myDbFilter = new LogEntryCategoryFilter();
myFilter.addCategory("Database");

/* now set the logger's filter */
logger.setFilter(myDbFilter);
    Using the logger above, only entries with the "Database" category will get logged. For example:
logger.logInfo( "This has no category, and won't get logged" );
logger.logInfo( "Network", "This won't get logged either" );
logger.logInfo( "Database", "This will get logged" );

    You can easily create a CompositeLogger which contains other loggers, each with its own filter.
    Also, you can create your own custom filter by simply sublassing LogEntryFilter, overriding the method canPass(LogEntry).
    By default, if you don't specify a filter for a logger, it will use a LogEntryPassFilter, which allows all log entries to get logged. You can set your own default filter class by doing something such as:
Logger.setDefaultFilterClass( MyCustomLogEntryFilter.class );

Creating a Custom Logger

The framework provides a very easy way for you to log to anything you choose. It starts by subclassing Logger. From there you have two basic choices.
If your needs are to simply write the LogEntry as a String to some device, then you can just override the method writeToLog(String). If your needs are more sophisticated, then you can override the method doLog(LogEntry). In either case, the methods should return true if they were successful, and false otherwise.

No comments:

Post a Comment