bb.util.logging
Class Logger2

java.lang.Object
  extended by java.util.logging.Logger
      extended by bb.util.logging.Logger2

public class Logger2
extends Logger

Subclass of Logger which adds this additional functionality:

  1. all logging methods log robustly (see log(LogRecord) log)
  2. the logIfNew methods

This class is multithread safe: every method which uses mutable state is synchronized.

Author:
Brent Boyer

Nested Class Summary
static class Logger2.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Field Summary
private  Formatter formatter
          Used by log in its error handling logic if need to resort to outputting to the console.
private  Set<String> loggedMessages
          Solely used (and lazy initialized) inside logIfNew to store messages known by this instance.
private  Logger logger
          This class actually delegates all of its low-level logging work to this internal Logger instance.
 
Fields inherited from class java.util.logging.Logger
global, GLOBAL_LOGGER_NAME
 
Constructor Summary
protected Logger2(Logger logger)
          Constructs a new Logger2 which ultimately defers to logger for all of its work.
 
Method Summary
 void addHandler(Handler handler)
           
static Logger2 getAnonymousLogger2()
          Returns getAnonymousLogger2(null).
static Logger2 getAnonymousLogger2(String resourceBundleName)
          Constructs a new Logger2 whose underlying logger is anonymous (i.e. unregistered with LogManager) and uses resourceBundleName.
 Filter getFilter()
           
 Handler[] getHandlers()
           
 Level getLevel()
           
static Logger2 getLogger2(String name)
          Returns getLogger2(name, null).
static Logger2 getLogger2(String name, String resourceBundleName)
          Constructs a new Logger2 whose underlying logger is registered with LogManager and uses name and resourceBundleName.
 String getName()
           
 Logger getParent()
           
private  String getRecordDescription(LogRecord record)
          Contract: should never throw any Throwable.
 ResourceBundle getResourceBundle()
           
 String getResourceBundleName()
           
private  String getThrowableDescription(Throwable t)
          Contract: should never throw any Throwable.
 boolean getUseParentHandlers()
           
 boolean isLoggable(Level level)
           
 void log(LogRecord record)
           Contract: should never throw any Throwable.
 void logIfNew(Level level, String sourceClass, String sourceMethod, String message)
          Simply calls logIfNew(level, sourceClass, sourceMethod, message, (Object[]) null).
 void logIfNew(Level level, String sourceClass, String sourceMethod, String message, Object parameter)
          Simply calls logIfNew(level, sourceClass, sourceMethod, message, new Object[] {parameter}).
 void logIfNew(Level level, String sourceClass, String sourceMethod, String message, Object[] parameters)
          This method only logs the information if it produces a message which is unknown by this class.
 void removeHandler(Handler handler)
           
 void setFilter(Filter newFilter)
           
 void setLevel(Level newLevel)
           
 void setParent(Logger parent)
           
 void setUseParentHandlers(boolean useParentHandlers)
           
 
Methods inherited from class java.util.logging.Logger
config, entering, entering, entering, exiting, exiting, fine, finer, finest, getAnonymousLogger, getAnonymousLogger, getLogger, getLogger, info, log, log, log, log, logp, logp, logp, logp, logrb, logrb, logrb, logrb, severe, throwing, warning
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

logger

private final Logger logger
This class actually delegates all of its low-level logging work to this internal Logger instance.

This is wasteful of cpu and memory, and forces this class to override many Logger methods just to do this delegation. Nevertheless, it was done because the Logger javadocs (as of JDK 1.6 at least) specify that subclasses must do this:

Subclassing Information: ... Therefore, any subclasses of Logger ... should take care to obtain a Logger instance from the LogManager class and should delegate operations ... to that instance
Another reason to do this delegation is because Sun has not exposed the private Logger.anonymous field. There is a RFE about this: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6830679


formatter

private final Formatter formatter
Used by log in its error handling logic if need to resort to outputting to the console. For this purpose, a concrete subclass of Formatter which prints out all the useful LogRecord information must be used.

Used by logIfNew in order to perform localization and param substitution via the Formatter.formatMessage method. For this purpose, any concrete subclass of Formatter which does not override formatMessage in an unexpected way may be assigned here. (That subclass's Formatter.format method is never used, so it is irrelevant what its output looks like.)


loggedMessages

private Set<String> loggedMessages
Solely used (and lazy initialized) inside logIfNew to store messages known by this instance.

Constructor Detail

Logger2

protected Logger2(Logger logger)
           throws NullPointerException
Constructs a new Logger2 which ultimately defers to logger for all of its work.

Throws:
NullPointerException - if logger == null
Method Detail

getAnonymousLogger2

public static Logger2 getAnonymousLogger2()
Returns getAnonymousLogger2(null).


getAnonymousLogger2

public static Logger2 getAnonymousLogger2(String resourceBundleName)
Constructs a new Logger2 whose underlying logger is anonymous (i.e. unregistered with LogManager) and uses resourceBundleName.


getLogger2

public static Logger2 getLogger2(String name)
                          throws IllegalArgumentException,
                                 IllegalStateException,
                                 SecurityException,
                                 RuntimeException
Returns getLogger2(name, null).

Throws:
IllegalArgumentException - if name is blank
IllegalStateException - if a Logger with name already exists
SecurityException - if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException - (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwable

getLogger2

public static Logger2 getLogger2(String name,
                                 String resourceBundleName)
                          throws IllegalArgumentException,
                                 IllegalStateException,
                                 SecurityException,
                                 RuntimeException
Constructs a new Logger2 whose underlying logger is registered with LogManager and uses name and resourceBundleName.

No other Logger with name may currently exist: this method's first action is to check this using LogManager. Only if this is the first Logger with name is it created.

Throws:
IllegalArgumentException - if name is blank; consiequently, this method cannot be used to create the root Logger (the one named "")
IllegalStateException - if a Logger with name already exists
SecurityException - if a security manager exists and if the caller does not have LoggingPermission("control")
RuntimeException - (or some subclass) if any other error occurs; this may merely wrap some other underlying Throwable

addHandler

public void addHandler(Handler handler)
Overrides:
addHandler in class Logger

getFilter

public Filter getFilter()
Overrides:
getFilter in class Logger

getHandlers

public Handler[] getHandlers()
Overrides:
getHandlers in class Logger

getLevel

public Level getLevel()
Overrides:
getLevel in class Logger

getName

public String getName()
Overrides:
getName in class Logger

getParent

public Logger getParent()
Overrides:
getParent in class Logger

getResourceBundle

public ResourceBundle getResourceBundle()
Overrides:
getResourceBundle in class Logger

getResourceBundleName

public String getResourceBundleName()
Overrides:
getResourceBundleName in class Logger

getUseParentHandlers

public boolean getUseParentHandlers()
Overrides:
getUseParentHandlers in class Logger

isLoggable

public boolean isLoggable(Level level)
Overrides:
isLoggable in class Logger

log

public void log(LogRecord record)

Contract: should never throw any Throwable. Instead, this implementation logs robustly. It first calls logger.log, and if that succeeds, then it returns with no further action. However, if a Throwable is raised, then it tries to log record as well as the new Throwable to System.err. Similarly, if the System.err code raises a Throwable, then it tries to log record and both Throwables to System.out). Finally, if the System.out code raises a Throwable, then it is silently ignored.

This robust logging behavior was chosen because some users may want to log inside finally blocks. In this case, logging should not throw a Throwable, because that might skip other actions inside that finally block.

Overrides:
log in class Logger

removeHandler

public void removeHandler(Handler handler)
Overrides:
removeHandler in class Logger

setFilter

public void setFilter(Filter newFilter)
Overrides:
setFilter in class Logger

setLevel

public void setLevel(Level newLevel)
Overrides:
setLevel in class Logger

setParent

public void setParent(Logger parent)
Overrides:
setParent in class Logger

setUseParentHandlers

public void setUseParentHandlers(boolean useParentHandlers)
Overrides:
setUseParentHandlers in class Logger

getRecordDescription

private String getRecordDescription(LogRecord record)
Contract: should never throw any Throwable.


getThrowableDescription

private String getThrowableDescription(Throwable t)
Contract: should never throw any Throwable.


logIfNew

public void logIfNew(Level level,
                     String sourceClass,
                     String sourceMethod,
                     String message)
              throws IllegalArgumentException
Simply calls logIfNew(level, sourceClass, sourceMethod, message, (Object[]) null).

Throws:
IllegalArgumentException - if level == null; sourceClass is blank; sourceMethod is blank; message is blank;

logIfNew

public void logIfNew(Level level,
                     String sourceClass,
                     String sourceMethod,
                     String message,
                     Object parameter)
              throws IllegalArgumentException
Simply calls logIfNew(level, sourceClass, sourceMethod, message, new Object[] {parameter}).

Throws:
IllegalArgumentException - if level == null; sourceClass is blank; sourceMethod is blank; message is blank;

logIfNew

public void logIfNew(Level level,
                     String sourceClass,
                     String sourceMethod,
                     String message,
                     Object[] parameters)
              throws IllegalArgumentException
This method only logs the information if it produces a message which is unknown by this class. It is useful when you wish to make just one log entry for an event which may occur many times, in order to avoid clogging the logs with redundant information.

For optimal performance, this method immediately returns if level is such that logger would never even log it, since this is a quick test.

Otherwise, a new LogRecord is constructed from the params. Its message then has localization and param substitution performed on it in order to produce the actual message that might be logged. This actual message is then compared against the messages know by this instance from a previous call to one of these logIfNew methods. If it is new, then the LogRecord is logged.

Side effect: if it is new, the actual message will be stored in an internal data structure of this class. This storage is permanent, which makes memory exhaustion possible. So, use this method with great care in long running processes.

Throws:
IllegalArgumentException - if level == null; sourceClass is blank; sourceMethod is blank; message is blank